diff --git a/mower/resources/rogue/node_Sarkaz/狭路相逢.png b/mower/resources/rogue/node_Sarkaz/狭路相逢.png new file mode 100644 index 00000000..a3e31fcb --- /dev/null +++ b/mower/resources/rogue/node_Sarkaz/狭路相逢.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:225a80f0398b06b6e3959465fcc542d8c48e1cfa4b326f49f4c95fbe4f74a184 +size 4870 diff --git a/mower/solvers/rogue/data.py b/mower/solvers/rogue/data.py index 0f7ad138..b3fbb690 100644 --- a/mower/solvers/rogue/data.py +++ b/mower/solvers/rogue/data.py @@ -1,5 +1,6 @@ from mower.utils import config from mower.utils import typealias as tp +from mower.utils.image import cmatch, cropimg, loadres from .utils import template @@ -17,6 +18,7 @@ node_type = { "思维边界", "先行一步", "兴致盎然", + "狭路相逢", "Boss", ] } @@ -25,25 +27,32 @@ node_type = { class Node: def __init__(self, scope): self.scope: tp.Scope = scope # 节点范围 + self.status = False # 是否为下一步可前往的节点 self.next_nodes = [] # 可前往的下一个节点 self.detect_type() def detect_type(self): for type in node_type[config.conf.maa_rg_theme]: res_name = f"rogue/node_{config.conf.maa_rg_theme}/{type}" + res = loadres(res_name) match_scope = ( (self.scope[0][0] + 50, self.scope[1][0]), (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: self.type = type self.type_scope = scope + # 用平均色判断可否前往 + img = cropimg(scene_image, scope) + if cmatch(img, res) and self.type != "Boss": + self.status = True return self.type = "未知" self.type_scope = match_scope -nodes: dict[str, Node] = {} -current_layer = 0 -scene_image = None +nodes: dict[str, Node] = {} # 节点编号:节点对象 +next_step: dict[str, int] = {} # 节点编号:剩余刷新次数 +current_layer = 0 # 当前层数 +scene_image = None # 整层的图像 diff --git a/mower/solvers/rogue/detect_node.py b/mower/solvers/rogue/detect_node.py index 380b50bc..27b4e078 100644 --- a/mower/solvers/rogue/detect_node.py +++ b/mower/solvers/rogue/detect_node.py @@ -2,9 +2,10 @@ import cv2 import numpy as np from mower.solvers.rogue import data +from mower.utils import config from mower.utils import typealias as tp -from mower.utils.image import thres2 -from mower.utils.vector import in_scope +from mower.utils.image import cropimg, loadres, thres2 +from mower.utils.vector import in_scope, sa, vs from .utils import template @@ -17,6 +18,7 @@ class NodeDetector: self.detect_nodes() self.detect_x_edges() self.detect_y_edges() + self.check_next_step() def merge_rectangles(self, rects: list): "合并有重叠的矩形" @@ -141,7 +143,24 @@ class NodeDetector: 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: data.nodes[node_id].next_nodes.append(next_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 diff --git a/mower/solvers/rogue/utils.py b/mower/solvers/rogue/utils.py index 7b7c38f0..bbb20022 100644 --- a/mower/solvers/rogue/utils.py +++ b/mower/solvers/rogue/utils.py @@ -5,7 +5,7 @@ import cv2 from mower.utils import config from mower.utils import typealias as tp 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 @@ -21,17 +21,17 @@ def find_text(text: str, size: int, threshold: float = 0.8) -> tp.Coordinate | N 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]: - "在全景图中找模板" + "在img1中模板匹配img2" try: - logger.debug(f"{res=}") - template = loadres(res) - h, w, _ = template.shape + h, w, _ = img2.shape if scope: x, y = scope[0] - img = cropimg(image, scope) - result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED) + img1 = cropimg(img1, scope) + 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) score = max_val p1 = (max_loc[0] + x, max_loc[1] + y) diff --git a/mower/solvers/rogue/whole_scene.py b/mower/solvers/rogue/whole_scene.py index 440aa1b6..d70eed65 100644 --- a/mower/solvers/rogue/whole_scene.py +++ b/mower/solvers/rogue/whole_scene.py @@ -1,5 +1,5 @@ import cv2 -from matplotlib import pyplot as plt +import numpy as np from mower.solvers.rogue import data from mower.utils import config @@ -26,12 +26,51 @@ class WholeScene(BaseSolver): logger.error(f"拼接过程中出现错误: {e}") 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 = [] new = cropimg(config.recog.img, ((0, 150), (1920, 880))) images.append(new) 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))) gray1 = cv2.cvtColor(new, cv2.COLOR_RGB2GRAY) gray2 = cv2.cvtColor(images[-1], cv2.COLOR_RGB2GRAY) @@ -42,10 +81,15 @@ class WholeScene(BaseSolver): logger.info("滑到底了") break logger.info(f"截图读取完了,有{len(images)}张截图") - for img in images: - plt.imshow(img) - plt.show() if len(images) > 1: - data.scene_image = self.stitch_images(images) + data.scene_image = self.horizontal_stitch(images) else: 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() diff --git a/mower/utils/graph/index.py b/mower/utils/graph/index.py index 0435aa91..ab7ae47a 100644 --- a/mower/utils/graph/index.py +++ b/mower/utils/graph/index.py @@ -29,10 +29,14 @@ from .utils import edge @edge(Scene.SIGN_IN_DAILY, Scene.INDEX) @edge(Scene.INDEX_ORIGINITE, Scene.INDEX) @edge(Scene.INDEX_SANITY, Scene.INDEX) -@edge(Scene.ANNOUNCEMENT, Scene.INDEX) @edge(Scene.SIGN_IN_ORUNDUM, Scene.INDEX) 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)