Python开发游戏自动化脚本(三)目标识别 您所在的位置:网站首页 java屏幕找图点击 Python开发游戏自动化脚本(三)目标识别

Python开发游戏自动化脚本(三)目标识别

2023-03-24 19:34| 来源: 网络整理| 查看: 265

大家好,我是沂阳,上一篇我们已经实现了后台窗口截屏,这一篇我们来探索如何实现目标识别。

目标识别的基本思想

我们可以把目标识别看作是在窗口截图中,寻找对应目标截图的坐标。从上一篇中我们可以知道,截图数据通常用一个矩阵来表示,那么目标识别,即从窗口截图这个大图中,识别出目标物体截图这个小图的坐标,最常见的做法,是从大图的左上角开始,将小图当作一个窗口沿着大图的宽或者高进行滑动,窗口的每次滑动,都可以得到一个子图,将这个子图与小图进行逐像素比较,匹配的个数越多,那么二者相同的概率就越高。

下面我们就以活动按钮为例,来实验一下上述方法是否有效,下面的所有图片,如果没有说明,则默认游戏窗口的截图大小为1334×750。我在我的台式机和笔记本上分别截取了两张活动按钮处的图片:

台式机截图pc_activity_btn.png笔记本截图laptop_activity_btn.png

我们可以直观的看到,如果两张截图的背景有很大的不同,这说明在目标识别中,我们需要排除背景的干扰,否则简单的使用逐像素匹配,是肯定行不通的。因此,我在笔记本上对活动按钮进行了五次截图,保留截图中的共同部分,得到了透明背景的活动按钮:

透明背景活动按钮截图transparent_activity_btn.png

接下来,我们只要比较非透明的部分,代码示例如下:

import cv2 import numpy as np # 读取图片,丢弃Alpha通道,转为灰度图 a = cv2.imread('pc_activity_btn.png', cv2.IMREAD_GRAYSCALE) # 读取图片,并保留Alpha通道 b = cv2.imread('transparent_activity_btn.png', cv2.IMREAD_UNCHANGED) # 取出Alpha通道 alpha = b[:,:,3] # 将透明点置0 a = cv2.bitwise_and(a, alpha) b = cv2.bitwise_and(cv2.cvtColor(b, cv2.COLOR_BGRA2GRAY), alpha) # 比较两张图,打印不相等的像素个数 print(np.count_nonzero(cv2.compare(a, b, cv2.CMP_NE))) # 运行结果: # 1

运行结果竟然是1?说明还是有一个像素点并不匹配,这是为什么呢?也许是我在制作透明图时,并未完整去除颜色会改变的部分?可能误打误撞,我经过查阅资料,得到这样一个事实:不同的设备,对同一个颜色的渲染是有误差的。基于这一事实,我们在比较两张图片时,必须考虑这一误差,否则我们的脚本,可能换一个设备就无法工作了。

优化方案——模板匹配

将误差纳入考量范围,同时兼顾大图中找小图的需要,我发现可以使用cv2.matchTemplate来实现,通过使用Alpha通道作为Mask可以排除透明部分,代码示例如下:

import cv2 import numpy as np # 读取图片,丢弃Alpha通道,转为灰度图 a = cv2.imread('pc_activity_btn.png', cv2.IMREAD_GRAYSCALE) # 读取图片,并保留Alpha通道 b = cv2.imread('transparent_activity_btn.png', cv2.IMREAD_UNCHANGED) # 取出Alpha通道 alpha = b[:,:,3] # 模板匹配,将alpha作为mask,TM_CCORR_NORMED方法的计算结果范围为[0, 1],越接近1越匹配 result = cv2.matchTemplate(a, b, cv2.TM_CCORR_NORMED, mask=alpha) # 获取结果中最大值和最小值以及他们的坐标 print(cv2.minMaxLoc(result)) # 运行结果: # (0.9999997615814209, 0.9999997615814209, (0, 0), (0, 0))

上面的代码中,我们是判断两张活动按钮截图的相似程度,下面尝试是否能在窗口截图中定位活动按钮的位置:

from ctypes import windll, byref, c_ubyte from ctypes.wintypes import RECT, HWND import numpy as np import time GetDC = windll.user32.GetDC CreateCompatibleDC = windll.gdi32.CreateCompatibleDC GetClientRect = windll.user32.GetClientRect CreateCompatibleBitmap = windll.gdi32.CreateCompatibleBitmap SelectObject = windll.gdi32.SelectObject BitBlt = windll.gdi32.BitBlt SRCCOPY = 0x00CC0020 GetBitmapBits = windll.gdi32.GetBitmapBits DeleteObject = windll.gdi32.DeleteObject ReleaseDC = windll.user32.ReleaseDC # 防止UI放大导致截图不完整 windll.user32.SetProcessDPIAware() def capture(handle: HWND): """窗口客户区截图 Args: handle (HWND): 要截图的窗口句柄 Returns: numpy.ndarray: 截图数据 """ # 获取窗口客户区的大小 r = RECT() GetClientRect(handle, byref(r)) width, height = r.right, r.bottom # 开始截图 dc = GetDC(handle) cdc = CreateCompatibleDC(dc) bitmap = CreateCompatibleBitmap(dc, width, height) SelectObject(cdc, bitmap) BitBlt(cdc, 0, 0, width, height, dc, 0, 0, SRCCOPY) # 截图是BGRA排列,因此总元素个数需要乘以4 total_bytes = width*height*4 buffer = bytearray(total_bytes) byte_array = c_ubyte*total_bytes GetBitmapBits(bitmap, total_bytes, byte_array.from_buffer(buffer)) DeleteObject(bitmap) DeleteObject(cdc) ReleaseDC(handle, dc) # 返回截图数据为numpy.ndarray return np.frombuffer(buffer, dtype=np.uint8).reshape(height, width, 4) if __name__ == "__main__": import cv2 handle = windll.user32.FindWindowW(None, "一梦江湖") # 截图时要保证游戏窗口的客户区大小是1334×750 image = capture(handle) # 转为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGRA2GRAY) # 读取图片,并保留Alpha通道 template = cv2.imread('transparent_activity_btn.png', cv2.IMREAD_UNCHANGED) # 取出Alpha通道 alpha = template[:,:,3] template = cv2.cvtColor(template, cv2.COLOR_BGRA2GRAY) # 模板匹配,将alpha作为mask,TM_CCORR_NORMED方法的计算结果范围为[0, 1],越接近1越匹配 result = cv2.matchTemplate(gray, template, cv2.TM_CCORR_NORMED, mask=alpha) # 获取结果中最大值和最小值以及他们的坐标 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) top_left = max_loc h, w = template.shape[:2] bottom_right = top_left[0] + w, top_left[1] + h # 在窗口截图中匹配位置画红色方框 cv2.rectangle(image, top_left, bottom_right, (0,0,255), 2) cv2.imshow('Match Template', image) cv2.waitKey()在窗口截图中尝试定位活动按钮

如图所示,准确的定位了活动按钮。我们不仅可以使用cv2.matchTemplate定位一个目标,这个链接展示了如何使用cv2.matchTemplate获取同一个目标的多个位置。

其他情况

还有一些复杂的情况我们并未考虑,比如最常见的分辨率问题,如果游戏窗口的分辨率并不是我们默认的1334×750,而模板匹配通过原理可以知道无法解决这个问题,最直观的解决方案,就是将模板图片,按照分辨率变化的比例进行缩放,再进行模板匹配,但是缩放后,图片会失真,又会对匹配造成影响。

对于这一问题如果有更好的方案或者思路,欢迎在评论区留言或者私信我讨论,非常感谢!

以上就是本篇的所有内容,下一篇我们继续探索如何实现后台键鼠操作。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有