airtest 是网易开发的一款基于图象识别的 ui 自动化框架,其对元素操作的流程是先对元素进行截图,再和客户端当前界面截图进行对比,找出位置,执行点击操作。

这样的操作免除了传统的查找控件的繁杂,但是对元素的断言却不够精细,尤其在不仅仅判断元素是否存在,而且还要对文本的内容进行检查时,可以说无能为力了。

想要解决这个问题,可以使用回归传统的元素查找方式(airtest 内置了各端通过元素查找方式的 api),查找到元素,再判断他的文本属性,得到内容;但这样的方式第一需要在元素的映射方式上增加传统的例如 xpath 的关系,第二很多时候由于不规范开发等原因,元素的定位属性往往不容易确定,花费时间查找其实已经丢失了图像识别控件的便利性。

在实际的操作中(我的项目是 pc 端 + 安卓端双端互动的场景),我发现可以利用 airtest 本身的 api,外加 ocr 的 python 库,做到对元素内容的识别,从而解决这个问题。

大概的思路是,先用 airtest 本身的 api,将待识别内容截图扣下来,然后利用 ocr 的库进行对其内容识别。

理论上,如果你的设备分辨率不会变更,那么其实不需要用到 airtest 的 api,直接用固定的位置,通过 opencv 等图像处理的库进行抠图保存即可,但由于生产设备的分辨率不能保证,因此,需要一个相对的位置,对这个位置,airtest 里有直接可用的方法,这个方法也是其本身对分辨率兼容的办法。

airtest 通过你截图时记录的坐标,设备分辨率,来生成一个 recordpos,这个 pos 其实就是一个用来计算当分辨率不同时他的坐标偏移量。

具体怎么通过 recordpos 进行计算坐标,这里就不赘述了,主要说一下通过 recordpos 进行元素查找的方法,这个方法是 get_predict_area,有四个参数,分别是 record_pos(截取控件图片时的偏移量)、image_wh(空间的宽高)、image_resolution(截取图片时记录的设备分辨率)、screen_resolution(查找控件时所在设备的实际分辨率)。

该方法会返回一个四角坐标,我们把这个四角坐标用内置的图片处理方法,在设备及图上进行截图保存,就可以获取到待识别控件的图片啦,然后再用 ocr 库对这个图片进行识别,就可以得到控件的文本内容了。

示例代码如下(只适用于本人的硬件设备,用作参考):

dev = device()
stu_answer_num = r'./valid_pic/stu_answer_num.png'  # 答题区域的截图路径
interact_pic_path = r'./valid_pic/snap_interact.png'  # 互动界面的截图路径
interact_image = dev.snapshot(interact_pic_path)  # 把互动界面截图保存

from PIL import Image
import pytesseract
from airtest import aircv
from airtest.core.cv import Predictor

screen_resolution = aircv.get_resolution(interact_image)  # 获取互动界面截图的实际分辨率
# 动态创建类继承自Predictor,修改类参数,偏移值DEVIATION为0,用以获取精准的截图区域
predictor = type('Pos', (Predictor,), {'DEVIATION': 0})
xmin, ymin, xmax, ymax = predictor.get_predict_area(record_pos=(0.404, -0.213), image_wh=(265, 45),
                                                    image_resolution=(1919, 1040),
                                                    screen_resolution=screen_resolution)  # 获取答题区域的截图坐标
predict_area = aircv.crop_image(interact_image, (xmin, ymin, xmax, ymax))
aircv.imwrite(stu_answer_num, predict_area)  # 将待识别区域进行截图保存
answer_str = pytesseract.image_to_string(Image.open(stu_answer_num), 'chi_sim')  # 识别出的文字类似’全 班 共 参 与 : 1/1‘
print(answer_str)
if answer_str.find('参 与'):
    answer_str = answer_str.split(' ')[-1]  # 取1/1
    answer_num = int(answer_str.split('/')[0])  # 取答题人数
    return answer_num
return


↙↙↙阅读原文可查看相关链接,并与作者交流