前言:
在游戏脚本类辅助中,找图返回坐标是一个常见需求。现成工具和方法虽然不少,但要保证高精度并不容易。
比如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是一种快速匹配算法,适合大数据集的匹配。我们可以通过设置trees
和checks
参数来调整精度和速度的平衡:
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
移动鼠标至找到的图像位置。
模糊测试
即便对目标图片进行了模糊处理,SIFT算法依然能够有效识别,可见其识别能力还是很不错的,这里用的最大搜索系数为0.1
我们的网络技术交流Q群:307531422
暂无评论内容