mower-ng/mower/utils/character_recognize.py

212 lines
6.7 KiB
Python

from functools import partial
import cv2
import numpy as np
from mower.models import avatar, portrait
from mower.utils import typealias as tp
from mower.utils.image import cmatch, cropimg, loadres, thres2
from mower.utils.log import logger
from mower.utils.matcher import GOOD_DISTANCE_LIMIT, flann, search_params
from mower.utils.vector import in_scope, va
FAST = cv2.FastFeatureDetector_create()
ORB = cv2.ORB_create(nfeatures=1000000, nlevels=1)
FLANN_INDEX_LSH = 6
index_params = dict(
algorithm=FLANN_INDEX_LSH,
table_number=1,
key_size=18,
multi_probe_level=1,
)
flann1 = cv2.FlannBasedMatcher(index_params, search_params)
def fast_keypoints(img: tp.GrayImage, mask: tp.GrayImage | None = None):
return ORB.compute(img, FAST.detect(img, mask))
def segment_room_select(img: tp.Image) -> list[tp.Scope]:
"基建房间内选干员"
line1 = cropimg(img, ((600, 519), (1920, 520)))
# 选中时的蓝色边框变白,信赖排序时的橙色变黑
hsv = cv2.cvtColor(line1, cv2.COLOR_RGB2HSV)
mask = cv2.inRange(hsv, (95, 230, 200), (105, 255, 230))
line1 = cv2.cvtColor(line1, cv2.COLOR_RGB2GRAY)
line1[mask > 0] = (255,)
mask = cv2.inRange(hsv, (10, 0, 0), (20, 255, 255))
line1[mask > 0] = (0,)
line1 = thres2(line1, 100)
line = line1[-1]
prev = line[0]
start = None
name_x = []
for i in range(1, line1.shape[1]):
curr = line[i]
if prev == 0 and curr == 255 and start and i - start > 186:
name_x.append((i + 434, i + 574))
elif prev == 255 and curr == 0:
start = i
prev = curr
avatar_y = ((242, 317), (662, 737))
avatar_p = []
for x in name_x:
for y in avatar_y:
avatar_p.append(tuple(zip(x, y)))
empty = loadres("choose_agent/empty")
if avatar_p and cmatch(cropimg(img, avatar_p[-1]), empty, 20):
avatar_p.pop()
logger.debug(avatar_p)
return avatar_p
def segment_team(img: tp.Image) -> list[tp.Scope]:
"编队界面"
# TODO: 利用img判断编队缺人的情况
result = []
for i in range(6):
x = 283 + 232 * i
for y in [204, 637]:
result.append(((x, y), va((x, y), (180, 110))))
logger.debug(result)
return result
def segment_team_select(img: tp.Image) -> list[tp.Scope]:
"作战编队选干员"
line1 = cropimg(img, ((545, 510), (1920, 511)))
# 选中时的蓝色边框变白,信赖排序时的橙色变黑
hsv = cv2.cvtColor(line1, cv2.COLOR_RGB2HSV)
mask = cv2.inRange(hsv, (95, 230, 200), (105, 255, 230))
line1 = cv2.cvtColor(line1, cv2.COLOR_RGB2GRAY)
line1[mask > 0] = (255,)
mask = cv2.inRange(hsv, (10, 0, 0), (20, 255, 255))
line1[mask > 0] = (0,)
line1 = thres2(line1, 100)
line = line1[-1]
prev = line[0]
start = None
name_x = []
for i in range(1, line1.shape[1]):
curr = line[i]
if prev == 0 and curr == 255 and start and i - start > 90:
name_x.append((i + 381, i + 521))
elif prev == 255 and curr == 0:
start = i
prev = curr
avatar_y = ((229, 304), (645, 720))
avatar_p = []
for x in name_x:
for y in avatar_y:
avatar_p.append(tuple(zip(x, y)))
empty = loadres("choose_agent/empty")
if avatar_p and cmatch(cropimg(img, avatar_p[-1]), empty, 20):
avatar_p.pop()
logger.debug(avatar_p)
return avatar_p
def match_operator(
gray: tp.GrayImage, segment: list[tp.Scope], model: list
) -> list[tuple[str | None, tp.Scope]]:
if len(segment) == 0:
logger.debug(f"{len(segment)=}")
return ()
w = model["w"]
h = model["h"]
col = model["col"]
name_list = model["name"]
pt2 = model["pt"]
des2 = model["des"]
mask = np.zeros((1080, 1920), dtype=np.uint8)
for seg in segment:
cv2.rectangle(mask, seg[0], seg[1], (255,), -1)
kp1, des1 = fast_keypoints(gray, mask)
result = [{} for _ in range(len(segment))]
for match in flann.knnMatch(des1, des2, k=2):
if len(match) != 2:
continue
m, n = match
if m.distance > GOOD_DISTANCE_LIMIT * n.distance:
continue
x, y = pt2[m.trainIdx]
x, y = int(x / w), int(y / h)
name = name_list[y * col + x]
for i, seg in enumerate(segment):
if in_scope(seg, kp1[m.queryIdx].pt):
if name in result[i]:
result[i][name] += 1
else:
result[i][name] = 1
break
result = [list(r.items()) for r in result]
result = [max(r, key=lambda p: p[1]) if r else None for r in result]
logger.debug(result)
bad_index = []
for i, r in enumerate(result):
if r is None or r[1] < 10:
bad_index.append(i)
if bad_index:
mask = np.zeros((1080, 1920), dtype=np.uint8)
for i in bad_index:
cv2.rectangle(mask, segment[i][0], segment[i][1], (255,), -1)
kp1, des1 = fast_keypoints(gray, mask)
result1 = [{} for _ in range(len(bad_index))]
for match in flann1.knnMatch(des1, des2, k=2):
if len(match) != 2:
continue
m, n = match
if m.distance > GOOD_DISTANCE_LIMIT * n.distance:
continue
x, y = pt2[m.trainIdx]
x, y = int(x / w), int(y / h)
name = name_list[y * col + x]
for i, j in enumerate(bad_index):
if in_scope(segment[j], kp1[m.queryIdx].pt):
if name in result1[i]:
result1[i][name] += 1
else:
result1[i][name] = 1
break
result1 = [list(r.items()) for r in result1]
result1 = [max(r, key=lambda p: p[1]) if r else None for r in result1]
logger.debug(result1)
for i, r in enumerate(result1):
result[bad_index[i]] = r
return list(zip([r[0] if r else None for r in result], segment))
match_portrait = partial(match_operator, model=portrait)
match_avatar = partial(match_operator, model=avatar)
def operator_room_select(img: tp.Image) -> list[tuple[str | None, tp.Scope]]:
"基建房间内选干员"
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
return match_portrait(gray, segment_room_select(img))
def operator_team(img: tp.Image) -> list[tuple[str | None, tp.Scope]]:
"编队界面"
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
return match_portrait(gray, segment_team(img))
def operator_team_select(img: tp.Image) -> list[tuple[str | None, tp.Scope]]:
"作战编队选干员"
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
return match_portrait(gray, segment_team_select(img))