利用Python+OpenCV实现截图匹配图像,支持自适应缩放、灰度匹配、区域匹配、匹配多个结果

一、依赖安装

pip install opencv-python
pip install pyautogui

二、获取系统缩放比例

注意:必须先通过ctypes获取wid之后才能导入pyautogui,如果需要在其它代码中引用该模块,最好把获取分辨率这部分代码放到程序入口处,然后传递给识图函数,避免提前导入pyautogui导致获取分辨率失败。

# 获取当前系统缩放比例
import ctypes
# wid必须在导入pyautogui之前赋值
wid = ctypes.windll.user32.GetSystemMetrics(0)
import pyautogui
screen_width , _ = pyautogui.size()
true_scale=screen_width/wid

 三、定义匹配结果类

x,y表示中心坐标,x1,y1,x2,y2表示四个顶点的坐标,match表示匹配度,取值范围0-100。

from dataclasses import dataclass
# 定义匹配结果类
@dataclass
class MatchInfo:
    match: float = 0
    x: int = 0
    y: int = 0
    x1: int = 0
    y1: int = 0
    x2: int = 0
    y2: int = 0

    @staticmethod
    def not_found():
        return MatchInfo()

    def __str__(self):
        return (
            f"匹配度={self.match:.2f}, "
            f"中心坐标: ({self.x}, {self.y}) "
        )

四、完整代码

参数:

template:模板图片,字符串,即待匹配图片,支持两种形式,可以传入图片地址,也可以直接传入图片名称,此时会从代码中设定的图片文件夹寻找图片;

min_threshold:最小匹配度,浮点数,只返回匹配度大于等于该值的结果,取值范围0-100

gray:灰度匹配,布尔值,为真时则忽略颜色,可以大幅提升匹配速度,适用于不考虑颜色的情况

a,b,c,d:区域匹配坐标,整数,都为0时不生效,否则只在该区域匹配,但是返回的结果坐标任然是相对于整个屏幕

image_sacle:图片缩放,浮点数,即传入的图片的缩放,也就是截图时电脑对应的缩放,默认和1.25,即125%,需要自己根据情况修改,取值是(1,1.25,1.5,1.75,2)

max_deviation:最大偏差值,整数,多个匹配结果之间距离的最小值,如果存在同一个目标被识别成两个结果的情况,可以适当增加这个参数的值

返回值:

列表类型,包含所有符合条件的匹配结果(MatchInfo对象),如果没有匹配到则是空列表

# 获取当前系统缩放比例
import ctypes
# wid必须在导入pyautogui之前赋值
wid = ctypes.windll.user32.GetSystemMetrics(0)
import pyautogui
screen_width , _ = pyautogui.size()
true_scale=screen_width/wid


from dataclasses import dataclass
# 定义匹配结果类
@dataclass
class MatchInfo:
    match: float = 0
    x: int = 0
    y: int = 0
    x1: int = 0
    y1: int = 0
    x2: int = 0
    y2: int = 0

    @staticmethod
    def not_found():
        return MatchInfo()

    def __str__(self):
        return (
            f"匹配度={self.match:.2f}, "
            f"中心坐标: ({self.x}, {self.y}) "
        )


import cv2
import os
import numpy as np

# 识图函数
def find_image_on_screen(template, min_threshold, gray=False, a=0, b=0, c=0, d=0,image_sacle=1.25,max_deviation = 13):    
    # 图片存放地址
    image_path='D:\\test\\image'
    # 处理传入的匹配度
    min_threshold = min_threshold / 100
    # 路径处理,如果传入的参数只是图片名称,就从默认路径寻找改图片
    if not os.path.isabs(template):
        template = os.path.join(image_path, template)
    # 读取模板图片
    templ = cv2.imread(template)
    # 获取屏幕截图
    screenshot = cv2.cvtColor(np.array(pyautogui.screenshot()), cv2.COLOR_RGB2BGR)
    # 获取模板图片长宽
    xx, yy = templ.shape[:-1]
    # 如果实际缩放和图片缩放不一致,就进行调整
    if true_scale != image_sacle:
        screenshot = cv2.resize(screenshot, None, fx=image_sacle / true_scale, fy=image_sacle / true_scale)
    # 判断是否需要灰度匹配
    if gray:
        screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
        templ = cv2.cvtColor(templ, cv2.COLOR_BGR2GRAY)
    # 判断是否需要区域匹配
    if a==0 and b==0 and c==0 and d==0:
        screenshot_region = screenshot  # 默认情况下,使用整个截图
    else:
        # 这里需要特殊处理,避免裁剪后的截图比模板图片小导致报错
        c = max(a + yy, c)
        d = max(b + xx, d)
        # 裁剪截图
        screenshot_region = screenshot[b:d, a:c]

    # 执行模板匹配
    result = cv2.matchTemplate(screenshot_region, templ, cv2.TM_CCOEFF_NORMED)
    # 获取满足匹配度的结果
    locations = np.where(result >= min_threshold)
    locations = list(zip(*locations[::-1]))
    match_info = {}
    # 对匹配结果进行筛选和去重,避免同一个目标被识别成多个结果,可以通过调整max_deviation的值来调整效果
    for loc in locations:
        x, y = loc
        x = x + a  # 调整x坐标以匹配整个截图
        y = y + b  # 调整y坐标以匹配整个截图
        match_value = result[y - b, x - a]
        skip_current = False
        for (prev_x, prev_y), prev_match_value in match_info.items():
            if abs(x - prev_x) <= max_deviation and abs(y - prev_y) <= max_deviation:
                if match_value > prev_match_value:
                    match_info[(x, y)] = match_value
                    del match_info[(prev_x, prev_y)]
                else:
                    skip_current = True
                break
        if not skip_current:
            match_info[(x, y)] = match_value
    # 构建匹配结果列表
    match_info_objects = []
    for (x, y), match_value in sorted(
        match_info.items(), key=lambda x: x[1], reverse=True
    ):
        h, w = xx, yy
        top_left = (x, y)
        bottom_right = (x + w, y + h)
        center = (x + w // 2, y + h // 2)
        matche = MatchInfo(
            match_value * 100,
            center[0],
            center[1],
            top_left[0],
            top_left[1],
            bottom_right[0],
            bottom_right[1],
        )
        match_info_objects.append(matche)
    return match_info_objects

五、示例用法

if __name__=='__main__':
    # 根据图片名称匹配,其他参数取默认值
    print("test1:")
    for s in (find_image_on_screen('test1.png')):
        print(s)
    
    print("test2:")
    # 根据图片路径匹配,匹配度85
    for s in (find_image_on_screen('D:\\test\\image\\test2.png',min_threshold=85)):
        print(s)

    print("test3:")
    # 根据路径匹配,忽略颜色,在指定区域匹配
    for s in (find_image_on_screen('D:\\test\\image\\test3.png',gray=True,a=11,b=35,c=983,d=639)):
        print(s)

运行结果:

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值