Add: Support .gif button assets

This commit is contained in:
LmeSzinc 2021-11-08 23:29:19 +08:00
parent 5f17cd71d5
commit 6184d2d71e
2 changed files with 86 additions and 38 deletions

View File

@ -1,5 +1,6 @@
import os
import imageio
import numpy as np
from PIL import Image
@ -21,50 +22,78 @@ VALID_SERVER = ['cn', 'en', 'jp', 'tw']
class ImageExtractor:
def __init__(self, module, file, config):
def __init__(self, module, file):
"""
Args:
module(str):
file(str): xxx.png
config(AzurLaneConfig):
file(str): xxx.png or xxx.gif
"""
self.module = module
self.name, self.ext = os.path.splitext(file)
self.config = config
self.area, self.color, self.button, self.file = {}, {}, {}, {}
for server in VALID_SERVER:
self.load(server)
def get_file(self, genre='', server='cn'):
file = f'{self.name}.{genre}{self.ext}' if genre else f'{self.name}{self.ext}'
file = os.path.join(self.config.ASSETS_FOLDER, server, self.module, file)
for ext in ['.png', '.gif']:
file = f'{self.name}.{genre}{ext}' if genre else f'{self.name}{ext}'
file = os.path.join(AzurLaneConfig.ASSETS_FOLDER, server, self.module, file).replace('\\', '/')
if os.path.exists(file):
return file
ext = '.png'
file = f'{self.name}.{genre}{ext}' if genre else f'{self.name}{ext}'
file = os.path.join(AzurLaneConfig.ASSETS_FOLDER, server, self.module, file).replace('\\', '/')
return file
def extract(self, file):
if os.path.splitext(file)[1] == '.gif':
# In a gif Button, use the first image.
bbox = None
mean = None
for image in imageio.mimread(file):
image = image[:, :, :3] if len(image.shape) == 3 else image
image = Image.fromarray(image)
new_bbox, new_mean = self._extract(image, file)
if bbox is None:
bbox = new_bbox
elif bbox != new_bbox:
logger.warning(f'{file} has multiple different bbox, this will cause unexpected behaviour')
if mean is None:
mean = new_mean
return bbox, mean
else:
image = Image.open(file).convert('RGB')
return self._extract(image, file)
@staticmethod
def extract(file):
image = Image.open(file).convert('RGB')
def _extract(image, file):
if image.size != (1280, 720):
logger.warning(f'Incorrect asset {image.size} {file}')
logger.warning(f'{file} has wrong resolution: {image.size}')
bbox = image.getbbox()
mean = get_color(image=image, area=bbox)
mean = tuple(np.rint(mean).astype(int))
return bbox, mean
def load(self, server='cn'):
if os.path.exists(self.get_file(server=server)):
area, color = self.extract(self.get_file(server=server))
file = self.get_file(server=server)
if os.path.exists(file):
area, color = self.extract(file)
button = area
if os.path.exists(self.get_file('AREA', server=server)):
area, _ = self.extract(self.get_file('AREA', server=server))
if os.path.exists(self.get_file('COLOR', server=server)):
_, color = self.extract(self.get_file('COLOR', server=server))
if os.path.exists(self.get_file('BUTTON', server=server)):
button, _ = self.extract(self.get_file('BUTTON', server=server))
override = self.get_file('AREA', server=server)
if os.path.exists(override):
area, _ = self.extract(override)
override = self.get_file('COLOR', server=server)
if os.path.exists(override):
_, color = self.extract(override)
override = self.get_file('BUTTON', server=server)
if os.path.exists(override):
button, _ = self.extract(override)
self.area[server] = area
self.color[server] = color
self.button[server] = button
self.file[server] = f"{self.config.ASSETS_FOLDER}/{server}/{self.module}/{self.name}{self.ext}"
self.file[server] = file
else:
logger.attr(server, f'{self.name} not found, use cn server assets')
self.area[server] = self.area['cn']
@ -115,10 +144,9 @@ class TemplateExtractor(ImageExtractor):
class ModuleExtractor:
def __init__(self, name, config):
def __init__(self, name):
self.name = name
self.config = config
self.folder = os.path.join(self.config.ASSETS_FOLDER, 'cn', name)
self.folder = os.path.join(AzurLaneConfig.ASSETS_FOLDER, 'cn', name)
@staticmethod
def split(file):
@ -137,13 +165,13 @@ class ModuleExtractor:
if file[0].isdigit():
continue
if file.startswith('TEMPLATE_'):
exp.append(TemplateExtractor(module=self.name, file=file, config=self.config).expression)
exp.append(TemplateExtractor(module=self.name, file=file).expression)
continue
# if file.startswith('OCR_'):
# exp.append(OcrExtractor(module=self.name, file=file, config=self.config).expression)
# continue
if self.is_base_image(file):
exp.append(ImageExtractor(module=self.name, file=file, config=self.config).expression)
exp.append(ImageExtractor(module=self.name, file=file).expression)
continue
logger.info('Module: %s(%s)' % (self.name, len(exp)))
@ -178,14 +206,14 @@ class AssetExtractor:
E.g. OCR_EXERCISE_TIMES.png.
"""
def __init__(self, config):
def __init__(self):
logger.info('Assets extract')
for module in os.listdir(config.ASSETS_FOLDER + '/cn'):
if os.path.isdir(os.path.join(config.ASSETS_FOLDER + '/cn', module)):
me = ModuleExtractor(name=module, config=config)
for module in os.listdir(AzurLaneConfig.ASSETS_FOLDER + '/cn'):
if os.path.isdir(os.path.join(AzurLaneConfig.ASSETS_FOLDER + '/cn', module)):
me = ModuleExtractor(name=module)
me.write()
if __name__ == '__main__':
ae = AssetExtractor(AzurLaneConfig('template'))
ae = AssetExtractor()

View File

@ -1,6 +1,7 @@
import os
import traceback
import imageio
from PIL import Image
import module.config.server as server
@ -35,6 +36,7 @@ class Button:
self._match_init = False
self.file = file[self.server] if isinstance(file, dict) else file
self.image = None
if self.file:
self.name = os.path.splitext(os.path.split(self.file)[1])[0]
elif name:
@ -43,6 +45,11 @@ class Button:
(filename, line_number, function_name, text) = traceback.extract_stack()[-2]
self.name = text[:text.find('=')].strip()
if self.file:
self.is_gif = os.path.splitext(self.file)[1] == '.gif'
else:
self.is_gif = False
def __str__(self):
return self.name
@ -92,6 +99,7 @@ class Button:
"""
self.color = get_color(image, self.area)
self.image = np.array(image.crop(self.area))
self.is_gif = False
return self.color
def load_offset(self, button):
@ -113,7 +121,14 @@ class Button:
If needs to call self.match, call this first.
"""
if not self._match_init:
self.image = np.array(Image.open(self.file).crop(self.area).convert('RGB'))
if self.is_gif:
self.image = []
for image in imageio.mimread(self.file):
image = image[:, :, :3] if len(image.shape) == 3 else image
image = crop(image, self.area)
self.image.append(image)
else:
self.image = np.array(Image.open(self.file).crop(self.area).convert('RGB'))
self._match_init = True
def match(self, image, offset=30, threshold=0.85):
@ -132,17 +147,22 @@ class Button:
if isinstance(offset, tuple):
offset = np.array((-offset[0], -offset[1], offset[0], offset[1]))
else:
offset = np.array((0, -offset, 0, offset))
# offset = np.array((0, -offset, 0, offset))
# offset = np.array((-offset, -offset, offset, offset))
offset = np.array((3, -offset, 3, offset))
image = np.array(image.crop(offset + self.area))
res = cv2.matchTemplate(self.image, image, cv2.TM_CCOEFF_NORMED)
_, similarity, _, point = cv2.minMaxLoc(res)
self._button_offset = area_offset(self._button, offset[:2] + np.array(point))
return similarity > threshold
if self.is_gif:
for template in self.image:
res = cv2.matchTemplate(template, image, cv2.TM_CCOEFF_NORMED)
_, similarity, _, point = cv2.minMaxLoc(res)
self._button_offset = area_offset(self._button, offset[:2] + np.array(point))
if similarity > threshold:
return True
return False
else:
res = cv2.matchTemplate(self.image, image, cv2.TM_CCOEFF_NORMED)
_, similarity, _, point = cv2.minMaxLoc(res)
self._button_offset = area_offset(self._button, offset[:2] + np.array(point))
return similarity > threshold
def match_appear_on(self, image, threshold=10):
"""