airTest是國內網易自研的一套基於圖像識別進行UI自動化測試的框架,目前已經可以支持andriod,ios,web端的UI測試,在google開發者大會上得到了google的高度認可。
最近在學習使用這個框架,首先來了解下他的原理
一、 airTest框架的構成
airTest ---這里指的是airTest核心源代碼
airTestIDE ---集成的開發環境,可以快速開發airTest腳本 (注意它自帶了python 3.X版本,不能直接使用本地的python庫)
Poco ---UI 控件檢索工具,支持各種客戶端
二、 airTest是如何進行識別的?
眾所周知,airTest的最大亮點就是通過圖像識別進行UI自動化測試,那么airTest的圖像識別是如何進行的呢?
1. 獲取屏幕截圖
2. 根據用戶傳遞的圖片與截圖進行對比
傳入的圖像需要進行縮放變化,寫用例時候的截圖進行變換后轉換成跑用例時候的截圖,以提高匹配成功率
image = self._resize_image(image, screen, ST.RESIZE_METHOD)
3. 圖像匹配,這里用的是openCV的模版匹配和特征匹配
3.1.模板匹配 cv2.mathTemplate
def find_template(im_source, im_search, threshold=0.8, rgb=False): """函數功能:找到最優結果.""" # 第一步:校驗圖像輸入 check_source_larger_than_search(im_source, im_search) # 第二步:計算模板匹配的結果矩陣res res = _get_template_result_matrix(im_source, im_search) # 第三步:依次獲取匹配結果 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) h, w = im_search.shape[:2] # 求取可信度: confidence = _get_confidence_from_matrix(im_source, im_search, max_loc, max_val, w, h, rgb) # 求取識別位置: 目標中心 + 目標區域: middle_point, rectangle = _get_target_rectangle(max_loc, w, h) best_match = generate_result(middle_point, rectangle, confidence) LOGGING.debug("threshold=%s, result=%s" % (threshold, best_match)) return best_match if confidence >= threshold else None def _get_template_result_matrix(im_source, im_search): """求取模板匹配的結果矩陣.""" # 灰度識別: cv2.matchTemplate( )只能處理灰度圖片參數 s_gray, i_gray = img_mat_rgb_2_gray(im_search), img_mat_rgb_2_gray(im_source) return cv2.matchTemplate(i_gray, s_gray, cv2.TM_CCOEFF_NORMED)
3.2.特征匹配 cv2.FlannBasedMatcher(index_params,search_params).knnMatch(des1,des2,k=2)
def find_sift(im_source, im_search, threshold=0.8, rgb=True, good_ratio=FILTER_RATIO): """基於sift進行圖像識別,只篩選出最優區域.""" # 第一步:檢驗圖像是否正常: if not check_image_valid(im_source, im_search): return None # 第二步:獲取特征點集並匹配出特征點對: 返回值 good, pypts, kp_sch, kp_src kp_sch, kp_src, good = _get_key_points(im_source, im_search, good_ratio) # 第三步:根據匹配點對(good),提取出來識別區域: if len(good) == 0: # 匹配點對為0,無法提取識別區域: return None elif len(good) == 1: # 匹配點對為1,可信度賦予設定值,並直接返回: return _handle_one_good_points(kp_src, good, threshold) if ONE_POINT_CONFI >= threshold else None elif len(good) == 2: # 匹配點對為2,根據點對求出目標區域,據此算出可信度: origin_result = _handle_two_good_points(im_source, im_search, kp_src, kp_sch, good) if isinstance(origin_result, dict): return origin_result if ONE_POINT_CONFI >= threshold else None else: middle_point, pypts, w_h_range = _handle_two_good_points(im_source, im_search, kp_src, kp_sch, good) elif len(good) == 3: # 匹配點對為3,取出點對,求出目標區域,據此算出可信度: origin_result = _handle_three_good_points(im_source, im_search, kp_src, kp_sch, good) if isinstance(origin_result, dict): return origin_result if ONE_POINT_CONFI >= threshold else None else: middle_point, pypts, w_h_range = _handle_three_good_points(im_source, im_search, kp_src, kp_sch, good) else: # 匹配點對 >= 4個,使用單矩陣映射求出目標區域,據此算出可信度: middle_point, pypts, w_h_range = _many_good_pts(im_source, im_search, kp_sch, kp_src, good) # 第四步:根據識別區域,求出結果可信度,並將結果進行返回: # 對識別結果進行合理性校驗: 小於5個像素的,或者縮放超過5倍的,一律視為不合法直接raise. _target_error_check(w_h_range) # 將截圖和識別結果縮放到大小一致,准備計算可信度 x_min, x_max, y_min, y_max, w, h = w_h_range target_img = im_source[y_min:y_max, x_min:x_max] resize_img = cv2.resize(target_img, (w, h)) confidence = _cal_sift_confidence(im_search, resize_img, rgb=rgb) best_match = generate_result(middle_point, pypts, confidence) print("[aircv][sift] threshold=%s, result=%s" % (threshold, best_match)) return best_match if confidence >= threshold else None # 如何找到特征點集 def _get_key_points(im_source, im_search, good_ratio): """根據傳入圖像,計算圖像所有的特征點,並得到匹配特征點對.""" # 准備工作: 初始化sift算子 sift = _init_sift() # 第一步:獲取特征點集,並匹配出特征點對: 返回值 good, pypts, kp_sch, kp_src kp_sch, des_sch = sift.detectAndCompute(im_search, None) kp_src, des_src = sift.detectAndCompute(im_source, None) # When apply knnmatch , make sure that number of features in both test and # query image is greater than or equal to number of nearest neighbors in knn match. if len(kp_sch) < 2 or len(kp_src) < 2: raise NoSiftMatchPointError("Not enough feature points in input images !") # 匹配兩個圖片中的特征點集,k=2表示每個特征點取出2個最匹配的對應點: matches = FLANN.knnMatch(des_sch, des_src, k=2) good = [] # good為特征點初選結果,剔除掉前兩名匹配太接近的特征點,不是獨特優秀的特征點直接篩除(多目標識別情況直接不適用) for m, n in matches: if m.distance < good_ratio * n.distance: good.append(m) # good點需要去除重復的部分,(設定源圖像不能有重復點)去重時將src圖像中的重復點找出即可 # 去重策略:允許搜索圖像對源圖像的特征點映射一對多,不允許多對一重復(即不能源圖像上一個點對應搜索圖像的多個點) good_diff, diff_good_point = [], [[]] for m in good: diff_point = [int(kp_src[m.trainIdx].pt[0]), int(kp_src[m.trainIdx].pt[1])] if diff_point not in diff_good_point: good_diff.append(m) diff_good_point.append(diff_point) good = good_diff return kp_sch, kp_src, good # sift對象 def _init_sift(): """Make sure that there is SIFT module in OpenCV.""" if cv2.__version__.startswith("3."): # OpenCV3.x, sift is in contrib module, you need to compile it seperately. try: sift = cv2.xfeatures2d.SIFT_create(edgeThreshold=10) except: print("to use SIFT, you should build contrib with opencv3.0") raise NoSIFTModuleError("There is no SIFT module in your OpenCV environment !") else: # OpenCV2.x, just use it. sift = cv2.SIFT(edgeThreshold=10) return sift
以上兩個匹配算法,哪個優先匹配上了,就直接返回結果
三、airTest的簡單腳本運行機制
3.1 打開ariTestIDE,編寫一個腳本,默認命名為: untitled.air
3.2 連接你的設備
3.3 編寫一個簡單的腳本
3.4 運行腳本
腳本實際顯示的信息如下:
touch(Template(r"tpl1551777086787.png", record_pos=(0.379, 0.922), resolution=(1080, 2160))) wait(Template(r"tpl1551778382115.png", record_pos=(-0.003, -0.551), resolution=(1080, 2160))) touch(Template(r"tpl1551775745377.png", record_pos=(-0.007, -0.547), resolution=(1080, 2160))) text("cmq00002@qq.com")
其中的record_pos為 【計算坐標對應的中點偏移值相對於分辨率的百分比】;【tpl1551777086787.png】為你在編寫腳本時候截圖的小圖片
官網: http://airtest.netease.com/
官方API文檔: https://airtest.readthedocs.io/zh_CN/latest/index.html
參考:https://blog.csdn.net/tianmi1988/article/details/84798720