| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- """
- 临时测试脚本:对 pic1.png 与 Screenshot-pic1.png 做多组参数匹配,直到找到匹配并在截图上标出。
- 用法: 在项目根目录执行
- python python/scripts/test_match_pic1_screenshot.py
- 或
- cd static/process/GenerateNote && python ../../../python/scripts/test_match_pic1_screenshot.py
- 输出: 控制台打印最佳参数与坐标,并生成 Screenshot-pic1-marked.png 标出匹配框与中心点。
- """
- from __future__ import print_function
- import os
- import sys
- import json
- # 项目根目录(本脚本在 python/scripts/)
- SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
- PROJECT_ROOT = os.path.normpath(os.path.join(SCRIPT_DIR, '..', '..'))
- if PROJECT_ROOT not in sys.path:
- sys.path.insert(0, PROJECT_ROOT)
- # 默认路径:流程目录下的 tmp
- PROCESS_DIR = os.path.join(PROJECT_ROOT, 'static', 'process', 'GenerateNote')
- TMP_DIR = os.path.join(PROCESS_DIR, 'tmp')
- SCREENSHOT_PATH = os.path.join(TMP_DIR, 'Screenshot-pic1.png')
- TEMPLATE_PATH = os.path.join(TMP_DIR, 'pic1.png')
- MARKED_OUT_PATH = os.path.join(TMP_DIR, 'Screenshot-pic1-marked.png')
- try:
- import cv2
- import numpy as np
- except ImportError:
- print('请安装: pip install opencv-python numpy')
- sys.exit(1)
- # 动态加载 image-match 模块(文件名带横杠)
- import importlib.util
- _image_match_path = os.path.join(SCRIPT_DIR, 'image-match.py')
- _spec = importlib.util.spec_from_file_location('image_match', _image_match_path)
- _image_match = importlib.util.module_from_spec(_spec)
- _spec.loader.exec_module(_image_match)
- load_image = _image_match.load_image
- multi_scale_template_match = _image_match.multi_scale_template_match
- match_by_features = _image_match.match_by_features
- HAS_ROMA = getattr(_image_match, 'HAS_ROMA', False)
- match_by_roma = getattr(_image_match, 'match_by_roma', lambda *a, **k: None)
- def multi_scale_with_correlation(screenshot, template, threshold=0.50, scale_min=0.4, scale_max=1.65, steps=80):
- """多尺度模板匹配并返回最佳相关系数。返回 (x, y, w, h, center_x, center_y, best_val) 或 None。"""
- gray_screen = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
- gray_tpl = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
- sh, sw = screenshot.shape[:2]
- t_h, t_w = template.shape[:2]
- best = None
- best_val = threshold
- step = max(0.02, (scale_max - scale_min) / float(steps))
- for scale in np.arange(scale_min, scale_max + step * 0.5, step):
- w = max(8, int(round(t_w * scale)))
- h = max(8, int(round(t_h * scale)))
- if h > sh or w > sw:
- continue
- resized = cv2.resize(gray_tpl, (w, h), interpolation=cv2.INTER_AREA)
- result = cv2.matchTemplate(gray_screen, resized, cv2.TM_CCOEFF_NORMED)
- min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
- if max_val > best_val:
- best_val = max_val
- x, y = int(max_loc[0]), int(max_loc[1])
- center_x = x + w // 2
- center_y = y + h // 2
- best = (x, y, w, h, center_x, center_y, best_val)
- if best is None:
- return None
- return best
- def draw_match(screenshot, x, y, w, h, center_x, center_y, out_path):
- """在截图上画匹配框和中心点并保存。"""
- img = screenshot.copy()
- cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
- cv2.circle(img, (center_x, center_y), 8, (0, 0, 255), 2)
- cv2.putText(img, 'center', (center_x + 10, center_y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)
- cv2.imwrite(out_path, img)
- print('已保存标注图:', out_path)
- def main():
- screenshot_path = SCREENSHOT_PATH
- template_path = TEMPLATE_PATH
- if len(sys.argv) >= 3:
- screenshot_path = sys.argv[1]
- template_path = sys.argv[2]
- if not os.path.isfile(screenshot_path):
- print('截图不存在:', screenshot_path)
- sys.exit(1)
- if not os.path.isfile(template_path):
- print('模板不存在:', template_path)
- sys.exit(1)
- screenshot = load_image(screenshot_path)
- template = load_image(template_path)
- if screenshot is None:
- print('无法加载截图')
- sys.exit(1)
- if template is None:
- print('无法加载模板')
- sys.exit(1)
- sh, sw = screenshot.shape[:2]
- t_h, t_w = template.shape[:2]
- print('截图尺寸: {}x{}, 模板尺寸: {}x{}'.format(sw, sh, t_w, t_h))
- # 1) 尝试 RoMa(若可用),仅当中心在上半区时才采用
- if HAS_ROMA:
- roma_result = match_by_roma(screenshot, template, min_matches=4)
- if roma_result is not None:
- x, y, w, h, center_x, center_y = roma_result
- if center_y <= sh * 0.6:
- print('[RoMa] 匹配成功: center=({}, {})'.format(center_x, center_y))
- draw_match(screenshot, x, y, w, h, center_x, center_y, MARKED_OUT_PATH)
- print('推荐 process.json: 保持 [0.15, 2.0], 方形裁剪如 [0.8, "w"]')
- sys.exit(0)
- # 2) 收集多尺度模板匹配结果(参数网格),优先取「中心在画面上半区」的匹配(格子图通常在上方)
- sh, sw = screenshot.shape[:2]
- upper_ratio = 0.6 # 中心 y < sh * upper_ratio 视为上半区(格子区域)
- candidates = []
- # 3) 多尺度模板匹配:参数网格(缩略图通常 scale 约 0.1~0.4)
- scale_min_list = [0.05, 0.10, 0.15]
- scale_max_list = [1.0, 1.5, 2.0]
- threshold_list = [0.40, 0.46, 0.52]
- best_result = None
- best_corr = 0.0
- best_params = None
- for scale_min in scale_min_list:
- for scale_max in scale_max_list:
- if scale_max <= scale_min:
- continue
- for th in threshold_list:
- r = multi_scale_with_correlation(
- screenshot, template,
- threshold=th, scale_min=scale_min, scale_max=scale_max, steps=50
- )
- if r is not None:
- x, y, w, h, cx, cy, corr = r
- candidates.append((corr, (x, y, w, h, cx, cy), (scale_min, scale_max, th)))
- # 优先选中心在上半区的最高相关匹配,否则选全局最高相关
- best_result = None
- best_corr = 0.0
- best_params = None
- for corr, rect, params in sorted(candidates, key=lambda t: -t[0]):
- x, y, w, h, cx, cy = rect
- if cy <= sh * upper_ratio and corr > best_corr:
- best_corr = corr
- best_result = rect
- best_params = params
- if best_result is None and candidates:
- best_corr, best_result, best_params = max(candidates, key=lambda t: t[0])
- print('(未在上半区找到匹配,使用全局最高相关;请查看标注图确认是否为底部缩略图)')
- if best_result is None:
- print('未找到任何匹配。可尝试: 降低 threshold、扩大 scale_min~scale_max、或检查模板是否在截图内。')
- sys.exit(1)
- x, y, w, h, center_x, center_y = best_result
- scale_min, scale_max, th = best_params
- print('')
- print('[多尺度模板] 匹配成功 (最佳相关系数 {:.3f}, 中心在上半区)'.format(best_corr))
- print(' 中心坐标: ({}, {})'.format(center_x, center_y))
- print(' 矩形: x={} y={} w={} h={}'.format(x, y, w, h))
- print(' 推荐 process.json inVars:')
- print(' inVars: ["tmp/pic{{idx}}.png", [{}, {}], [0.8, "w"]]'.format(scale_min, scale_max))
- print(' 并在 image-match.py 中 fallback 阈值可设为 <= {:.2f}'.format(th))
- print('')
- draw_match(screenshot, x, y, w, h, center_x, center_y, MARKED_OUT_PATH)
- if __name__ == '__main__':
- main()
|