1
0
mirror of https://github.com/sui-feng-cb/AzurLaneAutoScript1.git synced 2026-06-30 01:44:12 +08:00
* Opt: using template matching for commission suffix recognition (#5731)

* Opt: using pHash and template matching for commission suffix recognition

* Refactor: improve suffix image processing and hash calculation

* Revert "Upd: [JP] asset GET_ITEMS_X (#5718)" (#5751)

This reverts commit c852cff758.

* Chore: move hashlib to local import

* Upd: [TW] Event entrance of Revelations of Dust Rerun (event_20230223_cn)

---------

Co-authored-by: guoh064 <50830808+guoh064@users.noreply.github.com>
This commit is contained in:
LmeSzinc
2026-06-25 19:39:34 +08:00
committed by GitHub
8 changed files with 135 additions and 72 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -301,3 +301,4 @@ To add a new event, add a new row in here, and run `python -m module.config.conf
| 20260528 | event 20220818 cn | Operation Convergence | - | - | - | 復刻遠匯點作戰 | | 20260528 | event 20220818 cn | Operation Convergence | - | - | - | 復刻遠匯點作戰 |
| 20260605 | event 20260520 cn | Alliance Before the Hagiobull | - | - | - | 聖印前的同盟 | | 20260605 | event 20260520 cn | Alliance Before the Hagiobull | - | - | - | 聖印前的同盟 |
| 20260618 | event 20240521 cn | Light of the Martyrium Rerun | 复刻绽放于辉光之城 | Light of the Martyrium Rerun | 赫輝のマルティリウム(復刻) | - | | 20260618 | event 20240521 cn | Light of the Martyrium Rerun | 复刻绽放于辉光之城 | Light of the Martyrium Rerun | 赫輝のマルティリウム(復刻) | - |
| 20260625 | event 20230223 cn | Revelations of Dust | - | - | - | 復刻湮燼塵墟 |

View File

@@ -29,10 +29,10 @@ EXP_INFO_B = Button(area={'cn': (332, 107, 387, 118), 'en': (332, 107, 387, 118)
EXP_INFO_C = Button(area={'cn': (332, 56, 345, 107), 'en': (332, 56, 345, 107), 'jp': (332, 56, 345, 107), 'tw': (332, 56, 345, 107)}, color={'cn': (198, 208, 198), 'en': (198, 208, 198), 'jp': (198, 208, 198), 'tw': (198, 208, 198)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_C.png', 'en': './assets/en/combat/EXP_INFO_C.png', 'jp': './assets/jp/combat/EXP_INFO_C.png', 'tw': './assets/tw/combat/EXP_INFO_C.png'}) EXP_INFO_C = Button(area={'cn': (332, 56, 345, 107), 'en': (332, 56, 345, 107), 'jp': (332, 56, 345, 107), 'tw': (332, 56, 345, 107)}, color={'cn': (198, 208, 198), 'en': (198, 208, 198), 'jp': (198, 208, 198), 'tw': (198, 208, 198)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_C.png', 'en': './assets/en/combat/EXP_INFO_C.png', 'jp': './assets/jp/combat/EXP_INFO_C.png', 'tw': './assets/tw/combat/EXP_INFO_C.png'})
EXP_INFO_D = Button(area={'cn': (328, 45, 341, 119), 'en': (328, 45, 341, 119), 'jp': (328, 45, 341, 119), 'tw': (328, 45, 341, 119)}, color={'cn': (199, 208, 199), 'en': (199, 208, 199), 'jp': (199, 208, 199), 'tw': (199, 208, 199)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_D.png', 'en': './assets/en/combat/EXP_INFO_D.png', 'jp': './assets/jp/combat/EXP_INFO_D.png', 'tw': './assets/tw/combat/EXP_INFO_D.png'}) EXP_INFO_D = Button(area={'cn': (328, 45, 341, 119), 'en': (328, 45, 341, 119), 'jp': (328, 45, 341, 119), 'tw': (328, 45, 341, 119)}, color={'cn': (199, 208, 199), 'en': (199, 208, 199), 'jp': (199, 208, 199), 'tw': (199, 208, 199)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_D.png', 'en': './assets/en/combat/EXP_INFO_D.png', 'jp': './assets/jp/combat/EXP_INFO_D.png', 'tw': './assets/tw/combat/EXP_INFO_D.png'})
EXP_INFO_S = Button(area={'cn': (342, 107, 389, 119), 'en': (342, 107, 389, 119), 'jp': (342, 107, 389, 119), 'tw': (342, 107, 389, 119)}, color={'cn': (233, 242, 127), 'en': (233, 242, 127), 'jp': (233, 242, 127), 'tw': (233, 242, 127)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_S.png', 'en': './assets/en/combat/EXP_INFO_S.png', 'jp': './assets/jp/combat/EXP_INFO_S.png', 'tw': './assets/tw/combat/EXP_INFO_S.png'}) EXP_INFO_S = Button(area={'cn': (342, 107, 389, 119), 'en': (342, 107, 389, 119), 'jp': (342, 107, 389, 119), 'tw': (342, 107, 389, 119)}, color={'cn': (233, 242, 127), 'en': (233, 242, 127), 'jp': (233, 242, 127), 'tw': (233, 242, 127)}, button={'cn': (1133, 634, 1262, 650), 'en': (1133, 634, 1262, 650), 'jp': (1133, 634, 1262, 650), 'tw': (1133, 634, 1262, 650)}, file={'cn': './assets/cn/combat/EXP_INFO_S.png', 'en': './assets/en/combat/EXP_INFO_S.png', 'jp': './assets/jp/combat/EXP_INFO_S.png', 'tw': './assets/tw/combat/EXP_INFO_S.png'})
GET_ITEMS_1 = Button(area={'cn': (538, 217, 741, 253), 'en': (551, 223, 736, 250), 'jp': (548, 217, 741, 253), 'tw': (539, 217, 742, 253)}, color={'cn': (160, 192, 248), 'en': (166, 194, 235), 'jp': (144, 183, 250), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_1.png', 'en': './assets/en/combat/GET_ITEMS_1.png', 'jp': './assets/jp/combat/GET_ITEMS_1.png', 'tw': './assets/tw/combat/GET_ITEMS_1.png'}) GET_ITEMS_1 = Button(area={'cn': (538, 217, 741, 253), 'en': (551, 223, 736, 250), 'jp': (539, 220, 741, 252), 'tw': (539, 217, 742, 253)}, color={'cn': (160, 192, 248), 'en': (166, 194, 235), 'jp': (146, 184, 249), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_1.png', 'en': './assets/en/combat/GET_ITEMS_1.png', 'jp': './assets/jp/combat/GET_ITEMS_1.png', 'tw': './assets/tw/combat/GET_ITEMS_1.png'})
GET_ITEMS_1_RYZA = Button(area={'cn': (564, 217, 721, 245), 'en': (577, 211, 704, 239), 'jp': (566, 217, 719, 244), 'tw': (564, 218, 723, 246)}, color={'cn': (176, 199, 243), 'en': (172, 199, 246), 'jp': (179, 201, 243), 'tw': (173, 197, 242)}, button={'cn': (1000, 631, 1055, 689), 'en': (1000, 631, 1055, 689), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_1_RYZA.png', 'en': './assets/en/combat/GET_ITEMS_1_RYZA.png', 'jp': './assets/jp/combat/GET_ITEMS_1_RYZA.png', 'tw': './assets/tw/combat/GET_ITEMS_1_RYZA.png'}) GET_ITEMS_1_RYZA = Button(area={'cn': (564, 217, 721, 245), 'en': (577, 211, 704, 239), 'jp': (566, 217, 719, 244), 'tw': (564, 218, 723, 246)}, color={'cn': (176, 199, 243), 'en': (172, 199, 246), 'jp': (179, 201, 243), 'tw': (173, 197, 242)}, button={'cn': (1000, 631, 1055, 689), 'en': (1000, 631, 1055, 689), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_1_RYZA.png', 'en': './assets/en/combat/GET_ITEMS_1_RYZA.png', 'jp': './assets/jp/combat/GET_ITEMS_1_RYZA.png', 'tw': './assets/tw/combat/GET_ITEMS_1_RYZA.png'})
GET_ITEMS_2 = Button(area={'cn': (538, 146, 742, 182), 'en': (551, 149, 735, 175), 'jp': (547, 143, 742, 179), 'tw': (538, 148, 741, 182)}, color={'cn': (160, 192, 248), 'en': (167, 195, 235), 'jp': (145, 183, 250), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_2.png', 'en': './assets/en/combat/GET_ITEMS_2.png', 'jp': './assets/jp/combat/GET_ITEMS_2.png', 'tw': './assets/tw/combat/GET_ITEMS_2.png'}) GET_ITEMS_2 = Button(area={'cn': (538, 146, 742, 182), 'en': (551, 149, 735, 175), 'jp': (536, 146, 741, 182), 'tw': (538, 148, 741, 182)}, color={'cn': (160, 192, 248), 'en': (167, 195, 235), 'jp': (145, 182, 249), 'tw': (155, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_2.png', 'en': './assets/en/combat/GET_ITEMS_2.png', 'jp': './assets/jp/combat/GET_ITEMS_2.png', 'tw': './assets/tw/combat/GET_ITEMS_2.png'})
GET_ITEMS_3 = Button(area={'cn': (539, 143, 742, 179), 'en': (548, 136, 740, 172), 'jp': (547, 143, 742, 179), 'tw': (546, 145, 742, 178)}, color={'cn': (161, 193, 248), 'en': (152, 185, 237), 'jp': (145, 183, 250), 'tw': (156, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_3.png', 'en': './assets/en/combat/GET_ITEMS_3.png', 'jp': './assets/jp/combat/GET_ITEMS_3.png', 'tw': './assets/tw/combat/GET_ITEMS_3.png'}) GET_ITEMS_3 = Button(area={'cn': (539, 143, 742, 179), 'en': (548, 136, 740, 172), 'jp': (540, 143, 742, 179), 'tw': (546, 145, 742, 178)}, color={'cn': (161, 193, 248), 'en': (152, 185, 237), 'jp': (145, 182, 248), 'tw': (156, 190, 248)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_ITEMS_3.png', 'en': './assets/en/combat/GET_ITEMS_3.png', 'jp': './assets/jp/combat/GET_ITEMS_3.png', 'tw': './assets/tw/combat/GET_ITEMS_3.png'})
GET_ITEMS_3_CHECK = Button(area={'cn': (335, 184, 947, 203), 'en': (335, 184, 947, 203), 'jp': (335, 184, 947, 203), 'tw': (335, 184, 947, 203)}, color={'cn': (84, 95, 109), 'en': (84, 95, 109), 'jp': (84, 95, 109), 'tw': (84, 95, 109)}, button={'cn': (335, 184, 947, 203), 'en': (335, 184, 947, 203), 'jp': (335, 184, 947, 203), 'tw': (335, 184, 947, 203)}, file={'cn': './assets/cn/combat/GET_ITEMS_3_CHECK.png', 'en': './assets/en/combat/GET_ITEMS_3_CHECK.png', 'jp': './assets/jp/combat/GET_ITEMS_3_CHECK.png', 'tw': './assets/tw/combat/GET_ITEMS_3_CHECK.png'}) GET_ITEMS_3_CHECK = Button(area={'cn': (335, 184, 947, 203), 'en': (335, 184, 947, 203), 'jp': (335, 184, 947, 203), 'tw': (335, 184, 947, 203)}, color={'cn': (84, 95, 109), 'en': (84, 95, 109), 'jp': (84, 95, 109), 'tw': (84, 95, 109)}, button={'cn': (335, 184, 947, 203), 'en': (335, 184, 947, 203), 'jp': (335, 184, 947, 203), 'tw': (335, 184, 947, 203)}, file={'cn': './assets/cn/combat/GET_ITEMS_3_CHECK.png', 'en': './assets/en/combat/GET_ITEMS_3_CHECK.png', 'jp': './assets/jp/combat/GET_ITEMS_3_CHECK.png', 'tw': './assets/tw/combat/GET_ITEMS_3_CHECK.png'})
GET_SHIP = Button(area={'cn': (1104, 610, 1110, 630), 'en': (1104, 610, 1110, 630), 'jp': (1104, 610, 1110, 630), 'tw': (1104, 610, 1110, 630)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_SHIP.png', 'en': './assets/en/combat/GET_SHIP.png', 'jp': './assets/jp/combat/GET_SHIP.png', 'tw': './assets/tw/combat/GET_SHIP.png'}) GET_SHIP = Button(area={'cn': (1104, 610, 1110, 630), 'en': (1104, 610, 1110, 630), 'jp': (1104, 610, 1110, 630), 'tw': (1104, 610, 1110, 630)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (1000, 631, 1055, 689), 'en': (999, 630, 1047, 691), 'jp': (1000, 631, 1055, 689), 'tw': (1000, 631, 1055, 689)}, file={'cn': './assets/cn/combat/GET_SHIP.png', 'en': './assets/en/combat/GET_SHIP.png', 'jp': './assets/jp/combat/GET_SHIP.png', 'tw': './assets/tw/combat/GET_SHIP.png'})
LOADING_BAR = Button(area={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, color={'cn': (172, 205, 232), 'en': (172, 205, 232), 'jp': (172, 205, 232), 'tw': (172, 205, 232)}, button={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, file={'cn': './assets/cn/combat/LOADING_BAR.png', 'en': './assets/en/combat/LOADING_BAR.png', 'jp': './assets/jp/combat/LOADING_BAR.png', 'tw': './assets/tw/combat/LOADING_BAR.png'}) LOADING_BAR = Button(area={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, color={'cn': (172, 205, 232), 'en': (172, 205, 232), 'jp': (172, 205, 232), 'tw': (172, 205, 232)}, button={'cn': (33, 676, 1247, 680), 'en': (33, 676, 1247, 680), 'jp': (33, 676, 1247, 680), 'tw': (33, 676, 1247, 680)}, file={'cn': './assets/cn/combat/LOADING_BAR.png', 'en': './assets/en/combat/LOADING_BAR.png', 'jp': './assets/jp/combat/LOADING_BAR.png', 'tw': './assets/tw/combat/LOADING_BAR.png'})

View File

@@ -1,6 +1,5 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
import module.config.server as server
from module.base.decorator import Config from module.base.decorator import Config
from module.base.filter import Filter from module.base.filter import Filter
from module.base.utils import * from module.base.utils import *
@@ -23,20 +22,54 @@ COMMISSION_FILTER = Filter(
) )
class SuffixOcr(Ocr): def crop_suffix_image(image, area):
def pre_process(self, image): """
image = super().pre_process(image) Args:
image (np.ndarray):
area (tuple): Commission name area.
left = np.where(np.min(image[5:-5, :], axis=0) < 85)[0] Returns:
# Look back several pixels np.ndarray | None: Cropped suffix image, black letters on white background.
if server.server in ['jp']: """
look_back = 21 name_image = crop(image, area)
else: name_image = extract_letters(name_image, letter=(255, 255, 255), threshold=128).astype(np.uint8)
look_back = 18
if len(left):
image = image[:, left[-1] - look_back:]
return image line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()
columns = np.where(line < 250)[0]
if not len(columns):
return None
# Look back several pixels from the rightmost letter to include Roman numerals.
threshold = 250
look_back = 10
for i in range(columns[-1], 0, -1):
if line[i] > threshold:
if columns[-1] - i > look_back:
look_back = columns[-1] - i
break
left = columns[-1] - look_back
right = columns[-1] + 1
x1, y1 = area[0:2]
suffix_area = area_offset((left - 3, -3, right + 3, name_image.shape[0] + 3), (x1, y1))
image = crop(image, suffix_area)
image = extract_letters(image, letter=(255, 255, 255), threshold=128).astype(np.uint8)
return image
def image_hash(image):
"""
Args:
image (np.ndarray):
Returns:
str:
"""
if image is None:
return ''
import hashlib
return hashlib.md5(image.tobytes()).hexdigest()
class Commission: class Commission:
@@ -46,10 +79,10 @@ class Commission:
name: str name: str
# If success to parse commission name # If success to parse commission name
valid: bool valid: bool
# Suffix in roman numerals # Cropped suffix image, black letters on white background, or None
# May be wrong if commission does not have a suffix suffix_image: np.ndarray
# Value: ⅠⅡⅢⅤⅣⅥ # Hash of suffix image, used only for logging, or empty string if suffix_image is None
suffix: str suffix_hash: str
# Genre name in project_data.py # Genre name in project_data.py
# Value: major_comm, daily_resource, urgent_cube, ... # Value: major_comm, daily_resource, urgent_cube, ...
genre: str genre: str
@@ -113,8 +146,8 @@ class Commission:
self.genre = self.commission_name_parse(self.name) self.genre = self.commission_name_parse(self.name)
# Suffix # Suffix
ocr = SuffixOcr(button, lang='azur_lane', letter=(255, 255, 255), threshold=128, alphabet='IV') self.suffix_image = crop_suffix_image(self.image, self.button.area)
self.suffix = self.beautify_name(ocr.ocr(self.image)) self.suffix_hash = image_hash(self.suffix_image)
# Duration time # Duration time
area = area_offset((290, 68, 390, 95), self.area[0:2]) area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -160,8 +193,8 @@ class Commission:
self.genre = self.commission_name_parse(self.name) self.genre = self.commission_name_parse(self.name)
# Suffix # Suffix
ocr = SuffixOcr(button, lang='azur_lane', letter=(255, 255, 255), threshold=128, alphabet='IV') self.suffix_image = crop_suffix_image(self.image, self.button.area)
self.suffix = self.beautify_name(ocr.ocr(self.image)) self.suffix_hash = image_hash(self.suffix_image)
# Duration time # Duration time
area = area_offset((290, 68, 390, 95), self.area[0:2]) area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -209,8 +242,8 @@ class Commission:
self.genre = self.commission_name_parse(self.name) self.genre = self.commission_name_parse(self.name)
# Suffix # Suffix
ocr = SuffixOcr(button, lang='azur_lane', letter=(255, 255, 255), threshold=128, alphabet='IV') self.suffix_image = crop_suffix_image(self.image, self.button.area)
self.suffix = self.beautify_name(ocr.ocr(self.image)) self.suffix_hash = image_hash(self.suffix_image)
# Duration time # Duration time
area = area_offset((290, 68, 390, 95), self.area[0:2]) area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -254,8 +287,8 @@ class Commission:
self.genre = self.commission_name_parse(self.name) self.genre = self.commission_name_parse(self.name)
# Suffix # Suffix
ocr = SuffixOcr(button, lang='azur_lane', letter=(255, 255, 255), threshold=128, alphabet='IV') self.suffix_image = crop_suffix_image(self.image, self.button.area)
self.suffix = self.beautify_name(ocr.ocr(self.image)) self.suffix_hash = image_hash(self.suffix_image)
# Duration time # Duration time
area = area_offset((290, 68, 390, 95), self.area[0:2]) area = area_offset((290, 68, 390, 95), self.area[0:2])
@@ -288,7 +321,7 @@ class Commission:
self.status = dic[int(np.argmax(color))] self.status = dic[int(np.argmax(color))]
def __str__(self): def __str__(self):
name = f'{self.name} | {self.suffix}' name = f'{self.name} | {self.suffix_hash}' if self.suffix_hash else self.name
if not self.valid: if not self.valid:
return f'{name} (Invalid)' return f'{name} (Invalid)'
info = {'Genre': self.genre, 'Status': self.status, 'Duration': self.duration} info = {'Genre': self.genre, 'Status': self.status, 'Duration': self.duration}
@@ -315,7 +348,7 @@ class Commission:
if self.genre != other.genre or self.status != other.status: if self.genre != other.genre or self.status != other.status:
return False return False
if self.category_str == 'daily': if self.category_str == 'daily':
if self.suffix != other.suffix: if not self.suffix_match(other):
return False return False
if self.genre == 'urgent_box': if self.genre == 'urgent_box':
for tag in ['NYB', 'BIW']: for tag in ['NYB', 'BIW']:
@@ -332,7 +365,7 @@ class Commission:
return False return False
if self.repeat_count != other.repeat_count: if self.repeat_count != other.repeat_count:
return False return False
if self.genre in ['extra_oil', 'night_oil'] and self.suffix != other.suffix: if self.genre in ['extra_oil', 'night_oil'] and not self.suffix_match(other):
return False return False
return True return True
@@ -340,6 +373,35 @@ class Commission:
def __hash__(self): def __hash__(self):
return hash(f'{self.genre}_{self.name}') return hash(f'{self.genre}_{self.name}')
def suffix_match(self, other, similarity=0.75):
"""
Args:
other (Commission):
similarity (float): 0-1. Similarity.
Returns:
bool:
"""
if self.suffix_image is None and other.suffix_image is None:
return True
if self.suffix_image is None or other.suffix_image is None:
return False
def match(image, template):
template = crop(template, (3, 3, template.shape[1] - 3, template.shape[0] - 3), copy=False)
if image.shape[0] < template.shape[0] or image.shape[1] < template.shape[1]:
return 0.0
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
_, sim, _, _ = cv2.minMaxLoc(res)
return sim
sim = max(
match(self.suffix_image, other.suffix_image),
match(other.suffix_image, self.suffix_image)
)
return sim >= similarity
def parse_time(self, string): def parse_time(self, string):
""" """
Args: Args:

View File

@@ -1643,8 +1643,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -1656,11 +1656,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {
@@ -1925,8 +1925,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -1938,11 +1938,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {
@@ -2322,8 +2322,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -2335,11 +2335,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {
@@ -4069,8 +4069,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -4082,11 +4082,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {
@@ -4483,8 +4483,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -4496,11 +4496,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {
@@ -4897,8 +4897,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -4910,11 +4910,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {
@@ -5311,8 +5311,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -5324,11 +5324,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {
@@ -5715,8 +5715,8 @@
"type": "select", "type": "select",
"value": "campaign_main", "value": "campaign_main",
"option": [ "option": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
], ],
"option_cn": [ "option_cn": [
"event_20240521_cn" "event_20240521_cn"
@@ -5728,11 +5728,11 @@
"event_20240521_cn" "event_20240521_cn"
], ],
"option_tw": [ "option_tw": [
"event_20260520_cn" "event_20230223_cn"
], ],
"option_bold": [ "option_bold": [
"event_20240521_cn", "event_20230223_cn",
"event_20260520_cn" "event_20240521_cn"
] ]
}, },
"Mode": { "Mode": {

View File

@@ -742,7 +742,7 @@
"event_20220915_cn": "復刻紫絳槿嵐", "event_20220915_cn": "復刻紫絳槿嵐",
"event_20221124_cn": "復刻鍊金術士與秘密遺跡群島", "event_20221124_cn": "復刻鍊金術士與秘密遺跡群島",
"event_20221222_cn": "復刻定向折疊", "event_20221222_cn": "復刻定向折疊",
"event_20230223_cn": "湮燼塵墟", "event_20230223_cn": "復刻湮燼塵墟",
"event_20230525_cn": "空相交會點", "event_20230525_cn": "空相交會點",
"event_20230803_cn": "奏響鳶尾之歌", "event_20230803_cn": "奏響鳶尾之歌",
"event_20230817_cn": "愚者的天平", "event_20230817_cn": "愚者的天平",