为什么换了分辨率图色脚本就废了?用 OpenCV 模板匹配把"找图"做稳
做游戏图色脚本的人,几乎都踩过同一个坑:在 1920×1080 上写得好好的"找图",换台电脑、或窗口一缩放,整个脚本就找不着北了。传统做法(大漠插件 dm.FindPic、按键精灵的找图命令)本质是把一张固定尺寸的位图拿去逐像素比对,分辨率一变,图就对不上。
这一篇就讲:用 OpenCV 的模板匹配(cv2.matchTemplate)+ 多尺度,把"找图"做成能抗分辨率变化的能力。它依然是纯图色思路,但底层换成了更稳的算法。
一、传统找图为什么脆弱
大漠找图的原理可以简化成一句话:拿模板图在屏幕截图上滑动,每挪一个像素就比一遍,找差异最小的位置。问题在于:
- 尺寸敏感:模板是 100×40 的按钮,游戏在 125% 缩放下实际显示 125×50,像素就不对齐了。
- 抗干扰差:画面叠加了半透明提示、描边变化时,逐像素比对直接判"不匹配"。
- 旋转/形变无能为力:图标只要转个角度,传统找图就失效。
核心思路:OpenCV 不依赖"像素级严丝合缝",而是用归一化相关系数衡量相似度,再配合多尺度缩放模板,让同一套脚本能适配不同分辨率。
二、核心函数:cv2.matchTemplate
函数签名:
result = cv2.matchTemplate(image, templ, method[, result])
| 参数 | 含义 |
|---|---|
image | 待搜索的大图(截图),BGR 格式 ndarray |
templ | 模板小图,尺寸必须 ≤ 大图 |
method | 匹配方法,图色脚本首选 cv2.TM_CCOEFF_NORMED |
返回值:一个二维浮点数组 result,每个元素是该位置与模板的相似度。取值 -1~1,越接近 1 越像。
新手必踩坑:用
TM_CCOEFF_NORMED / TM_CCORR_NORMED 等"归一化"方法时,相似度是越大越好,要用 minMaxLoc 取 max;而 TM_SQDIFF 系列是越小越好,要取 min。方法选错、取值方向搞反,会整张图乱报匹配点。
三、单尺度匹配:先跑通最小可用版
import cv2
# 截图与大漠一样,OpenCV 读进来是 BGR 顺序
screen = cv2.imread('screen.png') # 大图
templ = cv2.imread('button.png') # 模板小图
# 归一化相关系数匹配:越接近 1 越像
res = cv2.matchTemplate(screen, templ, cv2.TM_CCOEFF_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(res)
print('最高相似度:', max_val)
if max_val >= 0.85: # 阈值自定,0.8~0.9 常见
x, y = max_loc # 匹配到的左上角坐标
print('按钮坐标:', x, y)
这段代码就能替代一次 dm.FindPic。但还没解决分辨率问题——模板尺寸固定的话,缩放后依然对不上。下面加多尺度。
四、多尺度匹配:抗分辨率变化的关键
场景:同一游戏在 100% / 125% / 150% 缩放下,按钮尺寸会变。做法是:把模板按多个比例缩放后分别去匹配,取全局最优。
import cv2
import numpy as np
def find_template(screen, templ, threshold=0.85, scales=(1.0, 0.9, 0.8, 1.1, 1.2)):
h, w = templ.shape[:2]
best = None
for scale in scales:
tw, th = int(w * scale), int(h * scale)
# 跳过非法尺寸:模板不能大于大图、不能缩没了
if tw < 1 or th < 1 or tw > screen.shape[1] or th > screen.shape[0]:
continue
t = cv2.resize(templ, (tw, th))
res = cv2.matchTemplate(screen, t, cv2.TM_CCOEFF_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(res)
if best is None or max_val > best[0]:
best = (max_val, max_loc, scale, (tw, th))
if best and best[0] >= threshold:
x, y = best[1]
return (x, y, best[2], best[3]) # 左上角、缩放比、模板尺寸
return None
screen = cv2.imread('screen.png')
templ = cv2.imread('button.png')
hit = find_template(screen, templ)
print('命中:', hit) # 例如 (430, 120, 1.1, (88, 44))
为什么这样更稳:无论游戏是 125% 还是 150% 显示,只要真实按钮落在一个缩放比例附近,多尺度循环就能"对上号",一套脚本适配多种分辨率,这正是传统固定位图找图做不到的。
五、工程上还要补的两件事
- ROI 限定搜索区:别拿全屏去 matchTemplate,先把截图裁到按钮可能出现的区域(如底部技能栏),速度能快一个量级。
- 预处理提特征:匹配前对截图和模板做灰度化、二值化或边缘提取(见本专栏 L5/L7),能排除半透明描边、背景纹理的干扰,命中率更高。
性能提醒:多尺度 + 全屏匹配在 1080p 下可能跑几十毫秒。图色脚本若要逐帧实时,请配合 ROI 裁剪、降采样、或只匹配关键状态点,别无脑全屏扫。
本讲要点
cv2.matchTemplate用归一化方法时取 max,TM_SQDIFF取 min,方向别反。- 模板必须比大图小,否则直接报错。
- 多尺度缩放模板是抗分辨率变化的核心技巧。
- 配合 ROI 裁剪与预处理,速度、命中率才能兼顾。
下一篇我们聊另一个图色刚需——找色:为什么直接比 RGB 容易被亮度骗,以及用 HSV + cv2.inRange 让颜色识别更抗干扰。
