MSS截图转NumPy传入cv2.matchTemplate匹配异常问题
问题核心原因
你遇到的截图全黑、匹配失败问题和色彩空间转换无关,核心是两个逻辑错误:
- Windows高分屏缩放适配缺失:如果你系统开了125%/150%等显示缩放,默认运行的程序DPI感知等级不足,MSS会按缩放后的逻辑分辨率计算坐标,你写死的截图区域坐标超出实际逻辑屏幕边界,抓到的像素值全为0,自然imshow全黑、模板匹配无结果。本地读取PNG能跑是因为不存在屏幕坐标抓取的问题。
- 坐标体系不统一:你用来计算距离的参考点
pta = np.array([[1036, 746]])是全屏绝对坐标,但模板匹配输出的坐标是截图区域内的相对坐标,直接计算欧氏距离结果完全错误。 - 附带小bug:
cv2.waitKey的按键判断逻辑写法错误,按q关闭窗口的功能不生效;未做无匹配结果的边界判断,没找到模板时会触发空数组计算报错。
修复方案
- 程序启动时开启Windows DPI感知,让MSS获取真实物理屏幕坐标,避免截图越界。
- 把模板匹配得到的相对坐标加上截图区域的左上角偏移,转换为全屏绝对坐标后再计算距离。
- 补全无匹配结果的异常判断,修正窗口按键响应逻辑。
- 调试阶段先全屏截图确认MSS输出正常,再调整局部截图的坐标参数,避免坐标越界。
修复后完整代码
import cv2 import numpy as np import mss import time from scipy.spatial.distance import cdist import ctypes # 开启Windows DPI感知,解决高分屏缩放截图全黑问题 try: ctypes.windll.user32.SetProcessDPIAware() except: # 非Windows系统直接跳过 pass time.sleep(3) # 原10秒等待过长,3秒足够完成窗口切换 def screen_template_closest(): with mss.mss() as sct: # 调试时可先打印屏幕分辨率确认坐标范围:print(sct.monitors) # 局部截图区域 monitor = {"top": 1059, "left": 62, "width": 285, "height": 640} img = np.array(sct.grab(monitor)) print("截图尺寸:", img.shape) # 显示原始截图 cv2.imshow("OpenCV/Numpy normal", img) # 显示灰度截图 img_gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY) cv2.imshow('OpenCV/Numpy grayscale', img_gray) # 读取模板 template = cv2.imread('C:\\Users\\Faon\\projetadminEnv3\\test\\templates.PNG', 0) h, w = template.shape res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) threshold = 0.75 loc = np.where(res >= threshold) coordsFinded = [] count = 0 mask = np.zeros(img.shape[:2], np.uint8) for pt in zip(*loc[::-1]): if mask[pt[1] + h//2, pt[0] + w//2] != 255: mask[pt[1]:pt[1]+h, pt[0]:pt[0]+w] = 255 count += 1 cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0,255,0), 3) # 把截图相对坐标转成全屏绝对坐标 abs_x = pt[0] + monitor["left"] abs_y = pt[1] + monitor["top"] coordsFinded.append((abs_x, abs_y)) cv2.imshow('Matches', img) # 修正waitKey响应逻辑 if cv2.waitKey(0) & 0xFF == ord('q'): cv2.destroyAllWindows() # 无匹配结果直接返回 if not coordsFinded: print("未找到符合阈值的匹配目标") return None # 计算离指定点最近的匹配目标 pta = np.array([[1036, 746]]) others1 = np.array(coordsFinded) print("所有匹配到的目标全屏坐标:", others1) distances = cdist(pta, others1) winnerClose = others1[distances.argmin()] print("距离指定点最近的目标坐标:", winnerClose) return winnerClose print(screen_template_closest())
调试注意事项
- 第一次运行先把
monitor参数改成sct.monitors[1](对应主屏幕全屏),确认imshow能正常显示屏幕内容,再调整局部截图的坐标值,避免坐标越界。 - 如果匹配不到目标,适当降低
threshold阈值,确认模板是在当前屏幕缩放比例下截取的,不要用缩放前/缩放后的图跨比例匹配。 - 参考点
pta的坐标要和DPI适配后的物理坐标保持一致,不要用缩放前的旧坐标。
内容的提问来源于stack exchange,提问作者BaxiDi
相关产品推荐
相关产品推荐

