2020-03-29 01:22:46 +08:00
|
|
|
|
import collections
|
|
|
|
|
|
2020-09-18 01:02:53 +08:00
|
|
|
|
from module.base.base import ModuleBase
|
2020-07-15 00:15:21 +08:00
|
|
|
|
from module.base.decorator import Config
|
2020-09-18 01:02:53 +08:00
|
|
|
|
from module.base.utils import *
|
2020-07-15 00:15:21 +08:00
|
|
|
|
from module.exception import CampaignNameError
|
2020-03-29 01:22:46 +08:00
|
|
|
|
from module.logger import logger
|
2020-09-18 01:02:53 +08:00
|
|
|
|
from module.map_detection.utils import Points
|
2020-07-15 00:15:21 +08:00
|
|
|
|
from module.ocr.ocr import Ocr
|
2020-09-18 01:02:53 +08:00
|
|
|
|
from module.template.assets import *
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
|
|
|
|
|
2020-09-18 01:02:53 +08:00
|
|
|
|
class CampaignOcr(ModuleBase):
|
2020-05-27 21:29:32 +08:00
|
|
|
|
stage_entrance = {}
|
|
|
|
|
campaign_chapter = 0
|
|
|
|
|
|
2020-09-18 18:45:20 +08:00
|
|
|
|
@staticmethod
|
|
|
|
|
def _campaign_get_chapter_index(name):
|
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
name (str, int):
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
int
|
|
|
|
|
"""
|
|
|
|
|
if isinstance(name, int):
|
|
|
|
|
return name
|
|
|
|
|
else:
|
|
|
|
|
if name.isdigit():
|
|
|
|
|
return int(name)
|
2021-03-26 15:23:49 +08:00
|
|
|
|
elif name in ['a', 'c', 'as', 'cs', 'sp', 'ex_sp']:
|
2020-09-18 18:45:20 +08:00
|
|
|
|
return 1
|
2021-03-28 18:00:57 +08:00
|
|
|
|
elif name in ['b', 'd', 'bs', 'ds', 'ex_ex']:
|
2020-09-18 18:45:20 +08:00
|
|
|
|
return 2
|
2021-01-12 02:52:43 +08:00
|
|
|
|
else:
|
|
|
|
|
raise CampaignNameError
|
2020-09-18 18:45:20 +08:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _campaign_ocr_result_process(result):
|
|
|
|
|
# The result will be like '7--2', because tha dash in game is '–' not '-'
|
|
|
|
|
result = result.lower().replace('--', '-').replace('--', '-')
|
|
|
|
|
if result.startswith('-'):
|
|
|
|
|
result = result[1:]
|
|
|
|
|
if len(result) == 2 and result[0].isdigit():
|
|
|
|
|
result = '-'.join(result)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _campaign_separate_name(name):
|
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
name (str): Stage name in lowercase, such as 7-2, d3, sp3.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple[str]: Campaign_name and stage index in lowercase, Such as ['7', '2'], ['d', '3'], ['sp', '3'].
|
|
|
|
|
"""
|
|
|
|
|
if name == 'sp':
|
|
|
|
|
return 'ex_sp', '1'
|
2021-01-02 17:08:54 +08:00
|
|
|
|
elif name.startswith('extra'):
|
|
|
|
|
return 'ex_ex', '1'
|
2020-09-18 18:45:20 +08:00
|
|
|
|
elif '-' in name:
|
|
|
|
|
return name.split('-')
|
|
|
|
|
elif name.startswith('sp'):
|
|
|
|
|
return 'sp', name[-1]
|
|
|
|
|
elif name[-1].isdigit():
|
|
|
|
|
return name[:-1], name[-1]
|
|
|
|
|
|
|
|
|
|
logger.warning(f'Unknown stage name: {name}')
|
|
|
|
|
return name[0], name[1:]
|
|
|
|
|
|
2020-09-18 01:02:53 +08:00
|
|
|
|
def campaign_match_multi(self, template, image, stage_image=None, name_offset=(75, 9), name_size=(60, 16),
|
2020-07-15 00:15:21 +08:00
|
|
|
|
name_letter=(255, 255, 255), name_thresh=128):
|
2020-05-27 21:29:32 +08:00
|
|
|
|
"""
|
|
|
|
|
Args:
|
|
|
|
|
template (Template):
|
|
|
|
|
image: Screenshot
|
2020-09-18 01:02:53 +08:00
|
|
|
|
stage_image: Screenshot to find stage entrance.
|
2020-05-27 21:29:32 +08:00
|
|
|
|
name_offset (tuple[int]):
|
|
|
|
|
name_size (tuple[int]):
|
|
|
|
|
name_letter (tuple[int]):
|
2020-07-15 00:15:21 +08:00
|
|
|
|
name_thresh (int):
|
2020-05-27 21:29:32 +08:00
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
list[Button]: Stage clear buttons.
|
|
|
|
|
"""
|
2020-03-29 01:22:46 +08:00
|
|
|
|
digits = []
|
2020-09-18 01:02:53 +08:00
|
|
|
|
stage_image = image if stage_image is None else stage_image
|
|
|
|
|
result = template.match_multi(stage_image, similarity=0.85)
|
|
|
|
|
result = Points(result, config=self.config).group()
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
2020-04-18 21:19:15 +08:00
|
|
|
|
for point in result:
|
2020-09-26 02:59:10 +08:00
|
|
|
|
button = tuple(np.append(point, point + template.size))
|
2020-04-18 21:19:15 +08:00
|
|
|
|
point = point + name_offset
|
|
|
|
|
name = image.crop(np.append(point, point + name_size))
|
2020-07-15 00:15:21 +08:00
|
|
|
|
name = extract_letters(name, letter=name_letter, threshold=name_thresh)
|
2020-05-27 21:29:32 +08:00
|
|
|
|
stage = self._extract_stage_name(name)
|
2020-09-18 01:02:53 +08:00
|
|
|
|
|
|
|
|
|
area = area_offset(stage, point)
|
2020-09-18 18:45:20 +08:00
|
|
|
|
color = get_color(image, button)
|
2020-09-18 01:02:53 +08:00
|
|
|
|
digits.append(Button(area=area, color=color, button=button, name='stage'))
|
2020-05-27 21:29:32 +08:00
|
|
|
|
|
|
|
|
|
return digits
|
|
|
|
|
|
|
|
|
|
@Config.when(SERVER='en')
|
|
|
|
|
def campaign_extract_name_image(self, image):
|
|
|
|
|
digits = []
|
|
|
|
|
|
2020-09-18 01:02:53 +08:00
|
|
|
|
if 'normal' in self.config.STAGE_ENTRANCE:
|
|
|
|
|
digits += self.campaign_match_multi(TEMPLATE_STAGE_CLEAR, image, name_offset=(70, 12), name_size=(60, 14))
|
|
|
|
|
digits += self.campaign_match_multi(TEMPLATE_STAGE_PERCENT, image, name_offset=(45, 3), name_size=(60, 14))
|
2020-11-26 22:46:58 +08:00
|
|
|
|
if 'half' in self.config.STAGE_ENTRANCE:
|
|
|
|
|
digits += self.campaign_match_multi(
|
|
|
|
|
TEMPLATE_STAGE_HALF_PERCENT, image, name_offset=(48, 0), name_size=(60, 16))
|
2020-09-18 01:02:53 +08:00
|
|
|
|
if 'blue' in self.config.STAGE_ENTRANCE:
|
|
|
|
|
digits += self.campaign_match_multi(
|
|
|
|
|
TEMPLATE_STAGE_BLUE_PERCENT, image, extract_letters(image, letter=(255, 255, 255), threshold=153),
|
|
|
|
|
name_offset=(55, 0), name_size=(60, 16))
|
|
|
|
|
digits += self.campaign_match_multi(
|
|
|
|
|
TEMPLATE_STAGE_BLUE_CLEAR, image, extract_letters(image, letter=(99, 223, 239), threshold=153),
|
|
|
|
|
name_offset=(60, 12), name_size=(60, 16))
|
2020-05-27 21:29:32 +08:00
|
|
|
|
|
|
|
|
|
return digits
|
|
|
|
|
|
|
|
|
|
@Config.when(SERVER=None)
|
|
|
|
|
def campaign_extract_name_image(self, image):
|
|
|
|
|
digits = []
|
2020-04-18 21:19:15 +08:00
|
|
|
|
|
2020-09-18 01:02:53 +08:00
|
|
|
|
if 'normal' in self.config.STAGE_ENTRANCE:
|
|
|
|
|
digits += self.campaign_match_multi(TEMPLATE_STAGE_CLEAR, image, name_offset=(75, 9), name_size=(60, 16))
|
|
|
|
|
digits += self.campaign_match_multi(TEMPLATE_STAGE_PERCENT, image, name_offset=(48, 0), name_size=(60, 16))
|
2020-11-26 22:46:58 +08:00
|
|
|
|
if 'half' in self.config.STAGE_ENTRANCE:
|
|
|
|
|
digits += self.campaign_match_multi(
|
|
|
|
|
TEMPLATE_STAGE_HALF_PERCENT, image, name_offset=(48, 0), name_size=(60, 16))
|
2020-09-18 01:02:53 +08:00
|
|
|
|
if 'blue' in self.config.STAGE_ENTRANCE:
|
|
|
|
|
digits += self.campaign_match_multi(
|
|
|
|
|
TEMPLATE_STAGE_BLUE_PERCENT, image, extract_letters(image, letter=(255, 255, 255), threshold=153),
|
|
|
|
|
name_offset=(55, 0), name_size=(60, 16))
|
|
|
|
|
digits += self.campaign_match_multi(
|
|
|
|
|
TEMPLATE_STAGE_BLUE_CLEAR, image, extract_letters(image, letter=(99, 223, 239), threshold=153),
|
|
|
|
|
name_offset=(60, 12), name_size=(60, 16))
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
|
|
|
|
return digits
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2020-05-27 21:29:32 +08:00
|
|
|
|
def _extract_stage_name(image):
|
2020-04-27 19:32:58 +08:00
|
|
|
|
x_skip = 10
|
2020-03-29 01:22:46 +08:00
|
|
|
|
interval = 5
|
|
|
|
|
x_color = np.convolve(np.mean(image, axis=0), np.ones(interval), 'valid') / interval
|
|
|
|
|
x_list = np.where(x_color[x_skip:] > 235)[0]
|
|
|
|
|
if x_list is None or len(x_list) == 0:
|
|
|
|
|
logger.warning('No interval between digit and text.')
|
2021-01-02 17:08:54 +08:00
|
|
|
|
area = (0, 0, image.shape[1], image.shape[0])
|
|
|
|
|
else:
|
|
|
|
|
area = (0, 0, x_list[0] + 1 + x_skip, image.shape[0])
|
2020-07-15 00:15:21 +08:00
|
|
|
|
return np.array(area) + (-3, -7, 3, 7)
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
2020-05-27 21:29:32 +08:00
|
|
|
|
def _get_stage_name(self, image):
|
|
|
|
|
self.stage_entrance = {}
|
|
|
|
|
buttons = self.campaign_extract_name_image(image)
|
2020-09-18 01:02:53 +08:00
|
|
|
|
if len(buttons) == 0:
|
|
|
|
|
logger.warning('No stage found.')
|
2020-04-18 21:19:15 +08:00
|
|
|
|
|
2021-01-02 17:08:54 +08:00
|
|
|
|
ocr = Ocr(buttons, name='campaign', letter=(255, 255, 255), threshold=128,
|
|
|
|
|
alphabet='0123456789ABCDEFGHIJKLMNPQRSTUVWXYZ-')
|
2020-03-29 01:22:46 +08:00
|
|
|
|
result = ocr.ocr(image)
|
2020-04-18 21:19:15 +08:00
|
|
|
|
if not isinstance(result, list):
|
|
|
|
|
result = [result]
|
2020-09-18 18:45:20 +08:00
|
|
|
|
result = [self._campaign_ocr_result_process(res) for res in result]
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
2020-09-18 18:45:20 +08:00
|
|
|
|
chapter = [self._campaign_separate_name(res)[0] for res in result]
|
2021-01-11 02:16:07 +08:00
|
|
|
|
chapter = list(filter(('').__ne__, chapter))
|
|
|
|
|
if not chapter:
|
|
|
|
|
raise CampaignNameError
|
|
|
|
|
|
2020-03-29 01:22:46 +08:00
|
|
|
|
counter = collections.Counter(chapter)
|
2020-05-27 21:29:32 +08:00
|
|
|
|
self.campaign_chapter = counter.most_common()[0][0]
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
|
|
|
|
for name, button in zip(result, buttons):
|
|
|
|
|
button.area = button.button
|
|
|
|
|
button.name = name
|
2020-05-27 21:29:32 +08:00
|
|
|
|
self.stage_entrance[name] = button
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
2020-05-27 21:29:32 +08:00
|
|
|
|
logger.attr('Chapter', self.campaign_chapter)
|
|
|
|
|
logger.attr('Stage', ', '.join(self.stage_entrance.keys()))
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
|
|
|
|
def get_chapter_index(self, image):
|
|
|
|
|
"""
|
|
|
|
|
A tricky method for ui_ensure_index
|
|
|
|
|
"""
|
2020-04-25 17:10:22 +08:00
|
|
|
|
try:
|
2020-05-27 21:29:32 +08:00
|
|
|
|
self._get_stage_name(image)
|
2020-04-25 17:10:22 +08:00
|
|
|
|
except IndexError:
|
|
|
|
|
raise CampaignNameError
|
2020-03-29 01:22:46 +08:00
|
|
|
|
|
2020-09-18 18:45:20 +08:00
|
|
|
|
return self._campaign_get_chapter_index(self.campaign_chapter)
|