From 9644e7a36eccfacfe022432fa1ff6bd44e855039 Mon Sep 17 00:00:00 2001 From: aeon0 Date: Tue, 7 Dec 2021 12:14:50 +0100 Subject: [PATCH] Speed up template pathing (#226) * Speed up template pathing * change comment --- src/char/i_char.py | 10 +++++----- src/pather.py | 24 +++++++++++++----------- src/template_finder.py | 34 ++++++++++++++++++++++++---------- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/char/i_char.py b/src/char/i_char.py index 8c7c702..57c66f0 100644 --- a/src/char/i_char.py +++ b/src/char/i_char.py @@ -28,8 +28,8 @@ class IChar: self._screen = screen self._config = Config() self._last_tp = time.time() - # It actually is 0.04s per frame but many people have issues with it (because of lag?) - self._cast_duration = self._char_config["casting_frames"] * 0.05 + 0.04 + # Add a bit to be on the save side + self._cast_duration = self._char_config["casting_frames"] * 0.04 + 0.01 def can_teleport(self) -> bool: return bool(self._skill_hotkeys["teleport"]) @@ -82,7 +82,7 @@ class IChar: mouse.move(pos_monitor[0], pos_monitor[1], randomize=3, delay_factor=[factor*0.1, factor*0.14]) wait(0.012, 0.02) mouse.click(button="right") - wait(self._cast_duration, self._cast_duration + 0.03) + wait(self._cast_duration, self._cast_duration + 0.02) else: # in case we want to walk we actually want to move a bit before the point cause d2r will always "overwalk" pos_screen = self._screen.convert_monitor_to_screen(pos_monitor) @@ -140,11 +140,11 @@ class IChar: keyboard.send(self._char_config["battle_command"]) wait(0.08, 0.19) mouse.click(button="right") - wait(self._cast_duration + 0.08, self._cast_duration + 0.1) + wait(self._cast_duration + 0.13, self._cast_duration + 0.16) keyboard.send(self._char_config["battle_orders"]) wait(0.08, 0.19) mouse.click(button="right") - wait(self._cast_duration + 0.08, self._cast_duration + 0.1) + wait(self._cast_duration + 0.13, self._cast_duration + 0.16) keyboard.send(self._char_config["weapon_switch"]) wait(0.3, 0.35) # Make sure that we are back at the previous skill diff --git a/src/pather.py b/src/pather.py index 72c5cf0..33d5db5 100644 --- a/src/pather.py +++ b/src/pather.py @@ -92,15 +92,15 @@ class Pather: 122: {'ELDRITCH_2': (353, -145), 'ELDRITCH_3': (-149, -119)}, 123: {'ELDRITCH_3': (-99, -252), 'ELDRITCH_2': (403, -279), 'ELDRITCH_4': (-62, -109)}, # Shenk - 140: {'SHENK_0': (-149, -227), 'SHENK_17': (-500, 235), 'SHENK_15': (80, 13), 'SHENK_1': (445, -161)}, - 141: {'SHENK_0': (-129, 44), 'SHENK_17': (-520, 528), 'SHENK_15': (77, 293), 'SHENK_1': (464, 107), 'SHENK_2': (-167, -34)}, - 142: {'SHENK_1': (584, 376), 'SHENK_2': (-52, 235), 'SHENK_3': (357, -129), 'SHENK_4': (-443, -103)}, - 143: {'SHENK_2': (141, 505), 'SHENK_3': (549, 139), 'SHENK_4': (-251, 165), 'SHENK_6': (-339, -69)}, - 144: {'SHENK_6': (-108, 123), 'SHENK_7': (481, 151)}, - 145: {'SHENK_12': (97, -133), 'SHENK_7': (803, 372), 'SHENK_6': (209, 347), 'SHENK_8': (-245, 18)}, - 146: {'SHENK_12': (272, 111), 'SHENK_9': (-331, -144), 'SHENK_8': (-72, 258)}, - 147: {'SHENK_16': (317, -18), 'SHENK_9': (-67, 139), 'SHENK_10': (-431, 67)}, - 148: {'SHENK_16': (682, 103), 'SHENK_9': (301, 263), 'SHENK_10': (-65, 188), 'SHENK_11': (-306, 139)}, + 140: {'SHENK_0': (-149, -227), 'SHENK_1': (445, -161), 'SHENK_17': (-500, 235), 'SHENK_15': (80, 13)}, + 141: {'SHENK_0': (-129, 44), 'SHENK_1': (464, 107), 'SHENK_2': (-167, -34), 'SHENK_17': (-520, 528), 'SHENK_15': (77, 293)}, + 142: {'SHENK_1': (584, 376), 'SHENK_4': (-443, -103), 'SHENK_2': (-52, 235), 'SHENK_3': (357, -129)}, + 143: {'SHENK_4': (-251, 165), 'SHENK_2': (141, 505), 'SHENK_3': (549, 139), 'SHENK_6': (-339, -69)}, + 144: {'SHENK_6': (-108, 123), 'SHENK_7': (481, 151)}, + 145: {'SHENK_7': (803, 372), 'SHENK_12': (97, -133), 'SHENK_6': (209, 347), 'SHENK_8': (-245, 18)}, + 146: {'SHENK_12': (272, 111), 'SHENK_9': (-331, -144), 'SHENK_8': (-72, 258)}, + 147: {'SHENK_16': (317, -18), 'SHENK_9': (-67, 139), 'SHENK_10': (-431, 67)}, + 148: {'SHENK_16': (682, 103), 'SHENK_9': (301, 263), 'SHENK_10': (-65, 188), 'SHENK_11': (-306, 139)}, 149: {'SHENK_11': (261, 395), 'SHENK_10': (495, 421), 'SHENK_13': (393, -9)}, # A4 town 160: {"A4_TOWN_4": (-100, -133), "A4_TOWN_3": (-117, 238), "A4_TOWN_0": (-364, 151), "A4_TOWN_6": (24, -425), "A4_TOWN_5": (-347, -277)}, @@ -236,7 +236,7 @@ class Pather: def find_abs_node_pos(self, node_idx: int, img: np.ndarray) -> Tuple[float, float]: node = self._nodes[node_idx] - template_match = self._template_finder.search([*node], img, best_match=False) + template_match = self._template_finder.search([*node], img, best_match=False, roi=self._config.ui_roi["cut_skill_bar"], use_grayscale=True) if template_match.valid: # Get reference position of template in abs coordinates ref_pos_abs = self._screen.convert_screen_to_abs(template_match.position) @@ -378,5 +378,7 @@ if __name__ == "__main__": char = Hammerdin(config.hammerdin, config.char, screen, t_finder, ui_manager, pather) # pather.traverse_nodes_fixed("pindle_save_dist", char) # pather.traverse_nodes(Location.A3_TRAV_START, Location.A3_TRAV_SAVE_DIST, char) - pather.traverse_nodes(Location.A3_TOWN_START, Location.A3_STASH_WP, char) + t = time.time() + pather.traverse_nodes(Location.A5_SHENK_START, Location.A5_SHENK_SAVE_DIST, char) + print(time.time()-t) # display_all_nodes(pather, filter="TRAV") diff --git a/src/template_finder.py b/src/template_finder.py index 1db0d56..00a3b9f 100644 --- a/src/template_finder.py +++ b/src/template_finder.py @@ -37,7 +37,12 @@ class TemplateFinder: key = file_name[:-4].upper() template_img = load_template(file_path, 1.0, True) mask = alpha_to_mask(template_img) - self._templates[key] = [cv2.cvtColor(template_img, cv2.COLOR_BGRA2BGR), 1.0, mask] + self._templates[key] = [ + cv2.cvtColor(template_img,cv2.COLOR_BGRA2BGR), + cv2.cvtColor(template_img,cv2.COLOR_BGRA2GRAY), + 1.0, + mask + ] def get_template(self, key): return cv2.cvtColor(self._templates[key][0], cv2.COLOR_BGRA2BGR) @@ -49,7 +54,8 @@ class TemplateFinder: threshold: float = None, roi: List[float] = None, normalize_monitor: bool = False, - best_match: bool = False + best_match: bool = False, + use_grayscale: bool = False, ) -> TemplateMatch: """ Search for a template in an image @@ -59,6 +65,7 @@ class TemplateFinder: :param roi: Region of Interest of the inp_img to restrict search area. Format [left, top, width, height] :param normalize_monitor: If True will return positions in monitor coordinates. Otherwise in coordinates of the input image. :param best_match: If list input, will search for list of templates by best match. Default behavior is first match. + :param use_grayscale: Use grayscale template matching for speed up :return: Returns a TempalteMatch object with a valid flag """ threshold = self._config.advanced_options["template_threshold"] if threshold is None else threshold @@ -70,17 +77,20 @@ class TemplateFinder: if type(ref) == str: templates = [self._templates[ref][0]] - scales = [self._templates[ref][1]] - masks = [self._templates[ref][2]] + templates_gray = [self._templates[ref][1]] + scales = [self._templates[ref][2]] + masks = [self._templates[ref][3]] names = [ref] best_match = False elif type(ref) == list: templates = [self._templates[i][0] for i in ref] - scales = [self._templates[i][1] for i in ref] - masks = [self._templates[i][2] for i in ref] + templates_gray = [self._templates[i][1] for i in ref] + scales = [self._templates[i][2] for i in ref] + masks = [self._templates[i][3] for i in ref] names = ref else: templates = [ref] + templates_gray = [cv2.cvtColor(ref, cv2.COLOR_BGRA2GRAY)] scales = [1.0] masks = [None] best_match = False @@ -99,6 +109,9 @@ class TemplateFinder: rh *= scale if img.shape[0] > template.shape[0] and img.shape[1] > template.shape[1]: + if use_grayscale: + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + template = templates_gray[count] self.last_res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED, mask=mask) np.nan_to_num(self.last_res, copy=False, nan=0.0, posinf=0.0, neginf=0.0) _, max_val, _, max_pos = cv2.minMaxLoc(self.last_res) @@ -137,7 +150,8 @@ class TemplateFinder: time_out: float = None, threshold: float = None, best_match: bool = False, - take_ss: bool = True + take_ss: bool = True, + use_grayscale: bool = False ) -> TemplateMatch: """ Helper function that will loop and keep searching for a template @@ -152,7 +166,7 @@ class TemplateFinder: start = time.time() while 1: img = self._screen.grab() - template_match = self.search(ref, img, roi=roi, threshold=threshold, best_match=best_match) + template_match = self.search(ref, img, roi=roi, threshold=threshold, best_match=best_match, use_grayscale=use_grayscale) is_loading_black_roi = np.average(img[:, 0:self._config.ui_roi["loading_left_black"][2]]) < 1.0 if not is_loading_black_roi or "LOADING" in ref: if template_match.valid: @@ -172,14 +186,14 @@ if __name__ == "__main__": config = Config() screen = Screen(config.general["monitor"]) template_finder = TemplateFinder(screen) - search_templates = ["TP_ACTIVE", "TP_INACTIVE"] + search_templates = ["A3_TOWN_1"] while 1: # img = cv2.imread("") img = screen.grab() display_img = img.copy() start = time.time() for key in search_templates: - template_match = template_finder.search(key, img, best_match=True, threshold=0.35) + template_match = template_finder.search(key, img, best_match=True, threshold=0.35, use_grayscale=True) if template_match.valid: cv2.putText(display_img, str(template_match.name), template_match.position, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA) cv2.circle(display_img, template_match.position, 7, (255, 0, 0), thickness=5)