图色脚本的灵魂不是算法,是那点不肯将就的调参耐心
刚玩图色脚本那阵子,我以为难点在「找图算法多牛」。后来写了几十个脚本才醒悟:算法就那几样,真正拉开差距的,是你愿不愿意为一个阈值、一张模板较真。图色脚本干的核心活就两件——找色(在屏幕某区域定位一个颜色)和找图(定位一张小图出现在哪)。今天只聊找图,因为它最容易出问题,也最能见功力。
模板匹配到底在算什么
最稳的办法是 OpenCV 的 matchTemplate。它拿你给的小图(模板),在大图(截图)上滑窗比对,每个位置算一个相似度,最后你取相似度最高的点当匹配位置。代码其实很短:
import cv2
res = cv2.matchTemplate(screen, templ, cv2.TM_CCOEFF_NORMED)
minv, maxv, minl, maxl = cv2.minMaxLoc(res)
if maxv >= 0.85: # 阈值,关键
x, y = maxl
这里 TM_CCOEFF_NORMED 返回 -1 到 1,越接近 1 越像。但新手最常栽的就是那个 0.85 阈值:设太高,模板稍微被抗锯齿糊一点就匹配不到;设太低,满屏都是「匹配成功」。我的习惯是从 0.8 起,对着目标真实截图反复试,找到「刚好能稳定命中、又不误报」的临界值,而不是拍脑袋。
分辨率一变就翻车
模板匹配是逐像素比对,所以模板必须和截图同分辨率。你拿 1080p 截的按钮图,去 2K 屏上找,十有八九扑空。解决办法是多尺度:把截图按比例缩放成几档,分别和模板比,取最好的。
best = None
for scale in [0.8, 0.9, 1.0, 1.1, 1.25]:
w = int(screen.shape[1] * scale)
small = cv2.resize(screen, (w, int(screen.shape[0]*scale)))
r = cv2.matchTemplate(small, templ, cv2.TM_CCOEFF_NORMED)
_, mv, _, ml = cv2.minMaxLoc(r)
if best is None or mv > best[0]:
best = (mv, ml, scale)
另外,模板里千万别带半透明阴影或会动的背景。我早年贪方便,直接截了带渐变背景的按钮,结果换个壁纸匹配的置信度直接掉到 0.6。后来学乖了:模板只截目标本身,背景纯色,必要时用 mask 参数把无关区域遮掉。
颜色容差那点事
找色(单点/区域取色)比找图更轻量,但也更娇气。屏幕一点亮度变化,同一个按钮的「红」可能从 (220,40,40) 变成 (215,38,42)。所以找色几乎都要配容差:不是比「完全相等」,而是比「每个通道差的绝对值都在容差内」。容差设 10 还是 30,全看目标颜色和周围环境的对比——对比越强,容差可以越小,越不容易误判到旁边相似色块。
一个常被忽略的细节:截图格式和模板格式要一致(都是 BGR 或都转成灰度)。OpenCV 读图默认 BGR,如果你某处用 PIL 读成 RGB 又没转换,颜色比对会整体偏掉,debug 半天才发现是通道顺序。
图色这东西,说穿了没什么高深理论,但它极其考验你对「环境会变」这件事的敬畏。算法给你一个相似度数字,剩下怎么解读、怎么兜底、怎么在目标稍微模糊时还能认出来,全靠你调参时那点不肯将就的耐心。调好了,脚本稳得像长在屏幕上;调不好,就是三天一小崩、五天一大崩。
