"""Image/color matching helpers (OpenCV-based). Ported from scripts/detect_and_click.py.""" import os import cv2 import numpy as np from ba_auto import config, driver SPARKLE_SHOT_PATH = os.path.join(config.SCRATCHPAD_DIR, "cafe_live.png") SPARKLE_CLICK_OFFSET = (75, 47) SPARKLE_THRESHOLD = 0.97 # The cafe camera's zoom level isn't reset before farming, and the sparkle # icon's on-screen size scales with it (see screenshots/cafe/sparkle/ # 01_sparke_zoomed_centered.png vs 03_sparkle_zoomed_out.png) -- a single # fixed-scale template match misses whenever the camera isn't at the exact # zoom the template was captured at. Try a spread of scales instead. SPARKLE_SCALES = (0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2) def _masked_template(template): b, g, r = cv2.split(template.astype(np.int16)) yellow_white = ((r > 180) & (g > 140) & (r - b > 60)) | ((r > 200) & (g > 200) & (b > 200)) mask_plane = (yellow_white.astype(np.uint8)) * 255 return cv2.merge([mask_plane, mask_plane, mask_plane]) def find_cafe_sparkle(): driver.screenshot(SPARKLE_SHOT_PATH) template_full = cv2.imread(config.CAFE_SPARKLE_TEMPLATE) img = cv2.imread(SPARKLE_SHOT_PATH) th0, tw0 = template_full.shape[:2] best = None for scale in SPARKLE_SCALES: tw, th = max(1, round(tw0 * scale)), max(1, round(th0 * scale)) template = cv2.resize(template_full, (tw, th)) mask = _masked_template(template) result = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED, mask=mask) _, max_val, _, max_loc = cv2.minMaxLoc(result) if max_val >= SPARKLE_THRESHOLD and (best is None or max_val > best[0]): best = (max_val, max_loc[0], max_loc[1], tw, th, scale) if best is None: return None score, x, y, tw, th, scale = best ox, oy = SPARKLE_CLICK_OFFSET return (x + tw // 2 + round(ox * scale), y + th // 2 + round(oy * scale), score)