| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- """
- 诊断 pic0 误匹配:找出为何 pic0.png 会匹配到 草稿箱 区域而非正确缩略图。
- 用法: 在项目根目录执行
- python python/scripts/test_match_pic0_screenshot.py
- 输出: 不同 scale_min/threshold 下的匹配位置与相关系数,并生成标注图。
- """
- from __future__ import print_function
- import os
- import sys
- import json
- 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)
- 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-pic0.png')
- TEMPLATE_PATH = os.path.join(TMP_DIR, 'pic0.png')
- MARKED_WRONG = os.path.join(TMP_DIR, 'Screenshot-pic0-marked-wrong.png') # 当前错误匹配
- MARKED_BEST = os.path.join(TMP_DIR, 'Screenshot-pic0-marked-best.png') # 推荐参数匹配
- try:
- import cv2
- import numpy as np
- except ImportError:
- print('请安装: pip install opencv-python numpy')
- sys.exit(1)
- 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
- def multi_scale_with_correlation(screenshot, template, threshold=0.50, scale_min=0.05, scale_max=2.0, steps=60):
- """多尺度模板匹配,返回 (x, y, w, h, center_x, center_y, best_correlation) 或 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)
- return best
- def crop_template_square(template, percent=0.8, base='w'):
- """与 image-match.py 一致的方形中心裁剪。"""
- t_h, t_w = template.shape[:2]
- side_raw = (t_w if base == 'w' else t_h) * percent
- side = min(max(1, int(round(side_raw))), t_w, t_h)
- x0 = (t_w - side) // 2
- y0 = (t_h - side) // 2
- return template[y0:y0 + side, x0:x0 + side].copy()
- def draw_match(screenshot, x, y, w, h, center_x, center_y, out_path, label='match'):
- 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, label, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 1)
- out_dir = os.path.dirname(out_path)
- if out_dir:
- os.makedirs(out_dir, exist_ok=True)
- 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 or template is None:
- print('无法加载图片')
- sys.exit(1)
- # 与 process 一致:使用 [0.8, "w"] 裁剪模板
- template = crop_template_square(template, percent=0.8, base='w')
- sh, sw = screenshot.shape[:2]
- t_h, t_w = template.shape[:2]
- print('截图尺寸: {}x{}, 裁剪后模板: {}x{}'.format(sw, sh, t_w, t_h))
- print('')
- # 草稿箱通常在右上角;正确缩略图在内容区(偏左/中,偏上)
- def region_label(cx, cy):
- if cx >= sw * 0.65 and cy <= sh * 0.25:
- return '疑似草稿箱'
- return '内容区'
- scale_min_list = [0.05, 0.10, 0.15, 0.20, 0.25]
- threshold_list = [0.40, 0.44, 0.48, 0.52]
- results = []
- for scale_min in scale_min_list:
- for th in threshold_list:
- r = multi_scale_with_correlation(
- screenshot, template,
- threshold=th, scale_min=scale_min, scale_max=2.0, steps=60
- )
- if r is not None:
- x, y, w, h, cx, cy, corr = r
- label = region_label(cx, cy)
- results.append({
- 'scale_min': scale_min,
- 'threshold': th,
- 'x': x, 'y': y, 'w': w, 'h': h,
- 'center_x': cx, 'center_y': cy,
- 'corr': corr,
- 'label': label
- })
- if not results:
- print('未找到任何匹配,请降低阈值或扩大 scale 范围。')
- sys.exit(1)
- # 按相关系数从高到低排序
- results.sort(key=lambda r: -r['corr'])
- print('===== 所有匹配(按相关系数降序)=====')
- print('scale_min threshold center_x center_y corr 区域')
- print('-' * 60)
- for r in results[:20]:
- print(' {:.2f} {:.2f} {:4d} {:4d} {:.3f} {}'.format(
- r['scale_min'], r['threshold'], r['center_x'], r['center_y'], r['corr'], r['label']))
- # 当前逻辑:scale_min=0.05,阈值 0.40~0.52 中第一个命中的
- current = None
- for th in (0.52, 0.48, 0.44, 0.40):
- r = multi_scale_with_correlation(screenshot, template, threshold=th, scale_min=0.05, scale_max=2.0, steps=60)
- if r is not None:
- x, y, w, h, cx, cy, corr = r
- current = (x, y, w, h, cx, cy, corr)
- break
- if current:
- x, y, w, h, cx, cy, corr = current
- draw_match(screenshot, x, y, w, h, cx, cy, MARKED_WRONG, 'current(scale_min=0.05) corr=%.3f' % corr)
- print('')
- print('【当前逻辑】scale_min=0.05 时匹配到: center=({}, {}), corr={:.3f}, 区域={}'.format(
- cx, cy, corr, region_label(cx, cy)))
- # 推荐:取「内容区」且相关系数最高的匹配
- best_content = None
- for r in results:
- if r['label'] == '内容区':
- best_content = r
- break
- if best_content:
- r = best_content
- draw_match(screenshot, r['x'], r['y'], r['w'], r['h'], r['center_x'], r['center_y'],
- MARKED_BEST, 'best(scale_min=%.2f) corr=%.3f' % (r['scale_min'], r['corr']))
- print('')
- print('【推荐】内容区最佳: scale_min={:.2f}, threshold={:.2f}, center=({}, {}), corr={:.3f}'.format(
- r['scale_min'], r['threshold'], r['center_x'], r['center_y'], r['corr']))
- print('')
- print('建议: 在 image-match.py 中,对相册缩略图(pic) 将 scale_min_use 改为 min(scale_min, 0.15) 或 min(scale_min, 0.20),')
- print(' 避免 scale_min=0.05 时小尺度模板误匹配到右上角 草稿箱 区域。')
- else:
- print('')
- print('未找到内容区匹配,请检查截图与模板是否一致。')
- # 诊断结论
- print('')
- print('===== 诊断结论 =====')
- if current and region_label(current[4], current[5]) == '疑似草稿箱':
- print('原因: scale_min=0.05 时,模板被缩得很小,与右上角 草稿箱 区域产生较高相关系数,被选为最佳匹配。')
- print('解决: 提高相册缩略图的最小缩放 scale_min(如 0.15 或 0.20),只在该尺度以上搜索,避免误匹配。')
- else:
- print('当前参数下匹配位置正常,若仍出现误匹配可适当提高 threshold 或 scale_min。')
- if __name__ == '__main__':
- main()
|