mirror of
https://github.com/LmeSzinc/AzurLaneAutoScript.git
synced 2025-01-09 05:47:30 +08:00
Opt: Release cached assets
This commit is contained in:
parent
6d182bb620
commit
b37114d505
12
alas.py
12
alas.py
@ -315,30 +315,36 @@ class AzurLaneAutoScript:
|
||||
Returns:
|
||||
str: Name of the next task.
|
||||
"""
|
||||
from module.base.memory_opt import release_memory_after_task, release_memory_when_idle
|
||||
release_memory_after_task()
|
||||
|
||||
task = self.config.get_next()
|
||||
self.config.task = task
|
||||
self.config.bind(task)
|
||||
|
||||
from module.base.resource import release_resources
|
||||
if self.config.task.command != 'Alas':
|
||||
release_resources(next_task=task.command)
|
||||
|
||||
if task.next_run > datetime.now():
|
||||
release_memory_when_idle()
|
||||
logger.info(f'Wait until {task.next_run} for task `{task.command}`')
|
||||
method = self.config.Optimization_WhenTaskQueueEmpty
|
||||
if method == 'close_game':
|
||||
logger.info('Close game during wait')
|
||||
self.device.app_stop()
|
||||
release_resources()
|
||||
self.wait_until(task.next_run)
|
||||
self.run('start')
|
||||
elif method == 'goto_main':
|
||||
logger.info('Goto main page during wait')
|
||||
self.run('goto_main')
|
||||
release_resources()
|
||||
self.wait_until(task.next_run)
|
||||
elif method == 'stay_there':
|
||||
logger.info('Stay there during wait')
|
||||
release_resources()
|
||||
self.wait_until(task.next_run)
|
||||
else:
|
||||
logger.warning(f'Invalid Optimization_WhenTaskQueueEmpty: {method}, fallback to stay_there')
|
||||
release_resources()
|
||||
self.wait_until(task.next_run)
|
||||
|
||||
AzurLaneConfig.is_hoarding_task = False
|
||||
|
@ -6,10 +6,11 @@ from PIL import ImageDraw
|
||||
|
||||
import module.config.server as server
|
||||
from module.base.decorator import cached_property
|
||||
from module.base.resource import Resource
|
||||
from module.base.utils import *
|
||||
|
||||
|
||||
class Button:
|
||||
class Button(Resource):
|
||||
def __init__(self, area, color, button, file=None, name=None):
|
||||
"""Initialize a Button instance.
|
||||
|
||||
@ -50,6 +51,8 @@ class Button:
|
||||
else:
|
||||
self.is_gif = False
|
||||
|
||||
self.resource_add(key=self.file)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@ -131,6 +134,10 @@ class Button:
|
||||
self.image = load_image(self.file, self.area)
|
||||
self._match_init = True
|
||||
|
||||
def resource_release(self):
|
||||
self.image = None
|
||||
self._match_init = False
|
||||
|
||||
def match(self, image, offset=30, threshold=0.85):
|
||||
"""Detects button by template matching. To Some button, its location may not be static.
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
import random
|
||||
import re
|
||||
from functools import wraps
|
||||
|
||||
import numpy as np
|
||||
|
||||
from module.logger import logger
|
||||
|
||||
|
||||
@ -63,7 +62,7 @@ class Config:
|
||||
|
||||
flag = [value is None or self.config.__getattribute__(key) == value
|
||||
for key, value in record['options'].items()]
|
||||
if not np.all(flag):
|
||||
if not all(flag):
|
||||
continue
|
||||
|
||||
return record['func'](self, *args, **kwargs)
|
||||
@ -117,7 +116,7 @@ def function_drop(rate=0.5, default=None):
|
||||
def decorate(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if np.random.uniform(0, 1) > rate:
|
||||
if random.uniform(0, 1) > rate:
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
cls = ''
|
||||
|
142
module/base/resource.py
Normal file
142
module/base/resource.py
Normal file
@ -0,0 +1,142 @@
|
||||
import re
|
||||
|
||||
import gc
|
||||
import psutil
|
||||
|
||||
from module.base.decorator import cached_property
|
||||
from module.logger import logger
|
||||
|
||||
|
||||
def del_cached_property(obj, name):
|
||||
"""
|
||||
Delete a cached property safely.
|
||||
|
||||
Args:
|
||||
obj:
|
||||
name (str):
|
||||
"""
|
||||
if hasattr(obj, name):
|
||||
del obj.__dict__[name]
|
||||
|
||||
|
||||
def watch_memory(func):
|
||||
"""
|
||||
Show memory changes in log
|
||||
release_resources: 181.555MB -> 163.066MB
|
||||
"""
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
before = psutil.Process().memory_info().rss / (1024 * 1024)
|
||||
result = func(*args, **kwargs)
|
||||
after = psutil.Process().memory_info().rss / (1024 * 1024)
|
||||
logger.info(f'{func.__name__}: {round(before, 3)}MB -> {round(after, 3)}MB')
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_assets_from_file(file, regex):
|
||||
assets = set()
|
||||
with open(file, 'r', encoding='utf-8') as f:
|
||||
for row in f.readlines():
|
||||
result = regex.search(row)
|
||||
if result:
|
||||
assets.add(result.group(1))
|
||||
return assets
|
||||
|
||||
|
||||
class PreservedAssets:
|
||||
@cached_property
|
||||
def ui(self):
|
||||
assets = set()
|
||||
assets |= get_assets_from_file(
|
||||
file='./module/ui/assets.py',
|
||||
regex=re.compile(r'^([A-Za-z][A-Za-z0-9_]+) = ')
|
||||
)
|
||||
assets |= get_assets_from_file(
|
||||
file='./module/ui/ui.py',
|
||||
regex=re.compile(r'\(([A-Z][A-Z0-9_]+),')
|
||||
)
|
||||
assets |= get_assets_from_file(
|
||||
file='./module/handler/info_handler.py',
|
||||
regex=re.compile(r'\(([A-Z][A-Z0-9_]+),')
|
||||
)
|
||||
# MAIN_CHECK == MAIN_GOTO_CAMPAIGN
|
||||
assets.add('MAIN_GOTO_CAMPAIGN')
|
||||
return assets
|
||||
|
||||
|
||||
_preserved_assets = PreservedAssets()
|
||||
|
||||
|
||||
class Resource:
|
||||
instances = {}
|
||||
|
||||
def resource_add(self, key):
|
||||
Resource.instances[key] = self
|
||||
|
||||
def resource_release(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_loaded(cls, obj):
|
||||
if hasattr(obj, '_image') and obj._image is None:
|
||||
return False
|
||||
elif hasattr(obj, 'image') and obj.image is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def resource_show(cls):
|
||||
logger.hr('Show resource')
|
||||
for key, obj in cls.instances.items():
|
||||
if cls.is_loaded(obj):
|
||||
continue
|
||||
logger.info(f'{obj}: {key}')
|
||||
|
||||
|
||||
def release_resources(next_task=''):
|
||||
# Release all OCR models
|
||||
# Usually to have 2 models loaded and each model takes about 20MB
|
||||
# This will release 20-40MB
|
||||
from module.ocr.ocr import OCR_MODEL
|
||||
if 'Opsi' in next_task or 'commission' in next_task:
|
||||
# OCR models will be used soon, don't release
|
||||
models = []
|
||||
elif next_task:
|
||||
# Release OCR models except 'azur_lane'
|
||||
models = ['cnocr', 'jp', 'tw']
|
||||
else:
|
||||
models = ['azur_lane', 'cnocr', 'jp', 'tw']
|
||||
for model in models:
|
||||
del_cached_property(OCR_MODEL, model)
|
||||
|
||||
# Release assets cache
|
||||
# module.ui has about 80 assets and takes about 3MB
|
||||
# Alas has about 800 assets, but they are not all loaded.
|
||||
# Template images take more, about 6MB each
|
||||
for key, obj in Resource.instances.items():
|
||||
# Preserve assets for ui switching
|
||||
if next_task and str(obj) in _preserved_assets.ui:
|
||||
continue
|
||||
# if Resource.is_loaded(obj):
|
||||
# logger.info(f'Release {obj}')
|
||||
obj.resource_release()
|
||||
|
||||
# Release cached images for map detection
|
||||
from module.map_detection.utils_assets import ASSETS
|
||||
attr_list = [
|
||||
'ui_mask',
|
||||
'ui_mask_os',
|
||||
'ui_mask_stroke',
|
||||
'ui_mask_in_map',
|
||||
'ui_mask_os_in_map',
|
||||
'tile_center_image',
|
||||
'tile_corner_image',
|
||||
'tile_corner_image_list'
|
||||
]
|
||||
for attr in attr_list:
|
||||
del_cached_property(ASSETS, attr)
|
||||
|
||||
# Useless in most cases, but just call it
|
||||
gc.collect()
|
@ -5,11 +5,12 @@ import imageio
|
||||
import module.config.server as server
|
||||
from module.base.button import Button
|
||||
from module.base.decorator import cached_property
|
||||
from module.base.resource import Resource
|
||||
from module.base.utils import *
|
||||
from module.map_detection.utils import Points
|
||||
|
||||
|
||||
class Template:
|
||||
class Template(Resource):
|
||||
def __init__(self, file):
|
||||
"""
|
||||
Args:
|
||||
@ -21,6 +22,8 @@ class Template:
|
||||
self.is_gif = os.path.splitext(self.file)[1] == '.gif'
|
||||
self._image = None
|
||||
|
||||
self.resource_add(self.file)
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
if self._image is None:
|
||||
@ -28,9 +31,10 @@ class Template:
|
||||
self._image = []
|
||||
for image in imageio.mimread(self.file):
|
||||
image = image[:, :, :3].copy() if len(image.shape) == 3 else image
|
||||
image = self.pre_process(image)
|
||||
self._image += [image, cv2.flip(image, 1)]
|
||||
else:
|
||||
self._image = load_image(self.file)
|
||||
self._image = self.pre_process(load_image(self.file))
|
||||
|
||||
return self._image
|
||||
|
||||
@ -38,6 +42,19 @@ class Template:
|
||||
def image(self, value):
|
||||
self._image = value
|
||||
|
||||
def resource_release(self):
|
||||
self._image = None
|
||||
|
||||
def pre_process(self, image):
|
||||
"""
|
||||
Args:
|
||||
image (np.ndarray):
|
||||
|
||||
Returns:
|
||||
np.ndarray:
|
||||
"""
|
||||
return image
|
||||
|
||||
@cached_property
|
||||
def size(self):
|
||||
if self.is_gif:
|
||||
|
@ -1,6 +1,5 @@
|
||||
import numpy as np
|
||||
|
||||
from module.base.decorator import cached_property
|
||||
from module.base.timer import Timer
|
||||
from module.base.utils import red_overlay_transparency, get_color
|
||||
from module.combat.combat import Combat
|
||||
@ -9,23 +8,16 @@ from module.handler.info_handler import info_letter_preprocess
|
||||
from module.logger import logger
|
||||
from module.template.assets import *
|
||||
|
||||
TEMPLATE_AMBUSH_EVADE_SUCCESS.pre_process = info_letter_preprocess
|
||||
TEMPLATE_AMBUSH_EVADE_FAILED.pre_process = info_letter_preprocess
|
||||
TEMPLATE_MAP_WALK_OUT_OF_STEP.pre_process = info_letter_preprocess
|
||||
|
||||
|
||||
class AmbushHandler(Combat):
|
||||
MAP_AMBUSH_OVERLAY_TRANSPARENCY_THRESHOLD = 0.40
|
||||
MAP_AIR_RAID_OVERLAY_TRANSPARENCY_THRESHOLD = 0.35 # Usually (0.50, 0.53)
|
||||
MAP_AIR_RAID_CONFIRM_SECOND = 0.5
|
||||
|
||||
@cached_property
|
||||
def _load_ambush_template(self):
|
||||
TEMPLATE_AMBUSH_EVADE_SUCCESS.image = info_letter_preprocess(TEMPLATE_AMBUSH_EVADE_SUCCESS.image)
|
||||
TEMPLATE_AMBUSH_EVADE_FAILED.image = info_letter_preprocess(TEMPLATE_AMBUSH_EVADE_FAILED.image)
|
||||
return True
|
||||
|
||||
@cached_property
|
||||
def _load_walk_template(self):
|
||||
TEMPLATE_MAP_WALK_OUT_OF_STEP.image = info_letter_preprocess(TEMPLATE_MAP_WALK_OUT_OF_STEP.image)
|
||||
return True
|
||||
|
||||
def ambush_color_initial(self):
|
||||
MAP_AMBUSH.load_color(self.device.image)
|
||||
MAP_AIR_RAID.load_color(self.device.image)
|
||||
@ -52,7 +44,6 @@ class AmbushHandler(Combat):
|
||||
|
||||
def _handle_ambush_evade(self):
|
||||
logger.info('Map ambushed')
|
||||
_ = self._load_ambush_template
|
||||
self.wait_until_appear_then_click(MAP_AMBUSH_EVADE)
|
||||
|
||||
self.wait_until_appear(INFO_BAR_1)
|
||||
@ -119,7 +110,6 @@ class AmbushHandler(Combat):
|
||||
if not self.appear(INFO_BAR_1):
|
||||
return False
|
||||
|
||||
_ = self._load_walk_template
|
||||
image = info_letter_preprocess(np.array(self.image_crop(INFO_BAR_DETECT)))
|
||||
if TEMPLATE_MAP_WALK_OUT_OF_STEP.match(image):
|
||||
logger.warning('Map walk out of step.')
|
||||
|
@ -1,7 +1,7 @@
|
||||
from random import choice
|
||||
|
||||
import numpy as np
|
||||
from module.base.decorator import cached_property
|
||||
|
||||
from module.base.timer import Timer
|
||||
from module.combat.assets import GET_ITEMS_1
|
||||
from module.handler.assets import INFO_BAR_DETECT
|
||||
@ -12,6 +12,9 @@ from module.retire.dock import Dock, CARD_GRIDS
|
||||
from module.template.assets import TEMPLATE_ENHANCE_SUCCESS, TEMPLATE_ENHANCE_FAILED, TEMPLATE_ENHANCE_IN_BATTLE
|
||||
|
||||
VALID_SHIP_TYPES = ['dd', 'ss', 'cl', 'ca', 'bb', 'cv', 'repair', 'others']
|
||||
TEMPLATE_ENHANCE_SUCCESS.pre_process = info_letter_preprocess
|
||||
TEMPLATE_ENHANCE_FAILED.pre_process = info_letter_preprocess
|
||||
TEMPLATE_ENHANCE_IN_BATTLE.pre_process = info_letter_preprocess
|
||||
|
||||
|
||||
class Enhancement(Dock):
|
||||
@ -23,13 +26,6 @@ class Enhancement(Dock):
|
||||
return 10
|
||||
return 2000
|
||||
|
||||
@cached_property
|
||||
def _load_enhance_template(self):
|
||||
TEMPLATE_ENHANCE_SUCCESS.image = info_letter_preprocess(TEMPLATE_ENHANCE_SUCCESS.image)
|
||||
TEMPLATE_ENHANCE_FAILED.image = info_letter_preprocess(TEMPLATE_ENHANCE_FAILED.image)
|
||||
TEMPLATE_ENHANCE_IN_BATTLE.image = info_letter_preprocess(TEMPLATE_ENHANCE_IN_BATTLE.image)
|
||||
return True
|
||||
|
||||
def _enhance_enter(self, favourite=False, ship_type=None):
|
||||
"""
|
||||
Pages:
|
||||
@ -133,7 +129,6 @@ class Enhancement(Dock):
|
||||
True if able to enhance otherwise False
|
||||
Always paired with current ship_count
|
||||
"""
|
||||
_ = self._load_enhance_template
|
||||
skip_until_ensured = True
|
||||
enhanced = False
|
||||
while 1:
|
||||
|
Loading…
Reference in New Issue
Block a user