主頁 > 移動端開發 > Airtest影像識別測驗工具原理解讀&最佳實踐

Airtest影像識別測驗工具原理解讀&最佳實踐

2023-06-20 09:32:51 移動端開發

1 Airtest簡介

Airtest是一個跨平臺的、基于影像識別的UI自動化測驗框架,適用于游戲和App,支持平臺有Windows、Android和iOS,Airtest框架基于一種圖形腳本語言Sikuli,參考該框架后,不再需要一行行的寫代碼,通過截取按鈕或輸入框的圖片,用圖片組成測驗場景,這種方式學習成本低,簡單易上手,

2 Airtest實踐

APP接入流水執行緒序中,賽博平臺只支持air腳本,因此需要對京管家APP的UI自動化腳本進行的改造,如截圖可見,AirtestIDE的主界面由選單欄、快捷工具列和多個視窗組成,初始布局中的“設備視窗”是工具的設備連接互動區域,
air腳本生成步驟:

  1. 通過adb連接手機或模擬器
  2. 安裝應用APK
  3. 運行應用并截圖
  4. 模擬用戶輸入(點擊、滑動、按鍵)
  5. 卸載應用

通過以上步驟自動生成了 .air腳本,除錯程序中我們可以在IDE中運行代碼,支持多行運行以及單行運行,除錯通過后可在本地或服務器以命令列的方式運行腳本:
.air腳本運行方式:airtest run “path to your .air dir” —device Android
.air腳本生成報告的方式:airtest report “path to your .air dir”

3 Airtest定位方式決議

IDE的log查看視窗會時時列印腳本執行的日志,從中可以看出通過圖片決議執行位置的程序,下面就以touch方法為例,決議Airtest如何通過圖片獲取到元素位置從而觸發點擊操作,

@logwrap
def touch(v, times=1, **kwargs):
    """
    Perform the touch action on the device screen
    :param v: target to touch, either a ``Template`` instance or absolute coordinates (x, y)
    :param times: how many touches to be performed
    :param kwargs: platform specific `kwargs`, please refer to corresponding docs
    :return: finial position to be clicked, e.g. (100, 100)
    :platforms: Android, Windows, iOS
    """
    if isinstance(v, Template):
        pos = loop_find(v, timeout=ST.FIND_TIMEOUT)
    else:
        try_log_screen()
        pos = v
    for _ in range(times):
        G.DEVICE.touch(pos, **kwargs)
        time.sleep(0.05)
    delay_after_operation()
    return pos

click = touch  # click is alias of t

該方法通過loop_find獲取坐標,然后執行點擊操作 G.DEVICE.touch(pos, kwargs),接下來看loop_find如何根據模板轉換為坐標,

@logwrap
def loop_find(query, timeout=ST.FIND_TIMEOUT, threshold=None, interval=0.5, intervalfunc=None):
    """
    Search for image template in the screen until timeout
    Args:
        query: image template to be found in screenshot
        timeout: time interval how long to look for the image template
        threshold: default is None
        interval: sleep interval before next attempt to find the image template
        intervalfunc: function that is executed after unsuccessful attempt to find the image template
    Raises:
        TargetNotFoundError: when image template is not found in screenshot
    Returns:
        TargetNotFoundError if image template not found, otherwise returns the position where the image template has
        been found in screenshot
    """
    G.LOGGING.info("Try finding: %s", query)
    start_time = time.time()
    while True:
        screen = G.DEVICE.snapshot(filename=None, quality=ST.SNAPSHOT_QUALITY)
        if screen is None:
            G.LOGGING.warning("Screen is None, may be locked")
        else:
            if threshold:
                query.threshold = threshold
            match_pos = query.match_in(screen)
            if match_pos:
                try_log_screen(screen)
                return match_pos
        if intervalfunc is not None:
            intervalfunc()
        # 超時則raise,未超時則進行下次回圈:
        if (time.time() - start_time) > timeout:
            try_log_screen(screen)
            raise TargetNotFoundError('Picture %s not found in screen' % query)
        else:
            t

首先截取手機螢屏match_pos = query.match_in(screen),然后對比傳參圖片與截屏來獲取圖片所在位置match_pos = query.match_in(screen),接下來看match_in方法的邏輯:

def match_in(self, screen):
    match_result = self._cv_match(screen)
    G.LOGGING.debug("match result: %s", match_result)
    if not match_result:
        return None
    focus_pos = TargetPos().getXY(match_result, self.target_pos)
    return focus_pos

里面有個關鍵方法:match_result = self._cv_match(screen)

@logwrap
def _cv_match(self, screen):
    # in case image file not exist in current directory:
    ori_image = self._imread()
    image = self._resize_image(ori_image, screen, ST.RESIZE_METHOD)
    ret = None
    for method in ST.CVSTRATEGY:
        # get function definition and execute:
        func = MATCHING_METHODS.get(method, None)
        if func is None:
            raise InvalidMatchingMethodError("Undefined method in CVSTRATEGY: '%s', try 'kaze'/'brisk'/'akaze'/'orb'/'surf'/'sift'/'brief' instead." % method)
        else:
            if method in ["mstpl", "gmstpl"]:
                ret = self._try_match(func, ori_image, screen, threshold=self.threshold, rgb=self.rgb, record_pos=self.record_pos,
                                        resolution=self.resolution, scale_max=self.scale_max, scale_step=self.scale_step)
            else:
                ret = self._try_match(func, image, screen, threshold=self.threshold, rgb=self.rgb)
        if ret:
            break
    return ret

首先讀取圖片調整圖片尺寸,從而提升匹配成功率:
image = self._resize_image(ori_image, screen, ST.RESIZE_METHOD)
接下來是回圈遍歷匹配方法for method in ST.CVSTRATEGY,而ST.CVSTRATEGY的列舉值:

CVSTRATEGY = ["mstpl", "tpl", "surf", "brisk"]
if LooseVersion(cv2.__version__) > LooseVersion('3.4.2'):
    CVSTRATEGY = ["mstpl", "tpl", "sift", "brisk"]

func = MATCHING_METHODS.get(method, None),func可能的取值有mstpl、tpl、surf、shift、brisk,無論哪種模式都調到了共同的方法_try_math

if method in ["mstpl", "gmstpl"]:
    ret = self._try_match(func, ori_image, screen, threshold=self.threshold, rgb=self.rgb, record_pos=self.record_pos,
                            resolution=self.resolution, scale_max=self.scale_max, scale_step=self.scale_step)
else:
    ret = self._try_match(func, image, screen, threshold=self.threshold, rgb=self.rgb)

而_try_math方法中都是呼叫的func的方法find_best_result()

@staticmethod
def _try_match(func, *args, **kwargs):
    G.LOGGING.debug("try match with %s" % func.__name__)
    try:
        ret = func(*args, **kwargs).find_best_result()
    except aircv.NoModuleError as err:
        G.LOGGING.warning("'surf'/'sift'/'brief' is in opencv-contrib module. You can use 'tpl'/'kaze'/'brisk'/'akaze'/'orb' in CVSTRATEGY, or reinstall opencv with the contrib module.")
        return None
    except aircv.BaseError as err:
        G.LOGGING.debug(repr(err))
        return None
    else:
        return ret

以TemplateMatching類的find_best_result()為例,看一下內部邏輯如何實作,

@print_run_time
def find_best_result(self):
    """基于kaze進行影像識別,只篩選出最優區域."""
    """函式功能:找到最優結果."""
    # 第一步:校驗影像輸入
    check_source_larger_than_search(self.im_source, self.im_search)
    # 第二步:計算模板匹配的結果矩陣res
    res = self._get_template_result_matrix()
    # 第三步:依次獲取匹配結果
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    h, w = self.im_search.shape[:2]
    # 求取可信度:
    confidence = self._get_confidence_from_matrix(max_loc, max_val, w, h)
    # 求取識別位置: 目標中心 + 目標區域:
    middle_point, rectangle = self._get_target_rectangle(max_loc, w, h)
    best_match = generate_result(middle_point, rectangle, confidence)
    LOGGING.debug("[%s] threshold=%s, result=%s" % (self.METHOD_NAME, self.threshold, best_match))
    return best_match if confidence >= self.threshold else Non

重點看第二步:計算模板匹配的結果矩陣res,res = self._get_template_result_matrix()

def _get_template_result_matrix(self):
    """求取模板匹配的結果矩陣."""
    # 灰度識別: cv2.matchTemplate( )只能處理灰度圖片引數
    s_gray, i_gray = img_mat_rgb_2_gray(self.im_search), img_mat_rgb_2_gray(self.im_source)
    return cv2.matchTemplate(i_gray, s_gray, cv2.TM_CCOEFF_NORMED)

可以看到最終用的是openCV的方法,cv2.matchTemplate,那個優先匹配上就回傳結果,

4 總結

使用程序中可以發現Airtest框架有兩個缺點:一是對于背景透明的按鈕或者控制元件,識別難度大;二是無法獲取文本內容,但這一缺點可通過引入文字識別庫解決,如:pytesseract,
對不能用UI控制元件定位的部件,使用影像識別定位還是非常方便的,UI自動化腳本撰寫程序中可以將幾個框架結合使用,uiautomator定位速度較快,但對于flutter語言寫的頁面經常有一些部件無法定位,此時可以引入airtest框架用圖片進行定位,每個框架都有優劣勢,組合使用才能更好的實作目的,

作者:京東物流 范文君

來源:京東云開發者社區

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/555637.html

標籤:其他

上一篇:Android Studio中SQLite的使用,主要介紹sqlite插入和讀出圖片(ViewBinder)的操作方法

下一篇:返回列表

標籤雲
其他(161346) Python(38242) JavaScript(25508) Java(18249) C(15237) 區塊鏈(8271) C#(7972) AI(7469) 爪哇(7425) MySQL(7259) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4606) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2436) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) HtmlCss(1968) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • Airtest影像識別測驗工具原理解讀&最佳實踐

    Airtest是一個跨平臺的、基于影像識別的UI自動化測驗框架,適用于游戲和App,支持平臺有Windows、Android和iOS。Airtest框架基于一種圖形腳本語言Sikuli,參考該框架后,不再需要一行行的寫代碼,通過截取按鈕或輸入框的圖片,用圖片組成測驗場景,這種方式學習成本低,簡單易上... ......

    uj5u.com 2023-06-20 09:32:51 more
  • Android Studio中SQLite的使用,主要介紹sqlite插入和讀出圖片(View

    sqlite簡介 本人最近在寫一個小的安卓專案,開發app程序中用到了安卓自帶的sqlite。本文主要對sqlite圖片操作進行介紹,其他存入文本之類的操作和普通資料庫一樣,眾所周知,sqlite是一款輕型的資料庫,以下先簡單介紹一下sqlite,為后續做鋪墊,有了解的大佬可以跳過此部分: SQLi ......

    uj5u.com 2023-06-20 09:32:29 more
  • Android程式員成長之路

    # 一、Android程式員需要具備的素養 1. 應該熱愛學習Android知識 2. 具備基本的自學能力和解決問題的能力 3. 具備實踐能力 # 二、Android程式員最終需要熟練掌握的語言 1. Java(基本) 2. C/C++(進階) 3. Kotlin(基本) 4. Python(可選) ......

    uj5u.com 2023-06-20 09:32:23 more
  • Airtest影像識別測驗工具原理解讀&最佳實踐

    Airtest是一個跨平臺的、基于影像識別的UI自動化測驗框架,適用于游戲和App,支持平臺有Windows、Android和iOS。Airtest框架基于一種圖形腳本語言Sikuli,參考該框架后,不再需要一行行的寫代碼,通過截取按鈕或輸入框的圖片,用圖片組成測驗場景,這種方式學習成本低,簡單易上... ......

    uj5u.com 2023-06-20 09:32:16 more
  • Android-NDK開發——基本概念

    在Android開發中,有時候出于安全,性能,代碼共用的考慮,需要使用C/C++撰寫的庫。雖然在現代化工具鏈的支持下,這個作業的難度已經大大降低,但是畢竟萬事開頭難,初學者往往還是會遇到很多不可預測的問題。本篇就是基于此背景下寫的一份簡陋指南,希望能對剛開始撰寫C/C++庫的讀者有所幫助。同時為了盡 ......

    uj5u.com 2023-06-18 08:09:33 more
  • Android-JNI開發概論

    ### 什么是JNI開發 JNI的全稱是Java Native Interface,顧名思義,這是一種解決Java和C/C++相互呼叫的編程方式。**它其實只解決兩個方面的問題,怎么找到和怎么訪問。** 弄清楚這兩個話題,我們就學會了JNI開發。**需要注意的是,JNI開發只涉及到一小部分C/C++ ......

    uj5u.com 2023-06-18 08:09:29 more
  • Android-NDK開發——基本概念

    在Android開發中,有時候出于安全,性能,代碼共用的考慮,需要使用C/C++撰寫的庫。雖然在現代化工具鏈的支持下,這個作業的難度已經大大降低,但是畢竟萬事開頭難,初學者往往還是會遇到很多不可預測的問題。本篇就是基于此背景下寫的一份簡陋指南,希望能對剛開始撰寫C/C++庫的讀者有所幫助。同時為了盡 ......

    uj5u.com 2023-06-18 08:09:06 more
  • Android-JNI開發概論

    ### 什么是JNI開發 JNI的全稱是Java Native Interface,顧名思義,這是一種解決Java和C/C++相互呼叫的編程方式。**它其實只解決兩個方面的問題,怎么找到和怎么訪問。** 弄清楚這兩個話題,我們就學會了JNI開發。**需要注意的是,JNI開發只涉及到一小部分C/C++ ......

    uj5u.com 2023-06-18 08:09:00 more
  • Kotlin協程-從理論到實戰

    > 上一篇文章從理論上對Kotlin協程進行了部分說明,本文將在上一篇的基礎上,從實戰出發,繼續協程之旅。 ### 從源頭說起 在Kotlin中,要想使用協程,首先需要使用協程創建器創建,但還有個前提——協程作用域(`CoroutineScope`)。在早期的Kotlin實作中,協程創建器是一等函式 ......

    uj5u.com 2023-06-17 08:16:30 more
  • Kotlin協程-從一到多

    > 上一篇文章,我介紹了Kotlin協程的創建,使用,協作等內容。本篇將引入更多的使用場景,繼續帶你走進協程世界。 ### 使用協程處理異步資料流 常用編程語言都會內置對同一型別不同物件的資料集表示,我們通常稱之為容器類。不同的容器類適用于不同的使用場景。Kotlin的`Flow`就是在異步計算的需 ......

    uj5u.com 2023-06-17 08:16:25 more