diff --git a/.gitignore b/.gitignore index e39520bc..6ec299e0 100644 --- a/.gitignore +++ b/.gitignore @@ -110,6 +110,7 @@ venv.bak/ # custom logs/ map/ +maps/ map_data/ temp/ tmp/ diff --git a/utils/calculated.py b/utils/calculated.py index ec0ba10c..2cb89de8 100644 --- a/utils/calculated.py +++ b/utils/calculated.py @@ -66,7 +66,7 @@ def __init__(self, title=_("崩坏:星穹铁道"), det_model_name="ch_PP-OCRv3 self.end_list = ["Tab", _("轮盘"), _("唤起鼠标"), _("手机"), _("退出")] - def Click(self, points = None): + def click(self, points = None): """ 说明: 点击坐标 @@ -106,7 +106,7 @@ def appoint_click(self, points, appoint_points, hsv = [18, 18, 18]): log.info(_(_("识别超时"))) break - def Relative_click(self, points, click_time=0.5): + def relative_click(self, points, click_time=0.5): """ 说明: 点击相对坐标 @@ -130,8 +130,8 @@ def img_click(self, points): :param points: 坐标 """ left, top, __, __ = self.window.left, self.window.top, self.window.right, self.window.bottom - x, y = int(left + points[0]), int(top + points[1]) - log.debug((x, y)) + x, y = int(left + points[0] + sra_config_obj.left_border), int(top + points[1] + sra_config_obj.up_border) + log.info((x, y)) self.mouse.position = (x, y) self.mouse.press(mouse.Button.left) time.sleep(0.5) @@ -153,14 +153,14 @@ def ocr_click(self, characters, overtime = 10, frequency = 1, points = (0, 0, 0, __, pos = self.ocr_pos(characters, points) log.debug(characters) if pos: - self.Click(pos) + self.click(pos) time.sleep(0.3) return pos if time.time() - start_time > overtime: log.info(_("识别超时")) if overtime != 0 else None return False - def click_hsv(self, hsv_color, points=(0,0,0,0), offset=(0,0), flag=True, tolerance = 5): + def hsv_click(self, hsv_color, points=(0,0,0,0), offset=(0,0), flag=True, tolerance = 5): """ 说明: 点击指定hsv颜色,允许偏移 @@ -186,7 +186,7 @@ def click_hsv(self, hsv_color, points=(0,0,0,0), offset=(0,0), flag=True, tolera break ret = [x + pos[0] + offset[0] , y + pos[1] + offset[1] ] log.info(_('点击坐标{ret}').format(ret=ret)) - self.Click(ret) + self.click(ret) return True def take_screenshot(self,points=(0,0,0,0)): @@ -298,6 +298,9 @@ def click_target(self, target_path: str, threshold, flag:bool=True, check:bool=F "map_3-2": _("迥星港"), "map_3-3": _("太卜司"), "map_3-4": _("工造司"), + "map_3-5": _("丹鼎司"), + "map_3-6": _("鳞渊境"), + "change_team": _("更换队伍"), } ''' map: @@ -318,11 +321,11 @@ def click_target(self, target_path: str, threshold, flag:bool=True, check:bool=F log.info(_("选择传送锚点")) elif "map" in temp_name: log.info(_("选择地图")) - if "map" not in temp_name: + if "orientation" in temp_name or "transfer" in temp_name: if type(temp_ocr[temp_name]) == dict: result = self.ocr_click(temp_ocr[temp_name]["name"], points=temp_ocr[temp_name]["points"]) elif type(temp_ocr[temp_name]) == tuple: - self.Relative_click(temp_ocr[temp_name]) + self.relative_click(temp_ocr[temp_name]) result = True else: result = self.ocr_click(temp_ocr[temp_name]) @@ -333,7 +336,9 @@ def click_target(self, target_path: str, threshold, flag:bool=True, check:bool=F break if not self.is_blackscreen(): break - elif "point" in temp_name: + elif "change_team" in temp_name: + self.change_team() + elif "point" in temp_name and "map" in temp_name: target = cv.imread(target_path) start_time = time.time() while True: @@ -358,7 +363,7 @@ def click_target(self, target_path: str, threshold, flag:bool=True, check:bool=F # 右边列表太长了 尝试向下滚动5秒 再向上滚动5秒 # scroll内部sleep 0.5s 大概能10次 目前最长在雅利洛需要向下滚动7次 if first_timeout: # 点击右边的地图列表 - self.Relative_click((80, 50)) + self.relative_click((80, 50)) first_timeout = False if time.time() - start_time < 10: self.scroll(-10) @@ -376,26 +381,31 @@ def click_target(self, target_path: str, threshold, flag:bool=True, check:bool=F target = cv.imread(target_path) start_time = time.time() first_timeout = True - distance_iter = itertools.cycle([500, -500, -500]) + distance_iter = itertools.cycle([300, -300, -300]) level_iter = itertools.cycle([(3, 81), (3, 89), (3, 75)]) + move_num = 0 while True: result = self.scan_screenshot(target) log.info(result["max_val"]) if result["max_val"] > threshold: #points = self.calculated(result, target.shape) - self.Click(result["max_loc"]) + self.click(result["max_loc"]) break if time.time() - start_time > 5 and "point" in temp_name: start_x = (self.window.left+self.window.right) // 2 start_y = (self.window.top+self.window.bottom) // 2 import pyautogui # 写这里是为了防止缩放比获取错误 + log.info(move_num%3) + if move_num%3 == 0 and move_num != 0: + self.relative_click(next(level_iter)) + time.sleep(0.2) pyautogui.moveTo(start_x, start_y) pyautogui.mouseDown() pyautogui.moveTo(start_x, start_y+next(distance_iter), duration=1) pyautogui.mouseUp() - self.Relative_click(next(level_iter)) - if ((time.time() - start_time > 10 and "point" not in temp_name) \ - or (time.time() - start_time > 15 and "point" in temp_name)): #防止卡死.重启线程 + move_num+=1 + if ((time.time() - start_time > 15 and "point" not in temp_name) \ + or (time.time() - start_time > 30 and "point" in temp_name)): #防止卡死.重启线程 log.info(_("传送识别超时")) self.keyboard.press(Key.esc) time.sleep(0.1) @@ -407,14 +417,14 @@ def click_target(self, target_path: str, threshold, flag:bool=True, check:bool=F def fighting(self): start_time = time.time() - self.Click() + self.click() time.sleep(0.1) if self.has_red((4, 7, 10, 19)): while True: - result = self.get_pix_rgb(pos=(1422, 59)) + result = self.get_pix_rgb(pos=(1337, 62)) log.debug(f"进入战斗取色: {result}") if self.compare_lists([0, 0, 222], result) and self.compare_lists(result, [0, 0, 255]): - self.Click() + self.click() else: break time.sleep(0.1) @@ -424,13 +434,13 @@ def fighting(self): self.wait_fight_end() return True time.sleep(0.2) - result = self.get_pix_rgb(pos=(1422, 59)) + result = self.get_pix_rgb(pos=(1337, 62)) log.debug(f"进入战斗取色: {result}") if not (self.compare_lists([0, 0, 225], result) and self.compare_lists(result, [0, 0, 255])): self.wait_fight_end() # 无论是否识别到敌人都判断是否结束战斗,反正怪物袭击 return True - def Check_fighting(self): + def check_fighting(self): while True: end_str = str(self.part_ocr((20,95,100,100))) if any(substring in end_str for substring in self.end_list): @@ -455,14 +465,14 @@ def fighting_old(self): log.info(_("识别超时,此处可能漏怪!")) return False if self.scan_screenshot(self.attack,points=(3.75,5.5,11.6,23))["max_val"] > 0.97: #修改检测机制,精度更高 - self.Click() + self.click() time.sleep(0.3) doubt_time = time.time() log.info(_("监控疑问或警告")) while time.time() - doubt_time < 8: if self.scan_screenshot(self.doubt,points=(3.75,5.5,11.6,23))["max_val"] > 0.95 or self.scan_screenshot(self.warn,points=(3.75,5.5,11.6,23))["max_val"] > 0.95: log.info(_("识别到疑问或警告,等待怪物开战或反击")) - self.Click() + self.click() time.sleep(1.5) log.info(_("识别反击")) result = self.scan_screenshot(self.finish,points=(0,95,100,100)) @@ -474,14 +484,14 @@ def fighting_old(self): if result["max_val"] < 0.95: break else: - self.Click() + self.click() time.sleep(0.3) doubt_time = time.time() + 7 log.info(_("监控疑问或警告!")) while time.time() < doubt_time: if self.scan_screenshot(self.doubt,pos=(3.75,5.5,11.6,23))["max_val"] > 0.95 or self.scan_screenshot(self.warn,pos=(3.75,5.5,11.6,23))["max_val"] > 0.95: log.info(_("识别到疑问或警告,等待怪物开战或反击")) - self.Click() + self.click() time.sleep(1.5) log.info(_("识别反击")) break @@ -544,7 +554,7 @@ def wait_fight_end(self, type=0): break time.sleep(1) # 避免长时间ocr - def Mouse_move(self, x): + def mouse_move(self, x): """ 说明: 视角转动 @@ -577,14 +587,14 @@ def move(self, com = ["w","a","s","d","f"], time1=1, map_name=""): loc = self.get_loc(map_name=map_name) log.debug(loc) self.keyboard.press(com) - result = self.get_pix_r(pos=(1712, 958)) - log.debug(result) - if sra_config_obj.sprint and (self.compare_lists(result, [130, 160, 180]) or self.compare_lists([200, 200, 200], result)): - time.sleep(0.05) - log.info("疾跑") - self.mouse.press(mouse.Button.right) - self.mouse.release(mouse.Button.right) start_time = time.perf_counter() + if sra_config_obj.sprint: + result = self.get_pix_r(pos=(1712, 958)) + if (self.compare_lists(result, [130, 160, 180]) or self.compare_lists([200, 200, 200], result)): + time.sleep(0.05) + log.info("疾跑") + self.mouse.press(mouse.Button.right) + self.mouse.release(mouse.Button.right) while time.perf_counter() - start_time < (time1/move_division_excursion+move_excursion): pass self.keyboard.release(com) @@ -868,7 +878,7 @@ def wait_join(self): return endtime ''' endtime = time.time() - start_time - result = self.get_pix_rgb(pos=(1422, 59)) + result = self.get_pix_rgb(pos=(1337, 62)) log.debug(result) if self.compare_lists([0, 0, 222], result): log.info(_("已进入地图")) @@ -937,7 +947,7 @@ def monthly_pass(self): log.info(_("点击月卡")) pos = self.ocr_click(_("今日补给")) time.sleep(0.5) - self.Click(pos) + self.click(pos) def get_loc(self, map_name: str="", map_id: int=None): """ @@ -951,18 +961,35 @@ def get_loc(self, map_name: str="", map_id: int=None): """ if self.DEBUG: map_name2id = { - "基座舱段": 1, - "收容舱段": 2, - "支援舱段": 3 + "鳞渊境-1": 1, + "丹鼎司-1": 2, + "丹鼎司-2": 3, + "丹鼎司-3": 3, + "丹鼎司-4": 3, + "丹鼎司-5": 4, + "丹鼎司-6": 4, } - map_id = map_name2id.get(map_name, 1) if not map_id else map_id + if not map_id and map_name not in map_name2id: + return (0, 0) + map_id = map_name2id[map_name] if not map_id else map_id img = cv.imread(f"./maps/{map_id}.png") template = self.take_screenshot((4,8,10,20))[0] - #__, max_vl, max_loc, length, width = find_best_match(img, template,(100,120,5)) - max_val, max_loc = match_scaled(img, template,2.09) - print(max_val) + __, max_val, max_loc, __, __ = find_best_match(img, template,(100,120,5)) + #max_val, max_loc = match_scaled(img, template,2.09) cv.rectangle(img, max_loc, (max_loc[0] + 100, max_loc[1] + 100), (0, 255, 0), 2) - #show_img(img) - return (max_loc[0] + 100/2, max_loc[1] + 100/2) + show_img(img) + return (max_loc[0] + 63, max_loc[1] + 67) else: return (0, 0) + + def change_team(self): + """ + 说明: + 切换队伍 + """ + if self.ocr_click("队伍", points = (4, 1, 9, 6), overtime=1): + team_list = [(732, 79),(854, 83),(975, 77),(1091, 79),(1214, 81),(1333, 79)] # 队伍坐标 + self.img_click(team_list[sra_config_obj.team_number-1]) + return True + else: + return False diff --git a/utils/commission.py b/utils/commission.py index 9eb6d1ce..af86349d 100644 --- a/utils/commission.py +++ b/utils/commission.py @@ -52,11 +52,11 @@ def run(self): points2 = get_percentile([350,280,350+480,280+600],[1920,1080]) self.calculated.take_screenshot() - result = self.calculated.click_hsv([0,201,212], points=points1, offset=[-20,20], flag=True, tolerance=3) + result = self.calculated.hsv_click([0,201,212], points=points1, offset=[-20,20], flag=True, tolerance=3) if not result: log.info("可能没有任务") return False - result = self.calculated.click_hsv([0,201,212], points=points2, offset=[-20,20], flag=True, tolerance=3) + result = self.calculated.hsv_click([0,201,212], points=points2, offset=[-20,20], flag=True, tolerance=3) if not result: log.info("可能没有任务") return False diff --git a/utils/config.py b/utils/config.py index b1ff9761..d48d47fc 100644 --- a/utils/config.py +++ b/utils/config.py @@ -188,12 +188,15 @@ def get_class_methods(cls): methods.append(name) return methods -def load_config_data(cls): +def load_config_data(cls, __name): """ 加载配置文件 """ - methods= get_class_methods(cls) + #methods = get_class_methods(cls) sradata = read_json_file(CONFIG_FILE_NAME) + if __name in sradata: + setattr(cls, __name, sradata[__name]) + ''' lack_methods = set(methods) - set(sradata.keys()) # 获取缺少的配置 # 如果缺少配置则添加 if lack_methods: @@ -203,13 +206,14 @@ def load_config_data(cls): # 读取配置 for key, value in sradata.items(): setattr(cls, key, value) + ''' class SRADataMeta(type): def __setattr__(cls, __name, __value): type_hints = get_type_hints(cls) # 获取所有类属性的类型信息 __name_type = type_hints.get(__name) - if type(__value) != __name_type and __name_type is not None: - raise TypeError(f"类型错误, 期望类型为{__name_type.__name__}, 实际类型为{type(__value).__name__}") + if __name_type is not None and not isinstance(__value, __name_type): + raise TypeError(f"{__name}类型错误, 期望类型为{__name_type.__name__}, 实际类型为{type(__value).__name__}") modify_json_file(CONFIG_FILE_NAME, __name, __value) super().__setattr__(__name, __value) @@ -269,6 +273,10 @@ class SRAData(metaclass=SRADataMeta): """战斗时间""" fight_data: dict = {} """战斗数据""" + team_number: int = 1 + """切换队伍的队伍编号""" + stop: bool = False + """是否停止""" def __init__(self) -> None: @@ -277,21 +285,21 @@ def __init__(self) -> None: def __setattr__(self, __name: str, __value: Any) -> None: type_hints = get_type_hints(self) # 获取所有类属性的类型信息 __name_type = type_hints.get(__name) - if type(__value) != __name_type: - raise TypeError(f"类型错误, 期望类型为{__name_type.__name__}, 实际类型为{type(__value).__name__}") + if not isinstance(__value, __name_type): + raise TypeError(f"{__name}类型错误, 期望类型为{__name_type.__name__}, 实际类型为{type(__value).__name__}") modify_json_file(CONFIG_FILE_NAME, __name, __value) super().__setattr__(__name, __value) def __getattribute__(self, __name: str) -> Any: - if __name == "__dict__": + if "__" in __name: return super().__getattribute__(__name) if __name in self.__dict__: type_hints = get_type_hints(self) # 获取所有类属性的类型信息 __name_type = type_hints.get(__name) __value = super().__getattribute__(__name) - if type(__value) != __name_type: - raise TypeError(f"类型错误, 期望类型为{__name_type.__name__}, 实际类型为{type(__value).__name__}") - load_config_data(SRAData) + if not isinstance(__value, __name_type): + raise TypeError(f"{__name}类型错误, 期望类型为{__name_type.__name__}, 实际类型为{type(__value).__name__}") + load_config_data(SRAData, __name) return super().__getattribute__(__name) def set_config(self, key, value): diff --git a/utils/cv_tools.py b/utils/cv_tools.py index f7e9f363..eff7881c 100644 --- a/utils/cv_tools.py +++ b/utils/cv_tools.py @@ -1,171 +1,24 @@ ''' -Author: Xe-No +Author: Night-stars-1 Date: 2023-05-17 21:45:43 LastEditors: Night-stars-1 nujj1042633805@gmail.com -LastEditTime: 2023-06-27 17:36:19 +LastEditTime: 2023-07-19 23:22:23 Description: 一些cv工具 -Copyright (c) 2023 by Xe-No, All Rights Reserved. +Copyright (c) 2023 by Night-stars-1, All Rights Reserved. ''' - -from utils.get_angle import get_angle -import time +import time import cv2 as cv import numpy as np -import win32gui, win32api, win32con -import pyautogui - - -def cart_to_polar(xy): - x,y = xy - angle = np.degrees(np.arctan2(y, x)) - r = np.linalg.norm([x, y]) - return angle, r - -def get_vec(pos1,pos2): - x1,y1 =pos1 - x2,y2 =pos2 - return [x2-x1,y2-y1] - -def draw_lines(img, point_list, color=(0, 255, 0), thickness=2): - ''' - 在给定的图像上绘制多条线段。 - - Args: - img: 待绘制的原始图像,可以是 numpy.ndarray 类型。 - point_list: 包含多个点坐标的列表,用来表示需要连接的线段的端点坐标。 - color: 线段颜色,可以是元组类型,例如 (0, 255, 0) 表示绿色,默认为 (0, 255, 0)。 - thickness: 线段宽度,用来控制线段的粗细程度,默认为 2。 - - Returns: - 绘制完成后的图像。 - - Raises: - TypeError: 如果输入参数格式不正确,则会抛出类型错误异常。 - ''' - if not isinstance(img, np.ndarray): - raise TypeError('The input `img` parameter must be a numpy.ndarray type.') - # if not isinstance(color, tuple) or not all(isinstance(c, int) and 0 <= c <= 255 for c in color): - # raise TypeError('The input `color` parameter must be a tuple of integers between 0 and 255.') - if not isinstance(thickness, int) or thickness < 1: - raise TypeError('The input `thickness` parameter must be a positive integer.') - - for i in range(len(point_list)-1): - pt1 = point_list[i] - pt2 = point_list[i+1] - cv.line(img, pt1, pt2, color, thickness) - - return img - -def get_sorted_waypoints(map_hsv, points): - hcolor = {} - hcolor['start'] = 180 / 2 - hcolor['pioneer'] = 60 / 2 - hcolor['hunt'] = 10 / 2 - hcolor['garbage'] = 40 / 2 - - waypoints = [] - for point in points: - waypoint = {} - waypoint['pos'] = point - h, s, v = map_hsv[point[1], point[0]] - waypoint['s'] = s - waypoint['type'] = [k for k, val in hcolor.items() if val == h][0] - waypoints.append(waypoint) - - waypoints = sorted(waypoints, key=lambda x: x['s']) - si, sp = [[i, waypoint] for i, waypoint in enumerate(waypoints) if waypoint['type'] == 'start'][0] - sorted_waypoints = [sp] - sp['index'] = 0 - waypoints.pop(si) - cp = sp - count = 1 - while 1: - - if len(waypoints) == 0: - break - # 根据s,找s==最大值的列表 - max_s = max(waypoints, key=lambda x: x['s'])['s'] - print(max_s) - max_list = [[i, waypoint] for i, waypoint in enumerate(waypoints) if waypoint['s'] == max_s] - - print(max_list) - # 找到最近的点并排序 - ci, cp = min(max_list, key=lambda x: np.linalg.norm(get_vec(x[1]['pos'], cp['pos']))) - # 最大值列表必然位于前面 - # ci = [ i for i, p in enumerate(max_list) if p['pos'] == cp['pos']][0] - cp['index'] = count - waypoints.pop(ci) - sorted_waypoints.append(cp) - count += 1 - return sorted_waypoints - -def find_cluster_points(mask): - retval, labels, stats, centroids = cv.connectedComponentsWithStats(mask, connectivity=8) - # 输出每个聚类的中心坐标 - result = [] - # cv.imwrite('debug/mask.png', mask) - for i in range(1, len(stats)): - x = int(stats[i][0] + stats[i][2] / 2) - y = int(stats[i][1] + stats[i][3] / 2) - result.append([x, y]) - return result -def find_color_points(img, bgr_color, max_sq = 64): - mask = np.sum((img-bgr_color)**2,axis=-1)<= max_sq - mask = np.uint8(mask)*255 - cv.imwrite('debug/mask.png', mask) - return find_cluster_points(mask) - -def find_color_points_inrange(img, lowerb, upperb): - mask = cv.inRange(img, lowerb, upperb) - return find_cluster_points(mask) - -def find_nearest_point(points, target): - """ - 在一个点的列表中找到离目标点最近的点 - :param points: 一个包含多个点的列表,每个点都是二元组 (x, y) - :param target: 目标点,二元组 (x, y) - :return: 离目标点最近的点,二元组 (x, y) - """ - # 将点的列表转换为 NumPy 数组 - points_array = np.array(points) - - # 将目标点转换为 NumPy 数组,并将其扩展为与 points_array 相同的形状 - target_array = np.array(target) - target_array = np.expand_dims(target_array, axis=0) - target_array = np.repeat(target_array, points_array.shape[0], axis=0) - - # 计算每个点与目标点之间的距离 - distances = np.linalg.norm(points_array - target_array, axis=1) - - # 找到距离最小的点的索引 - nearest_index = np.argmin(distances) - - # 返回距离最小的点 - return nearest_index, points[nearest_index] - - -# def find_color_points(img, color): -# ys, xs = np.where(np.all(img == color, axis=-1))[:] -# points = np.array([xs,ys]).T -# return points -def dilate_img(img, iterations = 3): - kernel = np.ones((3, 3), np.uint8) - dilation = cv.dilate(img, kernel, iterations=iterations) - return dilation - -def get_binary(img, threshold=200): - gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) - _, binary = cv.threshold(gray, threshold, 255, cv.THRESH_BINARY) - return binary +from .log import log def show_img(img, scale=1, title='Image'): # cv.namedWindow('image', cv.WINDOW_NORMAL) h, w = img.shape[:2] img = cv.resize( img ,(int(w*scale), int(h*scale)) ) cv.imshow(title, img) - cv.waitKey(0) # 显示图像并等待1秒 + cv.waitKey(1000) # 显示图像并等待1秒 cv.destroyAllWindows() def show_imgs(imgs, title='Image'): @@ -173,142 +26,6 @@ def show_imgs(imgs, title='Image'): cv.waitKey(0) cv.destroyAllWindows() - - -def show_imgs(imgs, scale=1, title='Image'): - img = np.hstack(imgs) - show_img(img, scale, title) - -def get_loc(im, imt): - result = cv.matchTemplate(im, imt, cv.TM_CCORR_NORMED) - return cv.minMaxLoc(result) - -def take_screenshot(rect): - # 返回RGB图像 - hwnd = win32gui.FindWindow("UnityWndClass", "崩坏:星穹铁道") - left, top, right, bottom = win32gui.GetWindowRect(hwnd) - rect[0] += left - rect[1] += top - temp = pyautogui.screenshot(region=rect) - screenshot = np.array(temp) - screenshot = cv.cvtColor(screenshot, cv.COLOR_BGR2RGB) - return screenshot - -def take_minimap(rect = [47,58,187,187]): - return take_screenshot(rect) - -def take_fine_screenshot(rect = [47,58,187,187], n = 5, dt=0.01, dy=200): - total = take_screenshot(rect) - n = 5 - for i in range(n): - win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, 0, -dy, 0, 0) - mask = cv.compare(total, take_screenshot(rect), cv.CMP_EQ ) - total = cv.bitwise_and(total, mask ) - time.sleep(dt) - time.sleep(0.1) - for i in range(n): - win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, 0, dy, 0, 0) - mask = cv.compare(total, take_screenshot(rect), cv.CMP_EQ ) - total = cv.bitwise_and(total, mask ) - time.sleep(dt) - minimap = cv.bitwise_and(total, mask ) - return minimap - -def get_mask(img, color_range): - lower, upper = color_range - return cv.inRange(img, lower, upper) - -def get_mask_mk2(img_r): - img_hsv = cv.cvtColor(img_r, cv.COLOR_BGR2HSV) - h, s, v = cv.split(img_hsv) - # 筛选白色 H S<10 V 60~90% - mask1 = (s <25)*(v>255*0.6)*(v<255*0.9) - # 筛选蓝色摄像头扫过的白色 - mask2 = (95 250) & (b>80) - return mask.astype(np.uint8)*255 - -def get_camera_fan(color = [130, 130, 60],angle=0, w=187, h=187, delta=90, dimen =3, radius= 90): - center = (w//2, h//2) - # radius = min(h, w)//2 - fan = np.zeros((h, w, dimen), np.uint8) - # 计算圆心位置 - cx, cy = w // 2, h // 2 - axes = (w // 2, h // 2) - - startAngle, endAngle = angle -45, angle +45 # 画90度 - - cv.ellipse(fan, (cx, cy), axes, 0, startAngle, endAngle, color , -1) - return fan - -def get_gradient_mask(w,h): - center = [w // 2, h // 2] - radius = 0.8 *w - # 创建渐变掩码 - gradient_mask = np.zeros((w, h), dtype=np.uint8) - for r in range(gradient_mask.shape[0]): - for c in range(gradient_mask.shape[1]): - dist = np.sqrt((r-center[1])**2 + (c-center[0])**2) - value = max(0, min(1 - 2*dist/radius, 1)) - gradient_mask[r,c] = int(value * 255) - return gradient_mask - - -def filter_contours_surround_point(gray, point): - """过滤掉不包围指定点的轮廓""" - contours, hierarchy = cv.findContours(gray, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) - # 过滤掉所有不包含指定点的轮廓 - filtered_contours = [] - for i in range(len(contours)): - if cv.pointPolygonTest(contours[i], point, False) < 0: - filtered_contours.append(contours[i]) - - # 过滤掉所有不包围指定点的轮廓 - surrounded_contours = [] - for i in range(len(filtered_contours)): - rect = cv.boundingRect(filtered_contours[i]) - if rect[0] <= point[0] <= rect[0] + rect[2] and \ - rect[1] <= point[1] <= rect[1] + rect[3]: - surrounded_contours.append(filtered_contours[i]) - - return surrounded_contours - - -def sift_match(img1, img2): - # 创建SIFT对象 - sift = cv.SIFT_create() - - # 检测关键点和描述符 - kp1, des1 = sift.detectAndCompute(img1, None) - kp2, des2 = sift.detectAndCompute(img2, None) - - - # 建立FLANN匹配对象 - FLANN_INDEX_KDTREE = 0 - index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) - search_params = dict(checks=50) - flann = cv.FlannBasedMatcher(index_params, search_params) - - # 根据描述符进行匹配 - matches = flann.knnMatch(des1, des2, k=2) - - # 筛选最优匹配 - good_matches = [] - for m, n in matches: - if m.distance < 0.9 * n.distance: - good_matches.append(m) - - # 绘制匹配结果 - img_match = cv.drawMatches(img1, kp1, img2, kp2, good_matches, None, flags=2) - - return img_match - def match_scaled(img, template, scale, mask=False): # 返回最大相似度,中心点x、y t0 = time.time() @@ -362,4 +79,4 @@ def find_best_match(img, template, scale_range=(140, 170, 1)): width = int(width) max_corr = max_val - return scale_percent, max_corr, max_loc, length, width \ No newline at end of file + return scale_percent, max_corr, max_loc, length, width diff --git a/utils/cv_tracker.py b/utils/cv_tracker.py deleted file mode 100644 index ffc3631c..00000000 --- a/utils/cv_tracker.py +++ /dev/null @@ -1,703 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding:utf-8 -*- -# Created by Xe-No at 2023/5/17 -# 利用cv识别小地图并返回地图索引与坐标 -import cv2 as cv -import time -import sys, os, glob -import utils.cv_tools as ct -# from utils.switch_window import switch_window as sw -from utils.calculated import calculated -from utils.get_angle import * -from utils.route_helper import * -from utils.log import log -from .config import get_file, read_json_file, modify_json_file, read_maps, insert_key, CONFIG_FILE_NAME, _ - -# from ray_casting import ray_casting -# import log -import win32api -import win32con -import pyautogui - - -# from PIL import Image - - -def match_scaled(img, template, scale, mask=False): - # 返回最大相似度,中心点x、y - t0 = time.time() - # finish = cv.imread(".imgs/finish_fighting.jpg") - # while True: - # result = calculated.scan_screenshot(finish,pos=(0,95,100,100)) - # if result["max_val"] > 0.98: - # print("未进入战斗") - # break - - resized_template = cv.resize(template, (0,0), fx=scale, fy=scale) - if mask ==False: - res = cv.matchTemplate(img, resized_template, cv.TM_CCORR_NORMED) - else: - res = cv.matchTemplate(img, resized_template, cv.TM_CCORR_NORMED, mask=resized_template) - min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res) - h, w = resized_template.shape[:2] - x, y = max_loc[0] + w/2, max_loc[1] + h/2 - return [max_val, int(x), int(y)] - -def find_best_match(img, template, scale_range=(1.4, 2.0, 0.05), mask=False): - best_match = None - max_corr = -1 - print(img.shape) - - for scale in np.arange(scale_range[0], scale_range[1],scale_range[2]): - if mask==False: - [max_val, x, y] = match_scaled(img, template, scale) - else: - [max_val, x, y] = match_scaled(img, template, scale, mask=True) - print(f'正在匹配 {scale},相似度{max_val},坐标{[x,y]}') - if max_val > max_corr: - max_corr = max_val - max_ret = [x, y, scale, max_val] - return max_ret - - -class Tracker(): - """docstring for Tracker""" - def __init__(self): - self.cc = calculated() - - self.map_prefix = 'datas/map_raw/' - self.masked_map_prefix = 'datas/map_masked/' - self.template_prefix = 'datas/map_template_sample/' - self.result_prefix = 'datas/map_result/' - - # self.minimap_rect = [47,58,187,187] # 全尺度,只有圆形部分匹配度较差 - self.minimap_rect = (4,8,10,19) # (77,88,127,127) - self.full_minimap_rect = (47,58,187,187) - self.arrow_rect = (6.09375,11.8518,8.53375,16.2028)# (117,128,47,47) - # 节省时间,只有需要时才调用load_all_masked_maps - self.masked_maps = None - self.bgr_minimap_enemy = [48,48,233] #红 - self.bgr_map_maxway = [0,255,255]# 黄 - # self.bgr_map_minway = [180,254,254]# 浅黄 - self.bgr_map_start = [255,255,0] # 青 - self.bgr_map_hunt = [0,42,255] #红 - - - self.data = read_json_file(CONFIG_FILE_NAME) - self.DEBUG = self.data.get("debug", False) - - - - - def load_all_images(self, prefix, flag=cv.IMREAD_UNCHANGED): - images = {} - for file in glob.glob(f'{prefix}*.png'): - index = os.path.basename(file) - images[index] = cv.imread(file, flag) - return images - - def load_all_gray_images(self, prefix): - images = {} - for file in glob.glob(f'{prefix}*.png'): - index = os.path.basename(file) - images[index] = cv.imread(file, cv.IMREAD_GRAYSCALE) - return images - - def load_map(self, index, prefix): - return cv.imread(prefix+index) - - def load_all_masked_maps(self): - images = {} - for file in glob.glob(f'{self.masked_map_prefix}*.png'): - index = os.path.basename(file) - images[index] = cv.cvtColor( cv.imread(file), cv.COLOR_BGR2GRAY) - self.masked_maps = images - return images - - def save_all_masked_maps(self): - maps = self.load_all_images(self.map_prefix) - # # 路面掩膜 - - # show_img(b, 0.25) - - # map_b = get_mask(map_hsv,np.array([[0,0,30],[360,10,90]])) - # map_b = cv.medianBlur(map_b, 5) - # 路沿掩膜 - masked_maps = {} - for index, map_r in maps.items(): - b, g, r, a = cv.split(map_r) - mask = a>200 - b = b * mask - map_b = cv.threshold(b, 20, 150, cv.THRESH_BINARY)[1] - # map_hsv = cv.cvtColor(map_r, cv.COLOR_BGR2HSV) - # map_s = get_mask(map_hsv,np.array([[0,0,180],[360,10,255]])) - # map_s = cv.medianBlur(map_s, 3) - masked_maps[index] = map_b - cv.imwrite(self.masked_map_prefix + index, map_b) - # 保存之后也返回 - return masked_maps - - def get_img(self, prefix=None, img_path=False ): - if img_path: - img_r = cv.imread(prefix + img_path) - else: - img_r, *_ = self.cc.take_screenshot(self.minimap_rect) - return img_r - - def get_minimap_mask(self, mini_r, color_range = np.array([[0,0,180],[360,10,255]]) ): - # img_hsv = cv.cvtColor(img_r, cv.COLOR_BGR2HSV) - # img_s = get_mask(img_hsv, color_range) - mini_r = cv.cvtColor(mini_r, cv.COLOR_BGR2GRAY) - mini_b = cv.threshold(mini_r, 20, 150, cv.THRESH_BINARY)[1] - kernel = np.ones((5, 5), np.uint8) - mini_b = cv.dilate(mini_b, kernel, iterations=1) - - return mini_b - - def get_coord_by_map(self, map_b, img_r, scale=2.09): - # 固定地图,识别坐标 map_index 图片索引 img 彩色图 - img_s =self.get_minimap_mask(img_r) - img_s = cv.resize(img_s, (0,0), fx = scale, fy = scale) # 小地图到大地图的缩放尺度 - w,h = img_s.shape[:2] - min_val, max_val, min_loc, max_loc = ct.get_loc(map_b, img_s) - - print(max_val) - cx = max_loc[0] + w//2 - cy = max_loc[1] + h//2 - pos = [cx,cy] - return [cx, cy, max_val] - - def get_coord_by_map2(self, map_bgra, img_r, scale_range=(1.66,2.09,0.05), rect=[0,0,0,0]): - # 固定地图,识别坐标 map_index 图片索引 img 彩色图 - left, top, width, height = rect - - img_s = ct.get_mask_mk2(img_r) - - map_b = ct.get_mask_mk3(map_bgra) - if rect != [0,0,0,0]: - map_b = map_b[top:top+height,left:left+width] - - cv.imwrite('img.png', img_s) - cv.imwrite('map.png', map_b) - [cx, cy, max_val, scale] = find_best_match(map_b, img_s, scale_range=scale_range) - x = left + cx - y = top + cy - - return [int(x), int(y), max_val, scale] - - def get_front_angle(self): - main_angle = get_angle() - mini_r = ct.take_fine_screenshot(self.minimap_rect, n=1, dy=30) - mini_r = cv.cvtColor(mini_r, cv.COLOR_BGR2GRAY) - mini_b = cv.threshold(mini_r, 20, 150, cv.THRESH_BINARY)[1] - # 扇形组成简易神经网络 - h,w = mini_r.shape[:2] - fans = {} - fans['f'] = ct.get_camera_fan(color = 255, angle=main_angle, w=w, h=h, delta=30, dimen=1, radius=60) - fans['l'] = ct.get_camera_fan(color = 255, angle=main_angle-60, w=w, h=h, delta=90, dimen=1, radius=60) - fans['r'] = ct.get_camera_fan(color = 255, angle=main_angle+60, w=w, h=h, delta=90, dimen=1, radius=60) - # fans['b'] = get_camera_fan(color = 255, angle=main_angle-180, w=w, h=h, delta=90, dimen=1, radius=60) - - - lx = np.linspace(-1, 1, w) - ly = np.linspace(-1, 1, h) - xx, yy= np.meshgrid(lx,ly) - rr = xx*xx++yy*yy - count = {} - for key, fan in fans.items(): - # cx = np.mean(xx * fan) - # cy = np.mean(yy * fan) - count[key] = np.sum(mini_b * fan * rr)/255 - - print(count) - - if count['f'] > 200: - angle = 0 - else: - if count['r'] > count['l']: - angle =90 - else: - angle =-90 - - return angle - - def move_octo(self, r0, r1, map_bgra, v=12): - # 八方向移动 - print(f'开始从{r0}移动到{r1}') - x0, y0 = r0 - x1, y1 = r1 - dx, dy = x1-x0, y1-y0 - - angle = np.degrees(np.arctan2(dy, dx)) - r = np.linalg.norm([dx,dy]) - - direction = int((angle +22.5)//45) % 8 # 0 东 1 东南 ...顺时针类推 - operator = ['d','ds','s','sa','a','aw','w','wd'] - for key in operator[direction]: - pyautogui.keyDown(key) - if r < 30: - time.sleep(r/v) - print(f'初始距离只有{r}像素,直接走') - else: - time.sleep(1) - while 1: - img_r, *_ = self.cc.take_screenshot(self.minimap_rect) - [x,y,max_corr] = self.get_coord_by_map2( map_bgra, img_r, scale=2.09) - x, y = int(x/2), int(y/2) - dr = np.linalg.norm([x1-x,y1-y]) - print(f'目前位于{[x,y]},距离目标点还有{dr}像素') - if dr > 30: - time.sleep(0.5) - else: - time.sleep(dr/v) - break - - for key in operator[direction]: - pyautogui.keyUp(key) - - time.sleep(0.1) - - - - def move_to(self, pos0, pos1, map_bgra, v=24, blind_mode=0): - ix, iy = pos0 - tx, ty = pos1 - img_r, *_ = self.cc.take_screenshot(self.minimap_rect) - - img_s = ct.get_mask_mk2(img_r) - map_b = ct.get_mask_mk3(map_bgra) - - dx01, dy01 = tx-ix,ty-iy - rit = np.linalg.norm([dx01, dy01]) - dex01, dey01 = [dx01, dy01]/rit # 起点目标单位向量 - theta01 = np.arctan2(dy01,dx01) - angle01 = np.rad2deg(theta01) - self.turn_to(angle01,moving=1) - - t0 = time.time() - i=0 - - - - walking = 0 - while 1: - t1 = time.time() - dt = t1-t0 - - lock_angle = 0 # 是否锁定角度,在人物朝向角度小于某个阈值时,锁定 - - if i ==0: - x,y = ix,iy - if dt >1: - # if len(self.find_minimap_enemies()): - # print("开启寻猎") - # self.hunt() - # self.passive_hunt() - - img_r, *_ = self.cc.take_screenshot(self.minimap_rect) - img_s = ct.get_mask_mk2(img_r) - print(f'识别前位置{[x,y]}') - - - print(map_b[y-200:y+200,x-200:x+200].shape) - - # [max_corr, x,y] = match_scaled(map_b, img_s, scale = 2.09) - # 相对匹配 - t2 =time.time() - [max_corr, dx,dy] = match_scaled(map_b[y-200:y+200,x-200:x+200], img_s, scale = 2.09) #[y-200:y+200,x-200:x+200] - t3 =time.time() - print(f'图片匹配时间{t3-t2}') - dx-=200 - dy-=200 - x+=dx - y+=dy - dx,dy = tx-x, ty-y - theta = np.arctan2(dy,dx) - angle = np.rad2deg(theta) # 人物到目标的方位角 - direc = self.get_now_direc() # 人物朝向的方位角 - print(f'angle = {angle}, direc = {direc}') - if abs(angle - direc) < 5: - lock_angle = 1 # 已经对准,直接锁定角度 - - r = np.linalg.norm([dx,dy]) - print(f'第{i}次定位,移动时间{dt},将从{[x,y]}移动到{[tx,ty]},相对位移{[dx,dy]},匹配度{max_corr}') - if i == 0 or max_corr < 0.3: - pyautogui.keyUp("w") - self.cc.Check_fighting() - time.sleep(0.2) - pyautogui.keyDown("w") - - rc0 = np.linalg.norm([x-ix,y-iy]) - # 将dx,dy做旋转变换,dx_rot为 [当前to目标]在[初始to目标]方向上的投影,小于零说明走过头了,dy_rot则为最小距离,很大说明偏离主轴 - dx_rot = dex01 * dx + dey01 * dy - dy_rot = dey01 * dx - dex01 * dy - print(f'人物坐标系下,目标相对位置为{[dx_rot, dy_rot]}') - - # if dy_rot <5 and dx_rot > 0: - if not lock_angle: - t2 =time.time() - self.turn_to(angle,moving=1) - t3 =time.time() - print(f'转动时间{t3-t2}') - else: - # 锁定角度后,通过左右移动微调 - if dy_rot > 5: - pyautogui.press('a') - elif dy_rot < -5: - pyautogui.press('d') - elif blind_mode: - # 盲目模式下,锁定角度之后不再进行任何反馈 - rest_r = max(0,r-10) - print(f'只剩路程{r},只走{rest_r}像素,等待{rest_r/v}秒') - time.sleep(r/v) - # time.sleep(0.1) - - - # time.sleep(0.01) - # 提前停止转向,避免过度转向 - if dx_rot < 25: - rest_r = max(0,r-2) - print(f'只剩路程{r},只走{rest_r}像素,等待{rest_r/v}秒') - # time.sleep(r/v) - break - i+=1 - - - - def find_minimap_enemies(self): - minimap = ct.take_screenshot([77,88,127,127]) # 77,88,127,127 # 110,110,80,80 - hsv = cv.cvtColor(minimap, cv.COLOR_BGR2HSV) - h,s,v = cv.split(hsv) - mask = ((h < 6 ) | ( h > 175 )) * ( s>0.2*255) * (v>0.7*255) - # mask = (s>0.2*255) * (h>175) - mask = np.uint8(mask) *255 - # print(mask) - - enemies = ct.find_cluster_points(mask) - # enemies = ct.find_color_points(minimap, self.bgr_minimap_enemy, max_sq = 12000) - - print(enemies) - return enemies - - - def find_tagz(self): - above_img = ct.take_screenshot([800,0,400,200]) - tagz = cv.imread('temp/pc/tagz.jpg') - - res = cv.matchTemplate(above_img, tagz, cv.TM_CCORR_NORMED) - min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res) - if max_val >0.95: - return 1 - else: - return 0 - - - - def hunt(self): - # 巡猎模式,小地图上有敌人时触发 - # 触发后,寻找最近的敌人并攻击 - # 小地图上没有敌人后解除 - # 默认处于前进状态 - cx,cy = self.minimap_rect[2] / 2, self.minimap_rect[3] / 2 - - start_time = time.time() - - enemies = self.find_minimap_enemies() - if len(enemies) >0: - print("找到敌人") - _, ne = ct.find_nearest_point(enemies, [cx,cy]) - else: - return '没有找到敌人' - - - dx,dy = ne[0] - cx, ne[1] - cy - angle, r = ct.cart_to_polar([dx,dy]) - # if r < 10: - # if self.find_tagz(): - # 离得很近却没有Z,说明隔着墙或者其他异常,直接退出 - # return '异常' - - pyautogui.click() - pyautogui.keyUp("w") - self.cc.fighting() - pyautogui.keyDown("w") - - # return '正常' - - - # self.turn_to(angle, moving=1) - - def passive_hunt(self): - # 被动巡猎模式,有z标识时触发 - if self.find_tagz(): - self.cc.fighting() - - - - - def move_to_st(self, pos, map_bgra): - - tx, ty = pos - i = 0 - while 1: - img_r, *_ = self.cc.take_screenshot(self.minimap_rect) - - rect = [x-200,y-200,400,400] - [x,y,max_corr] = self.get_coord_by_map2( map_bgra, img_r, scale=2.09, rect= rect) - dx,dy = tx-x, ty-y - i+=1 - - theta = np.arctan2(dy,dx) - angle = np.rad2deg(theta) - r = np.linalg.norm([dx,dy]) - x_on = 0 - y_on = 0 - tolerance = 20 - - if x_on == 0: - if dx > tolerance: - pyautogui.keyDown('d') - x_on = 'd' - if dx < -tolerance: - pyautogui.keyDown('a') - x_on = 'a' - elif x_on == 'd': - if dx < tolerance: - pyautogui.keyUp('d') - x_on = 0 - elif x_on == 'a': - if dx > -tolerance: - pyautogui.keyUp('a') - x_on = 0 - - if y_on == 0: - if dy > tolerance: - pyautogui.keyDown('s') - y_on = 's' - if dy < -tolerance: - pyautogui.keyDown('w') - y_on = 'w' - elif y_on == 's': - if dy < tolerance: - pyautogui.keyUp('s') - y_on = 0 - elif y_on == 'w': - if dy > -tolerance: - pyautogui.keyUp('w') - y_on = 0 - print(f'将从{[x,y]}移动到{[tx,ty]},相对位移{[dx,dy]},x轴控制{x_on},y轴控制{y_on}') - - if x_on== 0 and y_on==0: - break - time.sleep(0.5) - - # 计算旋转变换矩阵 - def handle_rotate_val(self, x, y, rotate): - cos_val = np.cos(np.deg2rad(rotate)) - sin_val = np.sin(np.deg2rad(rotate)) - return np.float32( - [ - [cos_val, sin_val, x * (1 - cos_val) - y * sin_val], - [-sin_val, cos_val, x * sin_val + y * (1 - cos_val)], - ] - ) - - # 图像旋转(以任意点为中心旋转) - def image_rotate(self, src, rotate=0): - h, w, c = src.shape - M = self.handle_rotate_val(w // 2, h // 2, rotate) - img = cv.warpAffine(src, M, (w, h)) - return img - - def get_now_direc(self): - blue = np.array([234, 191, 4]) - arrow = cv.imread("temp\pc\loc_arrow.jpg") - loc_tp, *_ = self.cc.take_screenshot(self.arrow_rect) - loc_tp[np.sum(np.abs(loc_tp - blue), axis=-1) <= 50] = blue - loc_tp[np.sum(np.abs(loc_tp - blue), axis=-1) > 0] = [0, 0, 0] - mx_acc = 0 - ang = 0 - for i in range(0,360,30): - rt = self.image_rotate(arrow, -i) # 顺时坐标系 - result = cv.matchTemplate(loc_tp, rt, cv.TM_CCORR_NORMED) - min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result) - if max_val > mx_acc: - mx_acc = max_val - mx_loc = (max_loc[0] + 12, max_loc[1] + 12) - ang = i - - for i in range(-30,30,6): - i = (ang+i)%360 - rt = self.image_rotate(arrow, -i) # 顺时坐标系 - result = cv.matchTemplate(loc_tp, rt, cv.TM_CCORR_NORMED) - min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result) - if max_val > mx_acc: - mx_acc = max_val - mx_loc = (max_loc[0] + 12, max_loc[1] + 12) - ang = i - - for i in range(-6,6,1): - i = (ang+i)%360 - rt = self.image_rotate(arrow, -i) # 顺时坐标系 - result = cv.matchTemplate(loc_tp, rt, cv.TM_CCORR_NORMED) - min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result) - if max_val > mx_acc: - mx_acc = max_val - mx_loc = (max_loc[0] + 12, max_loc[1] + 12) - ang = i - ang = (ang - 90)%360 - - return ang - - def turn_by(self, x, speed_factor=None): - if x > 30: - y = 30 - elif x < -30: - y = -30 - else: - y = x - dx = int(9800 * y * 1295 / 1920 / 180 * 0.8701432110701552) # 需要校准 - - win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, dx, 0) # 进行视角移动 - time.sleep(0.05) - if x != y: - self.turn_by(x - y) - - - def turn_to(self, target_angle,speed_factor=1, n=1, moving=0): - print(f'目标{target_angle}度') - # 非移动状态, - # if abs(target_angle) >= 90 and abs(target_angle) <= 150: - # log.info(target_angle) - # pyautogui.keyUp('w') - # moving = 0 - - if moving == 0: - pyautogui.press('w') - time.sleep(0.2) - # n为校准次数,至少为1 - for _ in range(n): - current_angle = self.get_now_direc() - turn_angle = target_angle - current_angle - turn_angle -= round(turn_angle/360)*360 - print(f'要转{turn_angle}度') - self.turn_by(turn_angle,speed_factor) - - - - def turn_to_precise(self, target_angle, tolerance=4.0, moving=0): - print(f'目标{target_angle}度') - while 1: - if moving == 0: - pyautogui.press('w') - time.sleep(0.2) - current_angle = self.get_now_direc() - turn_angle = target_angle - current_angle - turn_angle -= round(turn_angle/360)*360 - print(f'{turn_angle} {current_angle}') - if abs(turn_angle) < tolerance: - break - - self.turn_by(turn_angle) - - - - - def find_map(self, img_r, scale=1.66): - if self.masked_maps == None: - self.load_all_masked_maps() - max_index = -1 - max_corr = -1 - max_ret = None - for index, map_b in self.masked_maps.items(): - # img_hsv = cv.cvtColor(img_r, cv.COLOR_BGR2HSV) - # img_s = get_mask(img_hsv,np.array([[0,0,160],[360,10,255]])) - - img_s = self.get_minimap_mask(img_r) - # show_img(img_s) - print(f'正在匹配{index}的缩放尺度') - # [scale, corr, loc] = find_best_match(map_b, img_s, (100,200,5)) - # [cx, cy, corr] = self.get_coord_by_map(self.masked_maps[index],img_r) - [corr, loc] = match_scaled(map_b, img_s, scale) - print(f'正在找{index},相似度{corr}') - # if corr < 0.5: - # continue - if corr > max_corr: - max_corr = corr - max_ret = [index, corr, loc, scale] - - [index, corr, loc, scale] = max_ret - [hh,hw] = img_r.shape[:2] # 半高半宽 - hh = int(hh * scale //2) - hw = int(hw * scale //2) - x = loc[0] + hw - y = loc[1] + hh - print(f'地图{index}的相似度为{corr},当前坐标为{[x,y]}') - return [index, [x,y], [hw,hh] ,corr] - - def get_scale(self, map_b, mini_b ): - - # 获取小地图比例尺,一次获取,终生受益 - - [scale, max_val, max_loc] = find_best_match(map_b, mini_b) - # show_img(ret) - [h, w] = mini_b.shape[:2] - hf = int(h * scale) - wf = int(w * scale) - - cv.rectangle(map_b, max_loc, np.add(max_loc, [wf,hf]), 255, 5 ) - ct.show_img(map_b, 0.25) - - return scale - - def run_route(self, map_index, path= 'maps/'): - map_bgra = cv.imread(f'{path}{map_index}', cv.IMREAD_UNCHANGED) - map_bgr = cv.imread(f'{path}{map_index}') - - start_point = ct.find_color_points(map_bgr, self.bgr_map_start)[0] - - map_hsv = cv.cvtColor(map_bgr, cv.COLOR_BGR2HSV) - h,s,v = cv.split(map_hsv) - mask = ((h < 6 ) | ( h > 175 )) * ( s>0.2*255) * (v>0.7*255) - mask = np.uint8(mask) *255 - hunt_point = ct.find_cluster_points(mask) - waypoints = ct.find_color_points_inrange(map_bgr, (0, 255, 255), (128, 255, 255)) - - log.info(f'起点{start_point}') - log.info(f'路径点{waypoints}') - log.info(f'寻猎点{hunt_point}') - - - - all_points = hunt_point + waypoints + [start_point] - - sorted_points = ct.get_sorted_waypoints(map_hsv, all_points) - log.info(sorted_points) - sorted_points = [point['pos'] for point in sorted_points] - - - log.info(sorted_points) - - for i in range(1, len(sorted_points)): - x0, y0 = sorted_points[i-1] - x1, y1 = sorted_points[i] - if i == 1: - angle0, _ = ct.cart_to_polar([x1-x0,y1-y0]) - print(f'路线刚开始,先转向至{angle0}') - pyautogui.press('w') - time.sleep(0.5) - self.turn_to(angle0) - pyautogui.press('w') - time.sleep(1) - - pyautogui.keyDown('w') - self.move_to([x0, y0], [x1, y1], map_bgra) - if sorted_points[i] in hunt_point: - print("开启寻猎") - # TODO:战斗检测仍需要修改 - self.hunt() - time.sleep(0.1) - - # 最后检查一遍路线终点有无怪物 - self.hunt() - pyautogui.keyUp('w') diff --git a/utils/get_angle.py b/utils/get_angle.py deleted file mode 100644 index 3953f640..00000000 --- a/utils/get_angle.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding:utf-8 -*- -# Created by Xe-No at 2023/5/15 -# 根据小地图箭头判定人物朝向,若想让摄像头方向与人物朝向一致,先按一次w即可。 -# 获取角度可以为后续脚本开发提供帮助,比如自动矫正角度,模拟宇宙寻路等 - -import cv2 as cv -import numpy as np -import pyautogui -import time -import matplotlib.pyplot as plt - -# 计算旋转变换矩阵 -def handle_rotate_val(x, y, rotate): - cos_val = np.cos(np.deg2rad(rotate)) - sin_val = np.sin(np.deg2rad(rotate)) - return np.float32( - [ - [cos_val, sin_val, x * (1 - cos_val) - y * sin_val], - [-sin_val, cos_val, x * sin_val + y * (1 - cos_val)], - ] - ) - -# 图像旋转(以任意点为中心旋转) -def image_rotate(src, rotate=0): - h, w, c = src.shape - M = handle_rotate_val(w // 2, h // 2, rotate) - img = cv.warpAffine(src, M, (w, h)) - return img - -def get_polar_stats(gray): - # 将坐标系从图像中心转换到图像左上角 - h, w = gray.shape - h = np.float64(h) - w = np.float64(w) - x, y = np.meshgrid(np.arange(w), np.arange(h)) - x -= w / 2. - y -= h / 2. - - # 将坐标从笛卡尔坐标系转换为极坐标系 - r, theta = cv.cartToPolar(x, y) - - angle_step = np.pi / 36 - angle_bins = np.arange(-np.pi, np.pi + angle_step, angle_step) - print(len(theta[gray==255])) - - hist, _ = np.histogram(theta[gray==255], bins=angle_bins) - # 统计不同角度的像素值 255 的像素数量 - ax = plt.subplot(121,projection='polar') - ax.plot(angle_bins[1:], hist, linewidth=2.0) - plt.show() - return hist - -def get_orb(img): - orb = cv.ORB_create() - # 检测关键点并计算描述子 - keypoints, descriptors = orb.detectAndCompute(img, None) - img_with_keypoints = cv.drawKeypoints(img, keypoints, None) - - # 显示结果 - # cv.imshow('Original', img) - cv.imshow('Keypoints', np.hstack((img, img_with_keypoints))) - cv.waitKey(0) - cv.destroyAllWindows() - -def get_furthest_point(points): - # 计算中心点坐标 - center = np.mean(points, axis=0) - # 初始化最大距离为 0,最远点为第一个点 - max_distance = 0 - furthest_point = points[0] - # 枚举每个点 - for point in points: - # 计算该点到中心点的距离 - distance = np.linalg.norm(point - center) - # 如果该点到中心点的距离大于当前最大距离,则更新最大距离和最远点 - if distance > max_distance: - max_distance = distance - furthest_point = point - return furthest_point - -def get_mask(img, color_range): - lower, upper = color_range - return cv.inRange(img, lower, upper) - -def get_img(xy,wh, debug=False, sample_image=False): - x,y = xy - w,h = wh - if sample_image: - # 任意一张截图即可,最重要的是保留左上部分 - img = cv.imread(sample_image) - else: - img = np.array(pyautogui.screenshot()) - img = cv.cvtColor(img, cv.COLOR_RGB2BGR) # pyautogui截取的图为RGB - return img[y:y+h, x:x+w] - -def get_angle(debug=False, sample_image=False): - - # x,y = [47,58] - # w,h = [187,187] - x,y = [117,128] - w,h = [47,47] - - - img = get_img([x,y], [w,h],debug,sample_image) - img = img - img0 = img.copy() - - hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) - - # 定义青色掩膜,使用较高饱和度下界,过滤掉摄像头圆弧 - cyan_mask = get_mask(hsv, color_range = np.array([ - [90, 200, 200], - [100, 255, 255]])) - # pyautogui.press('w') - # time.sleep(1) - - - # 查找轮廓 - contours, hierarchy = cv.findContours(cyan_mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) - - # 暴力断定只有一个青色箭头,获取箭头顶点 - if len(contours) != 1: - return False - contour = contours[0] - peri = cv.arcLength(contour, True) - approx = cv.approxPolyDP(contour, 0.03 * peri, True) - fp = get_furthest_point(approx[:,0,:]) - - - # 获取角度 - fx, fy = fp - dx = fx - w // 2.0 - dy = fy - h // 2.0 - angle = np.degrees(np.arctan2(dy, dx)) - angle = np.around(angle, 2) - - # print(angle) - if debug: - # 画出原图、二值掩膜图以及轮廓图 - cv.polylines(img, [approx], True, (0, 0, 255), thickness=2) - cv.circle(img, fp, 0, (255,255,255),9) - cv.imshow("result", np.hstack((img0,hsv,cv.cvtColor(cyan_mask, cv.COLOR_GRAY2RGB),img))) - cv.waitKey(0) - cv.destroyAllWindows() - - return angle - -def get_angle_2(): - x,y = [117,128] - w,h = [47,47] - loc_tp = get_img([x,y], [w,h]) - loc_tp = cv.cvtColor(loc_tp, cv.COLOR_RGB2BGR) - - blue = np.array([234, 191, 4]) - arrow = cv.imread('imgs/loc_arrow.jpg') - loc_tp[np.sum(np.abs(loc_tp - blue), axis=-1) > 0] = [0, 0, 0] - mx_acc = 0 - ang = 0 - for i in range(360): - rt = image_rotate(arrow, i) - result = cv.matchTemplate(loc_tp, rt, cv.TM_CCORR_NORMED) - min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result) - if max_val > mx_acc: - mx_acc = max_val - mx_loc = (max_loc[0] + 12, max_loc[1] + 12) - ang = i - print(max_val) - return ang - - - -def get_camera_angle(debug=False, sample_image=False): - - # x,y = [47,58] - # w,h = [187,187] - x,y = [107,118] - w,h = [67,67] - - img = get_img([x,y], [w,h],debug,sample_image) - img = img - - img0 = img.copy() - # img = cv.GaussianBlur(img, (5,5), 0) - hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) - - - # 定义青色掩膜,使用较低饱和度上界,过滤掉人物箭头 - cyan_mask = get_mask(hsv, color_range = np.array([ - [78, 30, 30], - [99, 150, 150]])) - - # 计算这些像素点的坐标平均值 - rows, cols = np.where(cyan_mask == 255) - cy, cx = np.mean(rows), np.mean(cols) - - dx = cx - w / 2.0 - dy = cy - h / 2.0 - angle = np.degrees(np.arctan2(dy, dx)) - angle = np.around(angle, 2) - # hist = get_polar_stats(cyan_mask) - - - # 输出结果 - # print(hist) - - # print(angle) - if debug: - # 画出原图、二值掩膜图以及轮廓图 - # cv.polylines(img, [approx], True, (0, 0, 255), thickness=2) - cv.circle(img, [int(cx),int(cy)], 0, (255,255,255),9) - cv.imshow("result", np.hstack((img0,img, hsv,cv.cvtColor(cyan_mask, cv.COLOR_GRAY2RGB),img))) - cv.waitKey(0) - cv.destroyAllWindows() - - return angle - - -def main(): - debug = True - sample_image = 'sample.png' #False or path - if not sample_image: - # switch_window() - time.sleep(0.5) - # angle = get_angle(debug,sample_image) - - x,y = [117,128] - w,h = [47,47] - img = get_img([x,y], [w,h],debug,sample_image) - get_orb(img) - - # get_angle(debug,sample_image) - # print(angle) - -# if __name__ == '__main__': -# main() diff --git a/utils/map.py b/utils/map.py index aae54b01..e861a118 100644 --- a/utils/map.py +++ b/utils/map.py @@ -37,7 +37,7 @@ def map_init(self): points = result["max_loc"] log.debug(points) for i in range(6): - self.calculated.Click(points) + self.calculated.click(points) break time.sleep(0.1) @@ -59,7 +59,7 @@ def start_map(self, map, map_name): elif key == "f": self.calculated.teleport(key, value) elif key == "mouse_move": - self.calculated.Mouse_move(value) + self.calculated.mouse_move(value) elif key == "fighting": if value == 1: # 进战斗 ret = self.calculated.fighting() @@ -82,7 +82,7 @@ def start_map(self, map, map_name): fight_data["day_time"] = day_time sra_config_obj.fight_data = fight_data elif value == 2: # 障碍物 - self.calculated.Click() + self.calculated.click() time.sleep(1) else: raise Exception(_("map数据错误, fighting参数异常:{map_filename}").format(map_filename=map_filename), map) @@ -159,8 +159,8 @@ def start_map(self:Map, start, check:bool=False): count = self.calculated.wait_join() log.info(_('地图加载完毕,加载时间为 {count} 秒').format(count=count)) time.sleep(2) # 加2s防止人物未加载 - map_name = name.split("-")[0] - self.start_map(map, map_name) + #map_name = name.split("-")[0] + self.start_map(map, name) else: log.info(_('地图编号 {start} 不存在,请尝试检查更新').format(start=start)) start_map(self, start) diff --git a/utils/record_v7.2.py b/utils/record_v7.2.py index b1838a64..f39c98a4 100644 --- a/utils/record_v7.2.py +++ b/utils/record_v7.2.py @@ -2,7 +2,7 @@ Author: AlisaCat Date: 2023-05-07 21:45:43 LastEditors: Night-stars-1 nujj1042633805@gmail.com -LastEditTime: 2023-06-12 21:15:15 +LastEditTime: 2023-07-19 21:35:30 Description: wasd移动,x是进战斗,鼠标左键是打障碍物等,不要用鼠标移动视角,用方向键左右来移动视角(脚本运行后方向键左右会映射成鼠标) F9停止录制并保存 Copyright (c) 2023 by AlisaCat, All Rights Reserved. @@ -44,7 +44,7 @@ def timestamped_print(*args, **kwargs): # 输出列表 event_list = [] # 不同操作间延迟记录 -last_time = time.time() +last_time = time.perf_counter() # 按键按下的时间字典 # key_down_time = {} # 创建一个默认值为0的字典 @@ -59,7 +59,7 @@ def timestamped_print(*args, **kwargs): save_name = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") -def Click(points): +def click(points): x, y = points win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,x,y,0,0)#按下 time.sleep(0.5) @@ -195,22 +195,22 @@ def on_press(key): pass elif key.char in key_list: save_mouse_move_by_key() - key_down_time[key.char] = time.time() + key_down_time[key.char] = time.perf_counter() if debug_mode: - print("捕捉按键按下:", key.char, time.time()) + print("捕捉按键按下:", key.char, time.perf_counter()) except AttributeError: pass def on_release(key): + current_time = time.perf_counter() global last_time, key_down_time, mouse_move_pos_list, cen_mouse_pos, mouse_watch, save_name - current_time = time.time() try: if key.char in key_list and key.char in key_down_time: event_list.append( {'key': key.char, 'time_sleep': key_down_time[key.char] - last_time, - 'duration': time.time() - key_down_time[key.char]}) - last_time = time.time() + 'duration': current_time - key_down_time[key.char]}) + last_time = time.perf_counter() del key_down_time[key.char] if debug_mode: print("捕捉:", event_list[-1]) @@ -218,7 +218,7 @@ def on_release(key): if debug_mode: print("捕捉X进入战斗") mouse_watch = False - Click(cen_mouse_pos) + click(cen_mouse_pos) mouse_watch = True if key.char == "v": if debug_mode: @@ -279,7 +279,7 @@ def on_click(x, y, button, pressed): global last_time if pressed: event_list.append( - {'key': 'click', 'time_sleep': time.time() - last_time}) + {'key': 'click', 'time_sleep': time.perf_counter() - last_time}) print("捕捉:", event_list[-1]) else: pass @@ -322,9 +322,9 @@ def save_json(): elif 'mouse_move_dxy' in element_save: normal_save_dict["map"].append( {"mouse_move": element_save['mouse_move_dxy'][0]}) - if not os.path.exists("map"): - os.makedirs("map") - with open(f'map//{save_name}.json', 'wb') as f: + if not os.path.exists("maps"): + os.makedirs("maps") + with open(f'maps//{save_name}.json', 'wb') as f: f.write(orjson.dumps(normal_save_dict, option=orjson.OPT_INDENT_2)) diff --git a/utils/route_helper.py b/utils/route_helper.py deleted file mode 100644 index e2c6bdac..00000000 --- a/utils/route_helper.py +++ /dev/null @@ -1,31 +0,0 @@ -from .get_angle import get_camera_angle, get_angle -# from .switch_window import switch_window -# from .calculated import * -import time -import pyautogui -import win32con, win32api -import numpy as np -import sys -import cv2 -# 分辨率1920x1080 -# 角度,顺时针为正,东方为0 -# 速度,正常情况下,跑动速度为25小地图像素/秒(停稳后),?大地图像素/秒 - -# ================基础操作 - - - - -def move(t, run=True): - print(f'前进{t}秒') - pyautogui.keyDown("w") - if run: - pyautogui.keyDown("shift") - time.sleep(t) - pyautogui.keyUp("w") - if run: - pyautogui.keyUp("shift") - time.sleep(0.5) - - - diff --git a/utils/simulated_universe.py b/utils/simulated_universe.py index 0d0ce51f..ae487b15 100644 --- a/utils/simulated_universe.py +++ b/utils/simulated_universe.py @@ -2,7 +2,7 @@ Author: Night-stars-1 nujj1042633805@gmail.com Date: 2023-05-19 16:04:28 LastEditors: Night-stars-1 nujj1042633805@gmail.com -LastEditTime: 2023-06-02 00:43:24 +LastEditTime: 2023-07-20 14:18:04 Description: Copyright (c) 2023 by Night-stars-1, All Rights Reserved. @@ -74,14 +74,14 @@ def choose_level(self, level: int, start: int, pos): for i in range(abs(level - start)): self.calculated.scroll(-1) time.sleep(0.5) - self.calculated.Relative_click((60, 50)) + self.calculated.relative_click((60, 50)) start_time = time.time() while True: left, top, right, bottom = self.window.left, self.window.top, self.window.right, self.window.bottom img_fp = ImageGrab.grab((left, top, right, bottom), all_screens=True) text, pos = self.calculated.ocr_pos(img_fp, _("下载初始角色")) if pos: - self.calculated.Click((left+pos[0], top+pos[1])) + self.calculated.click((left+pos[0], top+pos[1])) break if time.time() - start_time > 10: log.info(_("识别超时")) @@ -107,7 +107,7 @@ def choose_role(self, roles: list): if result['max_val'] > 0.90: roles.remove(role) #points = self.calculated.calculated(result, img.shape) - self.calculated.Click((result['max_loc'][0]+10, result['max_loc'][1]+10)) + self.calculated.click((result['max_loc'][0]+10, result['max_loc'][1]+10)) time.sleep(0.1) if len(roles) == 0: break @@ -193,7 +193,7 @@ def auto_map(self, start = 1, choose_list = []): self.choose_level(level, start, pos, platform) self.choose_role(roles, platform) if platform == _("PC"): - self.calculated.Relative_click((85, 90)) + self.calculated.relative_click((85, 90)) elif platform == _("模拟器"): os.system(f"adb -s {self.order} shell input tap 1020 654") self.calculated.ocr_click("O确认", 2)