批次影像識別的快速遍歷技巧

fishfish-yu發表於2024-08-22

此文章來源於專案官方公眾號:“AirtestProject”\
版權宣告:允許轉載,但轉載必須保留原連結;請勿用作商業或者非法用途

一、前言

最近,不少同學在 Q 群中頻繁提出疑問:在日常 UI 測試過程中,如何快速準確地識別頁面上的多個元素,或在日常測試中,如何高效地遍歷目標圖片列表,以確認畫面中是否包含特定元素?在官方交流 Q 群 2 群的 lincoln 同學給出了不錯的方法思路,我們也獲得了他的授權,現在我們一起來學習一下這個小技巧吧~

二、方法詳解

lincoln 同學提供了兩個方法函式,其中一個是區域性查詢,一個是多重查詢,我們就來看看他的一個函式邏輯是怎麼樣的吧。

程式碼邏輯的核心在於快速地識別目標影像。首先,將目標影像 (最好是特徵鮮明、尺寸小一些) 列表輸入Multiple_exists()函式。該函式透過迴圈執行截圖操作,每 0.2 秒進行一次,以最小化迴圈識別時間。接著將裝置螢幕截圖和目標影像傳遞給match_in_predict_area()函式,進行裁剪和搜尋。一旦找到匹配的影像,立即將座標資訊反饋給Multiple_exists()函式,並最終將影像編號和位置資訊返回至主函式,供進一步使用。

可以看到當日常在跑遊戲 ui 迴歸或 APP 迴歸的時候可以利用起來,當一個元素有多種 ui 表現形式或著需要判斷多個元素的情況時,我們可以參考 lincoln 同學提供的方式,修改成自己想要的效果。

下面是 lincoln 同學的方法函式原始碼,大家可以參考一下:

def match_in_predict_area(template, screen=None, rect=None):  # 區域性查詢
    # logger = Gvar.logger
    if screen is None:
        if G.DEVICE is None:
            raise Exception("G.DEVICE is none.")
    screen = G.DEVICE.snapshot()  # 截圖
    if screen is None:
        raise Exception("snapshot is none.")
    if rect is None:
        return template.match_in(screen)  # 如果沒有指定區域,在整個螢幕中查詢
    if not isinstance(rect, (list, tuple)):
        raise Exception("to crop a image, rect should be a list")
    else:
        # logger.debug("找到目標%s"%template.filename)
        predict_screen = aircv.crop_image(screen, rect)  # 圖片裁切,根據指定區域裁剪螢幕
        focus_pos = template.match_in(predict_screen)  # 在裁剪後的圖片中查詢模板
        if not focus_pos:
            return False  # 如果沒有找到匹配,返回False
        else:
            return focus_pos[0]+rect[0], focus_pos[1]+rect[1]  # 返回匹配位置,加上裁剪區域的偏移
    # end if
# end def

def Multiple_exists(targets,area=None,threshold=0.80,rgb=False,inti=5):#多重查詢

    # 根據裝置方向確定寬度和高度
    if (G.DEVICE.display_info['orientation']%2):
        width = G.DEVICE.display_info['height']
        height = G.DEVICE.display_info['width']
    else:
        width = G.DEVICE.display_info['width']
        height = G.DEVICE.display_info['height']
    #end if

    # 嘗試多次查詢目標
    for i in range(inti):
        #Gvar.logger.debug('第%d次查詢%s'%(i,targets))

        # 獲取全屏截圖
        fullScreen = G.DEVICE.snapshot()

        # 遍歷每個目標
        for target in targets:
            #print("查詢目標 %s"%target)
            if target :
                # 在預測區域內匹配目標,如果有元素固定出現的位置範圍,可以傳入,更進一步的減少識別時間
                focus_pos = match_in_predict_area(Template( '%d\%s.png'%(width, target),
                threshold=threshold, rgb=rgb),
                fullScreen, area)

            # 如果找到目標
            if focus_pos:
                #Gvar.logger.debug("找到目標 %s"%target)
                ref = targets.index(target)
                return ref,focus_pos
        #endif
    #end for

        # 每次查詢間隔0.2秒
        sleep(0.2)
#end for

    # 如果所有嘗試都失敗,返回-1和(-1,-1)
    return -1,(-1,-1)
#end def Multiple_exists

三、實際使用案例

透過上述所講的邏輯以及方式,我們這邊給大家提供一個小小的使用案例,透過識別遊戲畫面內的三個元素是否都存在,從而去判斷是否進入到我們需要的遊戲畫面。

參考程式碼如下:

# -*- encoding=utf8 -*-
__author__ = "Airtest"

import os
from airtest.core.api import *
from airtest.aircv import *

auto_setup(__file__)

from poco.drivers.android.uiautomation import AndroidUiautomationPoco
poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)

def Multiple_exists(targets, area=None, threshold=0.80, rgb=False, inti=5):
    # 定義一個函式,用於檢測多個目標是否存在
    # 引數說明:
    # targets: 目標列表
    # area: 搜尋區域,預設為None(全屏)
    # threshold: 匹配閾值,預設為0.80
    # rgb: 是否使用RGB匹配,預設為False
    # inti: 嘗試次數,預設為5次

    #新增找到的圖片位置資訊列表
    matches = []

    # 根據裝置方向確定螢幕寬高
    if (G.DEVICE.display_info['orientation'] % 2):
        width = G.DEVICE.display_info['height']
        height = G.DEVICE.display_info['width']
    else:
        width = G.DEVICE.display_info['width']
        height = G.DEVICE.display_info['height']

    # 在指定的嘗試次數內迴圈查詢目標
    for i in range(inti):
        # 獲取當前螢幕截圖
        fullScreen = G.DEVICE.snapshot()
        # 遍歷所有目標
        for target in targets:
            if target:
                # 構建目標圖片的完整路徑
                template_path = os.path.join(str(width), f"{target}.png")
                print(f"Trying to load template: {template_path}")
                # 在指定區域內查詢目標
                focus_pos = match_in_predict_area(Template(template_path, threshold=threshold, rgb=rgb), fullScreen, area)
            #若找到圖片則將當前圖片出現的位置傳入列表中
                if focus_pos:
                    matches.append((targets.index(target), focus_pos))
        # 如果所有目標都找到,立即返回結果
        if len(matches) == len(targets):
            return matches

        # 如果未找到所有目標,等待0.2秒後繼續下一次嘗試
        sleep(0.2)
    # 返回找到的所有匹配結果
    return matches       

def match_in_predict_area(template, screen=None, rect=None):  # 區域性找圖
    if screen is None:
        if G.DEVICE is None:
            raise Exception("G.DEVICE is none.")
    screen = G.DEVICE.snapshot()  # 獲取裝置螢幕截圖
    if screen is None:
        raise Exception("snapshot is none.")
    if rect is None:
        return template.match_in(screen)  # 如果沒有指定區域,在整個螢幕中查詢
    if not isinstance(rect, (list, tuple)):
        raise Exception("to crop a image, rect should be a list")
    else:
        # logger.debug("找到目標%s"%template.filename)
        predict_screen = aircv.crop_image(screen, rect)  # 裁剪指定區域的圖片
        focus_pos = template.match_in(predict_screen)  # 在裁剪後的圖片中查詢模板
        if not focus_pos:
            return False  # 如果沒有找到匹配,返回False
        else:
            return focus_pos[0]+rect[0], focus_pos[1]+rect[1]  # 返回匹配位置,加上裁剪區域的偏移

if __name__ == "__main__":   
    #開啟遊戲,去確認是否遊戲開始介面的元素已就位
    start_app("com.netease.dyll")
    sleep(1.0)
    poco(text="確定").click()
    touch([0.5,0.5])
    sleep(0.2)

    # 定義目標圖片的路徑列表
    targets = [r"D:/demo/pics.air/tpl1724135823600",r"D:/demo/pics.air/tpl1724135830426",r"D:/demo/pics.air/tpl1724135836266"]

    # 呼叫multiple_exists函式,查詢遊戲開始介面的圖片識別情況
    result = Multiple_exists(targets)

#     列印查詢結果
    for i in range(len(result)):
        print("圖片{}的位置是:{}".format(i+1,result[i]))

    # 判斷是否已進入遊戲開始介面
    if len(result) == len(targets) :
        print("確認已進入到遊戲的開始介面")

四、總結

本週推文我們分享了來自官方交流 Q 群 2 群的 lincoln 同學的一個快速遍歷找圖的方法函式,這裡再次感謝 lincoln 同學的分享,在函式內我們主要的一個減少識別時間的思路是透過截圖當前裝置畫面,並進行裁切判斷,從而避免整個裝置螢幕範圍內尋找元素。

如果大家有更多好用好玩的 Airtest 使用指令碼,也歡迎大家給我們投稿,同時如果大家在使用 Airtest 過程中有一些其他新的使用方式或者遇到了問題,又或者有任何想要深入瞭解的知識點,歡迎在官方交流群(526033840)裡告訴我們或者提交 issue。


AirtestIDE 下載:airtest.netease.com/\
Airtest 教程官網:airtest.doc.io.netease.com/\
搭建企業私有云服務:airlab.163.com/b2b

官方答疑 Q 群:526033840

暫無回覆。

相關文章