前言
起因是实战中locate对个别定位会有偏差,导致一直识别错误
相应的基本知识推荐阅读:详细分析Python中的Pyautogui库(附Demo)
1. 基本知识
pyautogui.locate(
)函数用于在屏幕上定位指定图像或颜色的区域
作用:
- 定位屏幕上的指定图像或颜色区域
- 用于自动化任务,例如查找特定的应用程序窗口、按钮、图标等
函数如下:locate(needleImage, haystackImage, **kwargs)
: 在haystackImage中查找needleImage的位置
needleImage
: 要搜索的图像(可以是字符串文件名或PIL.Image对象)haystackImage
: 要搜索的图像或区域(可以是字符串文件名、PIL.Image对象或(left, top, width, height)元组)**kwargs
: 传递给locateAll的其他关键字参数
最终的结果返回值:如果找到匹配点,则返回第一个匹配点,否则返回None
对于**kwargs
的关键字参数,可以详细参考下locateAll
的函数源码,补充其参数如下:
grayscale
: 可选参数,指定是否将图像转换为灰度图像。默认为Nonelimit
: 可选参数,指定要返回的匹配数量的上限。默认为None,表示没有限制region
: 可选参数,指定在哪个区域内搜索图像(以(left, top, width, height)元组形式指定)。默认为None,表示整个图像step
: 可选参数,指定搜索时的步长。默认为1,表示逐像素搜索confidence
: 可选参数,置信度阈值。仅当安装了OpenCV时才可用。默认为None
该函数的返回值为 所有匹配的位置的Box对象
2. 源代码分析
源代码已加入注释:
def locate(needleImage, haystackImage, **kwargs):
# 将limit设置为1,仅返回第一个匹配点
kwargs['limit'] = 1
# 查找所有匹配点
points = tuple(locateAll(needleImage, haystackImage, **kwargs))
# 如果至少找到一个匹配点
if len(points) > 0:
return points[0]
else:
# 如果USE_IMAGE_NOT_FOUND_EXCEPTION为True,则抛出异常
if USE_IMAGE_NOT_FOUND_EXCEPTION:
raise ImageNotFoundException('无法定位图像。')
# 否则返回None
else:
return None
其中使用到了locateAll函数,再次给出源代码:
@requiresPillow
def _locateAll_python(needleImage, haystackImage, grayscale=None, limit=None, region=None, step=1, confidence=None):
"""
在haystackImage中查找所有的needleImage的位置。
Args:
needleImage: 要搜索的图像(可以是字符串文件名或PIL.Image对象)。
haystackImage: 要搜索的图像或区域(可以是字符串文件名、PIL.Image对象或(left, top, width, height)元组)。
grayscale: 可选参数,指定是否将图像转换为灰度图像。默认为None。
limit: 可选参数,指定要返回的匹配数量的上限。默认为None,表示没有限制。
region: 可选参数,指定在哪个区域内搜索图像(以(left, top, width, height)元组形式指定)。默认为None,表示整个图像。
step: 可选参数,指定搜索时的步长。默认为1,表示逐像素搜索。
confidence: 可选参数,置信度阈值。仅当安装了OpenCV时才可用。默认为None。
Returns:
生成器,用于生成所有匹配的位置的Box对象。
Raises:
NotImplementedError: 如果confidence参数在没有安装OpenCV时被使用。
ImageNotFoundException: 如果未找到匹配点且USE_IMAGE_NOT_FOUND_EXCEPTION为True时引发。
"""
if confidence is not None:
raise NotImplementedError('当没有安装OpenCV时,confidence关键字参数不可用。')
# 设置所有参数
if grayscale is None:
grayscale = GRAYSCALE_DEFAULT
needleFileObj = None
if isinstance(needleImage, (str, unicode)):
# 'image' 是一个文件名,加载图像对象
needleFileObj = open(needleImage, 'rb')
needleImage = Image.open(needleFileObj)
haystackFileObj = None
if isinstance(haystackImage, (str, unicode)):
# 'image' 是一个文件名,加载图像对象
haystackFileObj = open(haystackImage, 'rb')
haystackImage = Image.open(haystackFileObj)
if region is not None:
haystackImage = haystackImage.crop((region[0], region[1], region[0] + region[2], region[1] + region[3]))
else:
region = (0, 0) # 因为代码总是考虑区域,所以设置为0
if grayscale: # 如果启用了灰度模式,则将needle和haystack图像转换为灰度图像
needleImage = ImageOps.grayscale(needleImage)
haystackImage = ImageOps.grayscale(haystackImage)
else:
# 如果不使用灰度,则确保比较的是RGB图像,而不是RGBA图像
if needleImage.mode == 'RGBA':
needleImage = needleImage.convert('RGB')
if haystackImage.mode == 'RGBA':
haystackImage = haystackImage.convert('RGB')
# 设置一些常量
needleWidth, needleHeight = needleImage.size
haystackWidth, haystackHeight = haystackImage.size
needleImageData = tuple(needleImage.getdata())
haystackImageData = tuple(haystackImage.getdata())
needleImageRows = [needleImageData[y * needleWidth:(y + 1) * needleWidth] for y in range(needleHeight)]
needleImageFirstRow = needleImageRows[0]
assert len(needleImageFirstRow) == needleWidth, '计算出的第一行的宽度与图像宽度不同。'
assert [len(row) for row in needleImageRows] == [needleWidth] * needleHeight, 'needleImageRows的大小与原始图像大小不同。'
numMatchesFound = 0
# 注意: 经过在以下代码上运行测试/基准测试.py之后,似乎步长大于1并不会带来任何显著的性能改进。
# 由于使用大于1的步长会导致匹配不够精确,因此将其设置为1。
step = 1 # 硬编码步长为1,直到找到改进的方法。
if step == 1:
firstFindFunc = _kmp
else:
firstFindFunc = _steppingFind
for y in range(haystackHeight):
for matchx in firstFindFunc(needleImageFirstRow, haystackImageData[y * haystackWidth:(y + 1) * haystackWidth], step):
foundMatch = True
for searchy in range(1, needleHeight, step):
haystackStart = (searchy + y) * haystackWidth + matchx
if needleImageData[searchy * needleWidth:(searchy + 1) * needleWidth] != haystackImageData[
haystackStart:haystackStart + needleWidth]:
foundMatch = False
break
if foundMatch:
# 找到匹配,报告匹配区域在haystack中的x、y、宽度、高度。
numMatchesFound += 1
yield Box(matchx + region[0], y + region[1], needleWidth, needleHeight)
if limit is not None and numMatchesFound >= limit:
# 达到限制。关闭文件句柄。
if needleFileObj is not None:
needleFileObj.close()
if haystackFileObj is not None:
haystackFileObj.close()
return
# 没有限制或限制未达到,但是无论如何都要关闭文件句柄。
if needleFileObj is not None:
needleFileObj.close()
if haystackFileObj is not None:
haystackFileObj.close()
if numMatchesFound == 0:
if USE_IMAGE_NOT_FOUND_EXCEPTION:
raise ImageNotFoundException('无法定位图像。')
else:
return
3. Demo
(×)错误版: (这里使用默认参数,有可能会错误,建议加上置信度参数)
我们先给一个Demo:(截图区域为当前,jd.jpg为当前区域的某一区域)
import pyautogui
target_image = 'target_image.png'
# 截取屏幕指定区域并保存为图像文件
screenshot = pyautogui.screenshot('shot.png')
print("截图已保存为 'shot.png'")
target_position = pyautogui.locate(target_image, screenshot)
print(target_position)
if target_position is not None:
center_x = target_position.left + (target_position.width / 2)
center_y = target_position.top + (target_position.height / 2)
print("目标图像中心坐标:", center_x, center_y)
结果输出如下:
在PyAutoGUI中,locate()函数用于在屏幕截图(screenshot)中查找目标图像(target_image)的位置。它默认使用图像的灰度模式,并且没有指定置信度阈值时,默认置信度阈值为None。这意味着如果未指定置信度阈值,则需要确保目标图像与屏幕截图非常相似,才能成功定位。如果目标图像与屏幕截图不是完全相同,例如在颜色、尺寸或旋转方面有细微差异,那么可能会导致无法成功定位,此时返回的位置将为None。
在没有设置confidence参数的情况下,默认的阈值为None,这意味着需要与完全相同的图像匹配。而当设置了confidence参数时,PyAutoGUI会尝试寻找与目标图像非常相似的区域,只要相似度超过了设置的置信度阈值,就会返回匹配的位置
(√)正确版:
截图如下:
import pyautogui
target_image = 'target_image.png'
# 截取屏幕指定区域并保存为图像文件
screenshot = pyautogui.screenshot('shot.png')
print("截图已保存为 'shot.png'")
# 注意此处修改的位置
target_position = pyautogui.locate(target_image, screenshot,confidence=0.68)
print(target_position)
if target_position is not None:
center_x = target_position.left + (target_position.width / 2)
center_y = target_position.top + (target_position.height / 2)
print("目标图像中心坐标:", center_x, center_y)
截图如下:
对于其他的参数还有region 以及 grayscale等
以下虽然可以执行,但是对于作者来说输出为None
- 默认参数:
target_position = pyautogui.locate('target_image.png', screenshot)
- 区域参数:
target_position = pyautogui.locate('target_image.png', screenshot, region=(100, 100, 800, 600))
- 灰度参数:
target_position = pyautogui.locate('target_image.png', screenshot, grayscale=True)
确保confidence可选参数在的话,在额外加其他可选参数,结果会正常:
# 注意此处修改的位置
target_position = pyautogui.locate(target_image, screenshot, confidence = 0.68 ,region=(1264, 32, 86, 24))
截图如下所示: