Python找图辅助 – 使用SIFT算法精准匹配(摆脱pyautogui的限制)

Python找图辅助 – 使用SIFT算法精准匹配(摆脱pyautogui的限制)

前言:

在游戏脚本类辅助中,找图返回坐标是一个常见需求。现成工具和方法虽然不少,但要保证高精度并不容易。

比如python自带的pyautogui的 locateOnScreen 只支持100%图像匹配度,这是一个优点,精准找图,但也是个缺点,背景一变化就找不到了。

再例如按键精灵的找图功能,虽然能自定义设置找图匹配率,但依然在某些背景的变换下仍然无法匹配到图像。

本篇教程将使用匹配精度最高的SIFT算法来实现一个多参数可控的找图功能。为什么说精度最高?因为在这类图像匹配算法中著名的有ORB、SURF、SIFT,而SIFT算法是一种即便图像在不同尺度、旋转、光照变化的情况下,它依然可以保持良好的匹配性能的算法,当然SIFT的效率自然也是比其他低一些的,不过就算低也低不了多少,对吧,所以我们还是用精度最高的SIFT算法。

关于算法各位不用担心,最后会我会封装为一个工具函数,直接使用就行了。

所需库:

import os
import cv2
import pyautogui

正文:

第一步:获取屏幕截图与目标图片数据

我们将使用pyautogui截取全屏图片,然后通过OpenCV读取屏幕截图和待匹配的目标图像。以下代码将截屏并读取图像数据,返回numpy数组:

import pyautogui
import cv2

pyautogui.screenshot(imageFilename="screen.png")
screenPic = cv2.imread("screen.png")
img_url = r"C:\testpic\wx.png"
myPic = cv2.imread(img_url)

第二步:获取图像的关键点与描述符

使用SIFT算法提取屏幕截图和目标图片的关键点及描述符:

sift = cv2.SIFT_create()
screenPicKP, screenPicDES = sift.detectAndCompute(screenPic, None)
myPicKP, myPicDES = sift.detectAndCompute(myPic, None)

第三步:配置Flann匹配器

接下来,设置Flann匹配器的参数。Flann是一种快速匹配算法,适合大数据集的匹配。我们可以通过设置treeschecks参数来调整精度和速度的平衡:

indexParams = dict(algorithm=0, trees=100)
searcheParams = dict(checks=1000)
flann = cv2.FlannBasedMatcher(indexParams, searcheParams)

第四步:进行图像匹配

使用Flann的knn匹配法获取初步匹配结果,并根据特征点的距离对结果进行排序:

matches = flann.knnMatch(screenPicDES, myPicDES, k=2)
matches = sorted(matches, key=lambda x: x[0].distance)

第五步:筛选最优匹配并返回坐标

通过循环调整匹配率,找到最优匹配并返回坐标。最大搜索系数和初始匹配率都可自定义,以确保找到图像的最优位置:

x, y = None, None
max_init_num = 0.4
init_num = 0.1

while init_num <= max_init_num:
    goodMatches = []
    for m, n in matches:
        if m.distance < init_num * n.distance:
            goodMatches.append(m)
    index = int(len(goodMatches) / 2)
    try:
        x, y = screenPicKP[goodMatches[index].queryIdx].pt
        break
    except:
        init_num += 0.1

os.remove("screen.png")
print(f"图像在屏幕上的x坐标为:{x}")
print(f"图像在屏幕上的y坐标为:{y}")

封装为工具函数

import os
import pyautogui
import cv2

'''
注意:文件路径不能含有中文!!!文件名也不能含有中文!!!
picPath:要找的图的路劲
tress:kd树的数量,数值越大,精度越大,但耗时越高,建议设置为100足够
checks:节点检查数量,数值越大,精度越大,但耗时越高,建议设置为1000足够
maxInit:最大搜索系数,范围为大于0~1的小数,建议设置为0.5或者0.7。数值越小找的越精确,但是也可能找不到;数值越大找到的可能性更大,但也可能会找错。
'''
def FindPicSIFT(picPath,trees,checks,maxInit):
    #第一步:获取屏幕截图与目标图片数据
    pyautogui.screenshot(imageFilename="screen.png")
    screenPic = cv2.imread("screen.png")
    imgPath = r""+picPath
    myPic = cv2.imread(imgPath)

    #第二步:获取图像的关键点与描述符
    sift = cv2.SIFT_create()
    screenPicKP, screenPicDES = sift.detectAndCompute(screenPic, None)
    myPicKP, myPicDES = sift.detectAndCompute(myPic, None)

    #第三步:配置Flann匹配器
    indexParams = dict(algorithm=0, trees=trees)
    searchParams = dict(checks=checks)
    flann = cv2.FlannBasedMatcher(indexParams, searchParams)

    #第四步:进行图像匹配
    matches = flann.knnMatch(screenPicDES, myPicDES, k=2)
    matches = sorted(matches, key=lambda x: x[0].distance)

    #第五步:筛选最优匹配并返回坐标
    x, y = None, None
    max_init_num = maxInit
    init_num = 0.1

    while init_num <= max_init_num:
        goodMatches = []
        for m, n in matches:
            if m.distance < init_num * n.distance:
                goodMatches.append(m)
        index = int(len(goodMatches) / 2)
        try:
            x, y = screenPicKP[goodMatches[index].queryIdx].pt
            break
        except:
            init_num += 0.1

    #第六步:移除截图,并返回坐标
    os.remove("screen.png")
    return x,y

初步测试

初步测试显示该方法可以成功找到图像,并通过pyautogui.moveTo移动鼠标至找到的图像位置。

cb

模糊测试

即便对目标图片进行了模糊处理,SIFT算法依然能够有效识别,可见其识别能力还是很不错的,这里用的最大搜索系数为0.1

mh

我们的网络技术交流Q群:307531422

© 版权声明
THE END
喜欢就支持一下吧
分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称

    暂无评论内容