原文由hakaboom发表于TesterHome社区,点击原文链接可与作者直接交流。
从18年开始,我接触了叉叉助手(平台已经被请喝茶了),通过图色识别,用来给常玩的游戏写挂机脚本,写了也有两三年.也算是我转行当游戏测试的理由.
去年11月,也是用了这身技术,混进了外包,薪资还不错,属于是混日子了,岗位是在发行,接触到很多游戏,因为接不了poco,到手只有apk,
日积月累,游戏越来越多,项目组却还是只有这点人.为了减轻自己的压力,就开始了UI自动化的不归路.
因为游戏引擎,是无法通过appium等框架去获取,如果不接入一些SDK,那么识别的方法只有图像识别.现在常见的开源框架
图像相关的常见方法:
UI自动化的核心在于查找元素,并且在什么位置.那么重点就会放在图像识别上.
基于深度学习的方案,需要大量的正负样本和标注工作,因此只能放弃.取而代之的是传统的识别方案.
在社区里、qq的测试群里就能发现,大多数人对传统图像识别的印象是:慢,不准.
今年过年前,去张江面试过一家游戏公司,也是发行公司,聊了一个多小时,聊下来他们的方案是airtest一种机型截一个图去做适配.我大受震撼.
总结下来图像识别的UI自动化难点:
那么我做了什么,项目就在这里:https://github.com/hakaboom/py_image_registration
目前也是在重构,重构完成后可能起个好名字:https://github.com/hakaboom/image_registration
一开始是参考了airtest的aircv部分,当时不想有那么多依赖,就拆出来了.
重构之后,通过对opencv一些api的封装,重新组织了构架和算法.目前效果感觉不错,也已经给airtest提了pr,后续也会推进合并.
建议版本可以是4.5.5
pip install opencv-python
pip install opencv-contrib-python
简单的理解: 用于描述图像特征的关键点
常见的特征点提取算法:
他们的好处是什么: 尺度和旋转不变性,说白了就是兼容不同分辨率、旋转、尺度的变换
速度排序: ORB(cuda)>SURF(cuda)>ORB>SURF>SIFT
效果排序(效果不止是特征点的数量,更重要的是特征点的质量): SIFT>ORB>SURF
import cv2
import time
from baseImage import Image, Rect
from image_registration.matching import SIFT
match = SIFT()
im_source = Image('tests/image/6.png')
im_search = Image('tests/image/4.png').crop(Rect(1498,68,50,56))
start = time.time()
result = match.find_all_results(im_source, im_search)
print(time.time() - start)
print(result)
img = im_source.clone()
for _ in result:
img.rectangle(rect=_['rect'], color=(0, 0, 255), thickness=3)
img.imshow('ret')
cv2.waitKey(0)
结果可以得到三个加号的位置
[
{'rect':
https://github.com/AirtestProject/Airtest/blob/d41737944738e651dd29564c29b88cc4c2e71e2e/airtest/aircv/keypoint_base.py#L133
1.获取特征点
2.匹配特征点
def match_keypoints(self, des_sch, des_src):
"""Match descriptors (特征值匹配)."""
# 匹配两个图片中的特征点集,k=2表示每个特征点取出2个最匹配的对应点:
return self.matcher.knnMatch(des_sch, des_src, k=2)
我们可以看到,这边k=2
代表,一个模板上的特征点,去匹配两个目标图像的特征点
3.筛选特征点
good = []
for m, n in matches:
if m.distance < self.FILTER_RATIO * n.distance:
good.append(m)
通过计算两个描述符之间的距离差,来筛选结果
4.根据透视变换或坐标计算,获取矩形,然后计算置信度
那么以上步骤会存在什么问题
n
个目标图片,那么还是k=2
的话,就会导致特征点数量变少distance
数值很高,但从结果上看,还是符合目标的,那么就意味着单纯根据距离去筛选特征点既然airtest存在这些问题,那么我做了什么改动,我把步骤一个个拆分
1.读取图片
from baseImage import Image
im_source = Image('tests/image/6.png')
这边用到了我另外一个库 https://github.com/hakaboom/base_image
主要的用处对opencv的图像数据进行格式和类型的转换,以及一些接口的包装
from baseImage import Image
from baseImage.constant import Place
Image(data='tests/image/0.png', place=Place.Ndarray) # 使用numpy
Image(data='tests/image/0.png', place=Place.Mat) # 使用Mat
Image(data='tests/image/0.png', place=Place.UMat) # 使用Umat
Image(data='tests/image/0.png', place=Place.GpuMat) # 使用cuda
2.创建特征点检测类
这边会有一些参数,除了threshold(过滤阈值)、rgb(是否通过rgb通道检测)以为,还有可以加入特征点提取器的一些配置,一般默认就好,具体可以查opencv文档
from image_registration.matching import SIFT
match = SIFT(threshold=0.8, rgb=True, nfeatures=50000)
3.识别
from image_registration.matching import SIFT
from baseImage import Image, Rect
im_source = Image('tests/image/6.png')
im_search = Image('tests/image/4.png').crop(Rect(1498,68,50,56))
match = SIFT(threshold=0.8, rgb=True, nfeatures=50000)
result = match.find_all_results(im_source, im_search)
4.解析下find_all_results
里做了什么,可以在image_registration.matching.keypoint.base
里找到基类
BaseKeypoint.create_matcher
image_registration.matching.keypoint.sift
def create_detector(self, **kwargs) -> cv2.SIFT:
nfeatures = kwargs.get('nfeatures', 0)
nOctaveLayers = kwargs.get('nOctaveLayers', 3)
contrastThreshold = kwargs.get('contrastThreshold', 0.04)
edgeThreshold = kwargs.get('edgeThreshold', 10)
sigma = kwargs.get('sigma', 1.6)
detector = cv2.SIFT_create(nfeatures=nfeatures, nOctaveLayers=nOctaveLayers, contrastThreshold=contrastThreshold,
edgeThreshold=edgeThreshold, sigma=sigma)
return detector
BaseKeypoint.create_detector
用于匹配模板和目标图片的特征点BFMatcher
: 暴力匹配, 总是尝试所有可能的匹配FlannBasedMatcher
: 算法更快,但是也能找到最近邻的匹配BaseKeypoint.get_keypoint_and_descriptor
BaseKeypoint.filter_good_point
cv2.DMatch
opencv的匹配关键点描述符类
distance
: 两个描述符之间的距离(欧氏距离等),越小表明匹配度越高imgIdx
: 训练图像索引queryIdx
: 查询描述符索引(对应模板图像)trainIdx
: 训练描述符索引(对应目标图像)cv2.Keypoint
opencv的特征点类
angle
: 特征点的旋转方向(0~360)class_id
: 特征点的聚类IDoctave
:特征点在图像金字塔的层级pt
: 特征点的坐标(x,y)response
: 特征点的响应强度size
: 特征点的直径大小distance
数值最小的点,为待匹配点A
待匹配点A
对应的queryIdx
和trainIdx
的keypoint(query_keypoint
,train_keypoint
,通过两个特征点的angle
可以计算出,特征点的旋转方向train_keypoint
与其他特征点的夹角,根据旋转不变性,我们可以根据模板上query_keypoint
的夹角,train_keypoint
的夹角query_keypoint
为原点,其他特征点的旋转角,还是根据旋转不变性,我们可以再去筛选以train_keypoint
原点,其他特征的的旋转角待匹配点A
)5.筛选完点集后,就可以进行匹配了,这边会有几种情况BaseKeypoint.extract_good_points
BaseKeypoint._handle_one_good_points
size
大小,获取尺度的变换BaseKeypoint._handle_two_good_points
BaseKeypoint._handle_three_good_points
BaseKeypoint._handle_many_good_points
BaseKeypoint._find_homography
,获取变换后的矩形顶点6.删除特征点
匹配完成后,如果识别成功,则删除目标区域的特征点,然后进入下一次循环
设备环境:
测试内容: 循环50次,获取目标图片和模板图片的特征点.
注:没有进行特征点的筛选, 特征点方法没有进行模板匹配计算置信度,因此实际速度会比测试的速度要慢
从图中可以看出cuda方法的速度最快,同时cpu的占用也小,原因是这部分算力给到了cuda
因为没有用代码获取cuda使用率,这边在任务管理器看的,只能说个大概数
还有其他的算法,opencv没有提供cuda或者是opencl的实现,只能用cpu加速
cv2.cuda.GpuMat
, cv2.UMat
调用cuda和opencl的算法.baseImage
可以快速的创建对应格式的图像from baseImage import Image
from baseImage.constant import Place
Image('tests/images/1.png', place=Place.GpuMat)
Image('tests/images/1.png', place=Place.UMat)
可以用cuda加速的识别方法, 需要调用其他的类函数,且图片格式需要是cv2.cuda.GpuMat
image_registration.matching.keypoint.orb.CUDA_ORB
image_registration.matching.template.matchTemplate.CudaMatchTemplate
可以用opencl加速的识别方法, 只需要传图像参数的时候,格式是UMat
,opencv会自动的调用opencl
方法
这边只讲了特征点获取/模板匹配的方法,在其他的图像处理函数中cuda
和opencl
也能有一定的加速,但是不如以上方法明显
Rect(372,69,537,583)
Rect(828,110,874,949)
,通过裁剪软件取得的范围是Rect(830,112,874,948)
from baseImage import Rect
from baseImage.coordinate import Anchor, screen_display_type, scale_mode_type
anchor = Anchor(
dev=screen_display_type(width=1280, height=720),
cur=screen_display_type(width=2532, height=1170, top=0, bottom=0, left=84, right=84),
orientation=1, mainPoint_scale_mode=scale_mode_type(), appurtenant_scale_mode=scale_mode_type()
)
rect = Rect(371, 68, 538, 584)
point = anchor.point(rect.x, rect.y, anchor_mode='Middle')
size = anchor.size(rect.width, rect.height)
print(Rect.create_by_point_size(point, size))
#
baseImage.utils.ssim
对场景进行识别与分类,然后去识别相应场景的特征点.用这样的方法去减少计算量界面1
, 界面2
,界面3
和一些通用控件
#6)备注
有其他疑问的话,可以在testerhome的游戏测试qq群里找到我581529846
原文由hakaboom发表于TesterHome社区,点击原文链接可与作者直接交流。
今日份的知识已摄入~
想了解更多前沿测试开发技术:欢迎关注「第十届MTSC大会·上海」>>>
1个主会场+12大专场,大咖云集精英齐聚
12个专场包括:
知乎、OpenHarmony、开源、游戏、酷家乐、音视频、客户端、服务端、数字经济、效能提升、质量保障、智能化测试