Airtest画像認識原理

11437 ワード

AirtestIDEは、ゲームやAppに適したプラットフォーム間UI自動化テストエディタです.
  • 自動化スクリプト録画、ワンタッチ再生、レポート表示、自動化テストプロセス
  • を簡単に実現
  • は画像認識に基づくAirtestフレームワークをサポートし、すべてのAndroidとWindowsゲーム
  • に適用される.
  • はUIコントロール検索に基づくPocoフレームワークをサポートし、Unity 3 d、Cocos 2 d、Android App
  • に適用される.
    一言でまとめると、PythonベースのUI自動化テストフレームワークAirtest(スクリーンショットでスクリプトを書く)とPoco(インタフェースUI要素でスクリプトを書く)の2つを発売し、提供したAirtestIDEで自動化テストスクリプトを迅速に作成することができます.
    本稿では,Airtestにおける画像認識についてコード走読を行い,画像認識原理の理解を深めることに重点を置く.
    1、仕事を準備して、ソースコードをダウンロードするhttps://github.com/AirtestProject/Airtest
    2、私たちは最も簡単なtouch方法から着手して、つまりある入ってきた画像をクリックするために、ソースコードはapiにあります.pyの中
    @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
        :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 touch

    この関数がクリック操作を行うのはG.DEVICE.touch(pos, **kwargs)です.
    posは画像マッチングで返される座標位置で、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) 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: time.sleep(interval)

    まず、携帯電話のスクリーンショットを取得し、スクリプトの入力画像と比較して一致する位置を取得します.match_pos = query.match_in(screen)
    cv.pyでTemplateクラスの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

    重要なのはself.cv_match(screen)
        @logwrap
        def _cv_match(self, screen):
            # in case image file not exist in current directory:
            image = self._imread()
            image = self._resize_image(image, screen, ST.RESIZE_METHOD)
            ret = None
            for method in ST.CVSTRATEGY:
                if method == "tpl":
                    ret = self._try_match(self._find_template, image, screen)
                elif method == "sift":
                    ret = self._try_match(self._find_sift_in_predict_area, image, screen)
                    if not ret:
                        ret = self._try_match(self._find_sift, image, screen)
                else:
                    G.LOGGING.warning("Undefined method in CV_STRATEGY: %s", method)
                if ret:
                    break
            return ret

    ここで入力された画像はズーム変化が必要であり,用例を書くときのスクリーンショットを変換して用例を走るときのスクリーンショットに変換し,マッチング成功率を向上させる.image = self._resize_image(image, screen, ST.RESIZE_METHOD)
    ここでのマッチング方法はST.CVSTRATEGY , Setting.py , を巡回します
        CVSTRATEGY = ["tpl", "sift"]
    , , 。 _find_sift_in_predict_area  _find_sift,
    cv.pyの_find_template  _find_sift
        def _find_template(self, image, screen):
            return aircv.find_template(screen, image, threshold=self.threshold, rgb=self.rgb)
    
        def _find_sift(self, image, screen):
            return aircv.find_sift(screen, image, threshold=self.threshold, rgb=self.rgb)

     
    3、まず
    aircv.find_具体的にはtemplate.py 
    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

    ポイントは_get_template_result_matrix
    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)

    ここでは、Airtestも自分ですごいアルゴリズムを研究していないので、直接使うOpenCVのテンプレートマッチング方法を見ることができます.
     
    4、次に別の方法を見る
    sift.py 
    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

    OpenCVの手法も用いられており、OpenCV 3であれば画像特徴点セットを探す手法が
    cv2.xfeatures2d.SIFT_create(edgeThreshold=10).detectAndCompute() 
     
    5.まとめると、最終的にはOpenCVの2つの方法、テンプレートマッチングと特徴マッチングが用いられる
    1.テンプレートマッチングcv 2.mathTemplate 2.  cv2.FlannBasedMatcher(index_params,search_params).knnMatch(des1,des2,k=2)
    どちらが優先的にマッチしたかは、直接結果に戻ります
     
    6、まとめ
  • 画像認識、uiコントロールで位置決めできない場所に対して、画像認識を使って位置決めし、いくつかのカスタムコントロール、H 5、ウィジェット、ゲームに対して、サポートすることができます.
  • は複数の端末をサポートしており、画像認識を使用するとandroidとiosを1セットのコードで互換性がありますよ.uiコントロールで位置決めするには互換性が必要です.
  • 欠点:背景が透明なボタンまたはコントロールに対して、識別の難易度が
  • 増加する