Compare commits

...

2 commits

Author SHA1 Message Date
0b1043b849 返回首页优化,避免公告场景与退出游戏同时触发时返回信号失效
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
2024-12-21 11:28:02 +08:00
8eee4520bf 节点相关方法补充优化
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
2024-12-19 17:42:11 +08:00
6 changed files with 103 additions and 24 deletions

BIN
mower/resources/rogue/node_Sarkaz/狭路相逢.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -1,5 +1,6 @@
from mower.utils import config from mower.utils import config
from mower.utils import typealias as tp from mower.utils import typealias as tp
from mower.utils.image import cmatch, cropimg, loadres
from .utils import template from .utils import template
@ -17,6 +18,7 @@ node_type = {
"思维边界", "思维边界",
"先行一步", "先行一步",
"兴致盎然", "兴致盎然",
"狭路相逢",
"Boss", "Boss",
] ]
} }
@ -25,25 +27,32 @@ node_type = {
class Node: class Node:
def __init__(self, scope): def __init__(self, scope):
self.scope: tp.Scope = scope # 节点范围 self.scope: tp.Scope = scope # 节点范围
self.status = False # 是否为下一步可前往的节点
self.next_nodes = [] # 可前往的下一个节点 self.next_nodes = [] # 可前往的下一个节点
self.detect_type() self.detect_type()
def detect_type(self): def detect_type(self):
for type in node_type[config.conf.maa_rg_theme]: for type in node_type[config.conf.maa_rg_theme]:
res_name = f"rogue/node_{config.conf.maa_rg_theme}/{type}" res_name = f"rogue/node_{config.conf.maa_rg_theme}/{type}"
res = loadres(res_name)
match_scope = ( match_scope = (
(self.scope[0][0] + 50, self.scope[1][0]), (self.scope[0][0] + 50, self.scope[1][0]),
(self.scope[0][1] - 50, self.scope[1][1]), (self.scope[0][1] - 50, self.scope[1][1]),
) )
score, scope = template(scene_image, res_name, self.scope) score, scope = template(scene_image, res, self.scope)
if score > 0.75: if score > 0.75:
self.type = type self.type = type
self.type_scope = scope self.type_scope = scope
# 用平均色判断可否前往
img = cropimg(scene_image, scope)
if cmatch(img, res) and self.type != "Boss":
self.status = True
return return
self.type = "未知" self.type = "未知"
self.type_scope = match_scope self.type_scope = match_scope
nodes: dict[str, Node] = {} nodes: dict[str, Node] = {} # 节点编号:节点对象
current_layer = 0 next_step: dict[str, int] = {} # 节点编号:剩余刷新次数
scene_image = None current_layer = 0 # 当前层数
scene_image = None # 整层的图像

View file

@ -2,9 +2,10 @@ import cv2
import numpy as np import numpy as np
from mower.solvers.rogue import data from mower.solvers.rogue import data
from mower.utils import config
from mower.utils import typealias as tp from mower.utils import typealias as tp
from mower.utils.image import thres2 from mower.utils.image import cropimg, loadres, thres2
from mower.utils.vector import in_scope from mower.utils.vector import in_scope, sa, vs
from .utils import template from .utils import template
@ -17,6 +18,7 @@ class NodeDetector:
self.detect_nodes() self.detect_nodes()
self.detect_x_edges() self.detect_x_edges()
self.detect_y_edges() self.detect_y_edges()
self.check_next_step()
def merge_rectangles(self, rects: list): def merge_rectangles(self, rects: list):
"合并有重叠的矩形" "合并有重叠的矩形"
@ -141,7 +143,24 @@ class NodeDetector:
data.nodes[next_id].type_scope[0][1], data.nodes[next_id].type_scope[0][1],
), ),
) )
score, _ = template(data.scene_image, "rogue/y_edge", scope) res = loadres("rogue/y_edge")
score, _ = template(data.scene_image, res, scope)
if score > 0.7: if score > 0.7:
data.nodes[node_id].next_nodes.append(next_id) data.nodes[node_id].next_nodes.append(next_id)
data.nodes[next_id].next_nodes.append(node_id) data.nodes[next_id].next_nodes.append(node_id)
def check_next_step(self):
for node_id in data.nodes:
if data.nodes[node_id].status:
data.next_step[node_id] = 2
def check_node_in_screen(self, node_id: str) -> tp.Scope | None:
score, scope = template(
data.scene_image, cropimg(config.recog.img, ((0, 150), (1920, 880)))
)
node_scope = data.nodes[node_id].scope
if in_scope(scope, node_scope[0]) and in_scope(scope, node_scope[1]):
res_scope = vs(node_scope[0], scope[0]), vs(node_scope[1], scope[0])
res_scope = sa(res_scope, (0, 150))
return res_scope
return None

View file

@ -5,7 +5,7 @@ import cv2
from mower.utils import config from mower.utils import config
from mower.utils import typealias as tp from mower.utils import typealias as tp
from mower.utils.generate_image import generate_image from mower.utils.generate_image import generate_image
from mower.utils.image import cropimg, loadres from mower.utils.image import cropimg
from mower.utils.log import logger from mower.utils.log import logger
@ -21,17 +21,17 @@ def find_text(text: str, size: int, threshold: float = 0.8) -> tp.Coordinate | N
def template( def template(
image: tp.Image, res: str, scope: Optional[tp.Scope] = None img1: tp.Image, img2: tp.Image, scope: Optional[tp.Scope] = None
) -> Tuple[float, tp.Scope]: ) -> Tuple[float, tp.Scope]:
"全景图中找模板" "img1中模板匹配img2"
try: try:
logger.debug(f"{res=}") h, w, _ = img2.shape
template = loadres(res)
h, w, _ = template.shape
if scope: if scope:
x, y = scope[0] x, y = scope[0]
img = cropimg(image, scope) img1 = cropimg(img1, scope)
result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED) else:
x, y = (0, 0)
result = cv2.matchTemplate(img1, img2, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
score = max_val score = max_val
p1 = (max_loc[0] + x, max_loc[1] + y) p1 = (max_loc[0] + x, max_loc[1] + y)

View file

@ -1,5 +1,5 @@
import cv2 import cv2
from matplotlib import pyplot as plt import numpy as np
from mower.solvers.rogue import data from mower.solvers.rogue import data
from mower.utils import config from mower.utils import config
@ -26,12 +26,51 @@ class WholeScene(BaseSolver):
logger.error(f"拼接过程中出现错误: {e}") logger.error(f"拼接过程中出现错误: {e}")
raise raise
def get_whole_scene(self): def horizontal_stitch(self, images: list) -> np.ndarray:
"""
水平方向拼接多张图像利用重叠区域对齐
"""
# 初始化最终拼接结果为第一张图像
stitched_image = images[0]
for i in range(1, len(images)):
# 当前图像和已拼接的图像
prev_img = stitched_image
curr_img = images[i]
# 转为灰度图,用于计算重叠区域
gray_prev = cv2.cvtColor(prev_img, cv2.COLOR_RGB2GRAY)
gray_curr = cv2.cvtColor(curr_img, cv2.COLOR_RGB2GRAY)
# 匹配 prev_img 的右侧区域和 curr_img 的左侧区域
match_width = min(
800, gray_prev.shape[1]
) # 假设重叠区域宽度最大为 800 像素
prev_region = gray_prev[:, -match_width:] # 取 prev_img 的右侧区域
result = cv2.matchTemplate(
gray_curr, prev_region, method=cv2.TM_CCOEFF_NORMED
)
_, _, _, max_loc = cv2.minMaxLoc(result)
overlap_x = max_loc[0] # 获取匹配的 x 坐标
# 校验重叠区域的合理性
if overlap_x >= 0 and overlap_x < curr_img.shape[1]:
# 拼接:保留 curr_img 中非重叠部分
non_overlap_part = curr_img[:, overlap_x + match_width :]
stitched_image = np.hstack((prev_img, non_overlap_part))
else:
logger.info("未检测到有效的重叠区域,直接拼接当前图像")
stitched_image = np.hstack((prev_img, curr_img))
return stitched_image
def get_whole_scene(self, draw=False):
images = [] images = []
new = cropimg(config.recog.img, ((0, 150), (1920, 880))) new = cropimg(config.recog.img, ((0, 150), (1920, 880)))
images.append(new) images.append(new)
while True: while True:
self.swipe_ext([(1820, 870), (1400, 870)], [200], 400, 0.1) self.swipe_ext([(1820, 870), (1000, 870)], [200], 400, 0.1)
new = cropimg(config.recog.img, ((0, 150), (1920, 880))) new = cropimg(config.recog.img, ((0, 150), (1920, 880)))
gray1 = cv2.cvtColor(new, cv2.COLOR_RGB2GRAY) gray1 = cv2.cvtColor(new, cv2.COLOR_RGB2GRAY)
gray2 = cv2.cvtColor(images[-1], cv2.COLOR_RGB2GRAY) gray2 = cv2.cvtColor(images[-1], cv2.COLOR_RGB2GRAY)
@ -42,10 +81,15 @@ class WholeScene(BaseSolver):
logger.info("滑到底了") logger.info("滑到底了")
break break
logger.info(f"截图读取完了,有{len(images)}张截图") logger.info(f"截图读取完了,有{len(images)}张截图")
for img in images:
plt.imshow(img)
plt.show()
if len(images) > 1: if len(images) > 1:
data.scene_image = self.stitch_images(images) data.scene_image = self.horizontal_stitch(images)
else: else:
data.scene_image = images[0] data.scene_image = images[0]
if draw:
from matplotlib import pyplot as plt
for img in images:
plt.imshow(img)
plt.show()
plt.imshow(data.scene_image)
plt.show()

View file

@ -29,10 +29,14 @@ from .utils import edge
@edge(Scene.SIGN_IN_DAILY, Scene.INDEX) @edge(Scene.SIGN_IN_DAILY, Scene.INDEX)
@edge(Scene.INDEX_ORIGINITE, Scene.INDEX) @edge(Scene.INDEX_ORIGINITE, Scene.INDEX)
@edge(Scene.INDEX_SANITY, Scene.INDEX) @edge(Scene.INDEX_SANITY, Scene.INDEX)
@edge(Scene.ANNOUNCEMENT, Scene.INDEX)
@edge(Scene.SIGN_IN_ORUNDUM, Scene.INDEX) @edge(Scene.SIGN_IN_ORUNDUM, Scene.INDEX)
def back_to_index(solver: BaseSolver): def back_to_index(solver: BaseSolver):
solver.back() solver.cback(1, id="back_to_index")
@edge(Scene.ANNOUNCEMENT, Scene.INDEX)
def close_announcement(solver: BaseSolver):
solver.ctap("announcement_close", 3)
@edge(Scene.LEAVE_INFRASTRUCTURE, Scene.INDEX) @edge(Scene.LEAVE_INFRASTRUCTURE, Scene.INDEX)