日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

谈谈如何使用 opencv 进行图像识别

發布時間:2023/12/2 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 谈谈如何使用 opencv 进行图像识别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文由hakaboom發表于TesterHome社區,點擊原文鏈接可與作者直接交流。

1)前言

從18年開始,我接觸了叉叉助手(平臺已經被請喝茶了),通過圖色識別,用來給常玩的游戲寫掛機腳本,寫了也有兩三年.也算是我轉行當游戲測試的理由.
去年11月,也是用了這身技術,混進了外包,薪資還不錯,屬于是混日子了,崗位是在發行,接觸到很多游戲,因為接不了poco,到手只有apk,
日積月累,游戲越來越多,項目組卻還是只有這點人.為了減輕自己的壓力,就開始了UI自動化的不歸路.

2)游戲UI自動化

因為游戲引擎,是無法通過appium等框架去獲取,如果不接入一些SDK,那么識別的方法只有圖像識別.現在常見的開源框架

  • 網易的Airtest,通過傳統識別進行自動化,還有airtestIDE可以簡單快速的編寫airtest代碼
  • 騰訊GameAISDK,通過深度學習進行自動化(沒用過,好久不維護了)
  • 阿里的SoloPi,主要功能是錄制、群控,有圖像匹配輔助
  • 圖像相關的常見方法:

  • 傳統的識別方法: 特征點、模板、輪廓
    • 特征點: SIFT, ORB
      • 下文會詳細講
    • 模板匹配: opencv的matchTemplate
      • 最簡單的方案,通過講模板在目標圖像中平移,找到最符合的目標
    • 輪廓: HALCON Shape-based Matching, Canny
      • 沒用過,寫不來,halcon的要花錢
  • 基于深度學習的方法:
    • 文字識別: PaddleOCR,tesseract
      • paddleOCR基本上開箱即用,但是對于游戲內的藝術字,還需要額外的訓練
    • 圖像分類: paddleClas
      • 沒有實際用過,感覺可以用在區分場景,然后去做更加詳細的識別.比如識別彈窗
    • 目標檢測: yolo
      • 之前很火的Fps外掛,基本就是靠這個去識別人體
  • UI自動化的核心在于查找元素,并且在什么位置.那么重點就會放在圖像識別上.
    基于深度學習的方案,需要大量的正負樣本和標注工作,因此只能放棄.取而代之的是傳統的識別方案.
    在社區里、qq的測試群里就能發現,大多數人對傳統圖像識別的印象是:慢,不準.
    今年過年前,去張江面試過一家游戲公司,也是發行公司,聊了一個多小時,聊下來他們的方案是airtest一種機型截一個圖去做適配.我大受震撼.
    總結下來圖像識別的UI自動化難點:

  • 識別慢
  • 識別結果不準確
  • 多分辨率不兼容性
  • 游戲UI更新,管理圖片庫的成本
  • 3)怎么解決

    那么我做了什么,項目就在這里:https://github.com/hakaboom/py_image_registration
    目前也是在重構,重構完成后可能起個好名字:https://github.com/hakaboom/image_registration

    一開始是參考了airtest的aircv部分,當時不想有那么多依賴,就拆出來了.
    重構之后,通過對opencv一些api的封裝,重新組織了構架和算法.目前效果感覺不錯,也已經給airtest提了pr,后續也會推進合并.

    安裝opencv-python

    建議版本可以是4.5.5

  • pypi上有編譯好的,但是只能用cpu方法:
    • pip install opencv-python
    • pip install opencv-contrib-python
  • 從源碼編譯,可以自定義更多的東西,比如增加cuda支持
    • 先從opencv倉庫克隆代碼
    • 剩下的看這里 https://github.com/hakaboom/py_image_registration/blob/master/doc/cuda_opencv.md
  • 什么是特征點

    簡單的理解: 用于描述圖像特征的關鍵點

    常見的特征點提取算法:

  • SIFT: 尺度不變特征變換. opencv只有cpu實現
  • SURF: surf的加速算法. opencv有cpu和cuda實現
  • ORB: 使用FAST特征檢測和BRIEF特征描述子. opencv有cpu和cuda實現
  • 他們的好處是什么: 尺度和旋轉不變性,說白了就是兼容不同分辨率、旋轉、尺度的變換
    速度排序: ORB(cuda)>SURF(cuda)>ORB>SURF>SIFT
    效果排序(效果不止是特征點的數量,更重要的是特征點的質量): SIFT>ORB>SURF

    例子

    • 6.png(2532x1170)iphone12pro上的截圖
    • 4.png(1922x1118 實際游戲渲染是1920x1080,多出來的是windows邊框)崩三桌面端的截圖, 裁剪了右上角的藍色加號區域當模板
    import cv2 import time from baseImage import Image, Rect from image_registration.matching import SIFTmatch = SIFT() im_source = Image('tests/image/6.png') im_search = Image('tests/image/4.png').crop(Rect(1498,68,50,56))start = time.time() result = match.find_all_results(im_source, im_search) print(time.time() - start) print(result) img = im_source.clone() for _ in result:img.rectangle(rect=_['rect'], color=(0, 0, 255), thickness=3) img.imshow('ret') cv2.waitKey(0)

    結果可以得到三個加號的位置

    [{'rect': <Rect [Point(1972.0, 33.0), Size[56.0, 58.0]], 'confidence': 0.9045119285583496}, {'rect': <Rect [Point(2331.0, 29.0), Size[52.0, 66.0]], 'confidence': 0.9046278297901154}, {'rect': <Rect [Point(1617.0, 30.0), Size[51.0, 64.0]], 'confidence': 0.9304171204566956} ]

    怎么進行匹配

    Airtest的aircv做了什么

    https://github.com/AirtestProject/Airtest/blob/d41737944738e651dd29564c29b88cc4c2e71e2e/airtest/aircv/keypoint_base.py#L133
    1.獲取特征點
    2.匹配特征點

    def match_keypoints(self, des_sch, des_src):"""Match descriptors (特征值匹配)."""# 匹配兩個圖片中的特征點集,k=2表示每個特征點取出2個最匹配的對應點:return self.matcher.knnMatch(des_sch, des_src, k=2)

    我們可以看到,這邊k=2代表,一個模板上的特征點,去匹配兩個目標圖像的特征點
    3.篩選特征點

    good = [] for m, n in matches:if m.distance < self.FILTER_RATIO * n.distance:good.append(m)

    通過計算兩個描述符之間的距離差,來篩選結果

    4.根據透視變換或坐標計算,獲取矩形,然后計算置信度

    那么以上步驟會存在什么問題

  • 在第二步,假設圖片中存在n個目標圖片,那么還是k=2的話,就會導致特征點數量變少
  • 在第三步,篩選的方法不太合理,實際debug中會發現,一些特征點即使distance數值很高,但從結果上看,還是符合目標的,那么就意味著單純根據距離去篩選特征點
    的方法是不靠譜的
  • 在第四步,獲取完特征點后,airtest的方式是,根據透視變換獲取目標的四個頂點坐標,計算出最小外接矩形.
    那么如果目標圖片存在旋轉/形變,那么最后獲取的圖片會裁剪到多余目標,造成置信度降低
  • 既然airtest存在這些問題,那么我做了什么改動,我把步驟一個個拆分

    我的特征點匹配

    1.讀取圖片

    from baseImage import Image im_source = Image('tests/image/6.png')

    這邊用到了我另外一個庫 https://github.com/hakaboom/base_image
    主要的用處對opencv的圖像數據進行格式和類型的轉換,以及一些接口的包裝

    • 使用place參數,修改數據格式
      • Ndarray: 格式為numpy.ndarray格式
      • Mat: 和numpy基本一致
      • Umat: python的綁定不多,沒有ndarray靈活,可以用于opencl加速
      • GpuMat: opencv的cuda格式,需要注意顯存消耗
    from baseImage import Image from baseImage.constant import PlaceImage(data='tests/image/0.png', place=Place.Ndarray) # 使用numpy Image(data='tests/image/0.png', place=Place.Mat) # 使用Mat Image(data='tests/image/0.png', place=Place.UMat) # 使用Umat Image(data='tests/image/0.png', place=Place.GpuMat) # 使用cuda

    2.創建特征點檢測類
    這邊會有一些參數,除了threshold(過濾閾值)、rgb(是否通過rgb通道檢測)以為,還有可以加入特征點提取器的一些配置,一般默認就好,具體可以查opencv文檔

    from image_registration.matching import SIFTmatch = SIFT(threshold=0.8, rgb=True, nfeatures=50000)

    3.識別

    from image_registration.matching import SIFT from baseImage import Image, Rectim_source = Image('tests/image/6.png') im_search = Image('tests/image/4.png').crop(Rect(1498,68,50,56))match = SIFT(threshold=0.8, rgb=True, nfeatures=50000) result = match.find_all_results(im_source, im_search)

    4.解析下find_all_results里做了什么,可以在image_registration.matching.keypoint.base里找到基類

    • 第一步: 創建特征點提取器BaseKeypoint.create_matcher
      例:image_registration.matching.keypoint.sift
    def create_detector(self, **kwargs) -> cv2.SIFT:nfeatures = kwargs.get('nfeatures', 0)nOctaveLayers = kwargs.get('nOctaveLayers', 3)contrastThreshold = kwargs.get('contrastThreshold', 0.04)edgeThreshold = kwargs.get('edgeThreshold', 10)sigma = kwargs.get('sigma', 1.6)detector = cv2.SIFT_create(nfeatures=nfeatures, nOctaveLayers=nOctaveLayers, contrastThreshold=contrastThreshold,edgeThreshold=edgeThreshold, sigma=sigma)return detector
    • 第二步: 創建特征點匹配器BaseKeypoint.create_detector用于匹配模板和目標圖片的特征點
      有兩種匹配器,
      • BFMatcher: 暴力匹配, 總是嘗試所有可能的匹配
      • FlannBasedMatcher: 算法更快,但是也能找到最近鄰的匹配
    • 第三步: 提取特征點BaseKeypoint.get_keypoint_and_descriptor
      用第一步創建的提取器去獲取特征點.ORB這種,還需要額外的去增加描述器.具體就看代碼實現吧.
    • 第四步: 匹配特征點
      用第二步創建的匹配器,獲取特征點集
    • 第五步: 篩選特征點BaseKeypoint.filter_good_point
      • cv2.DMatchopencv的匹配關鍵點描述符類
        • distance: 兩個描述符之間的距離(歐氏距離等),越小表明匹配度越高
        • imgIdx: 訓練圖像索引
        • queryIdx: 查詢描述符索引(對應模板圖像)
        • trainIdx: 訓練描述符索引(對應目標圖像)
      • cv2.Keypointopencv的特征點類
        • angle: 特征點的旋轉方向(0~360)
        • class_id: 特征點的聚類ID
        • octave:特征點在圖像金字塔的層級
        • pt: 特征點的坐標(x,y)
        • response: 特征點的響應強度
        • size: 特征點的直徑大小
          知道了這兩種類之后,我們就可以通過第四步獲取的特征點集進行篩選
      • 步驟1: 根據queryIdx的索引對列表進行重組,主要目的是,讓一個模板的特征點只可以對應一個目標的特征點
      • 步驟2: 根據distance的升序,對特征點集進行排序,提取出第一個點,也就是當前點集中,distance數值最小的點,為待匹配點A
      • 步驟3. 獲取點待匹配點A對應的queryIdx和trainIdx的keypoint(query_keypoint,train_keypoint,通過兩個特征點的angle可以計算出,特征點的旋轉方向
      • 步驟4. 計算train_keypoint與其他特征點的夾角,根據旋轉不變性,我們可以根據模板上query_keypoint的夾角,
        去篩選train_keypoint的夾角
      • 步驟5. 計算以query_keypoint為原點,其他特征點的旋轉角,還是根據旋轉不變性,我們可以再去篩選以train_keypoint原點,其他特征的的旋轉角
      • 最后,我們就可以獲取到,所有匹配的點、圖片旋轉角度、基準點(待匹配點A)

    5.篩選完點集后,就可以進行匹配了,這邊會有幾種情況BaseKeypoint.extract_good_points

    • 沒有特征點,其實肯定會有一個特征點
    • 有1組特征點BaseKeypoint._handle_one_good_points
      - 根據兩個特征點的size大小,獲取尺度的變換
      - 根據步驟4中返回的旋轉角度,獲取變換后的矩形頂點
      - 通過透視變換,獲取目標圖像區域,與目標圖像進行模板匹配,計算置信度
    • 有2組特征點BaseKeypoint._handle_two_good_points
      - 計算兩組特征點的兩點之間距離,獲取尺度的變換
      - 根據步驟4中返回的旋轉角度,獲取變換后的矩形頂點
      - 通過透視變換,獲取目標圖像區域,與目標圖像進行模板匹配,計算置信度
    • 有3組特征點BaseKeypoint._handle_three_good_points
      - 根據三個特征點組成的三角形面積,獲取尺度的變換
      - 根據步驟4中返回的旋轉角度,獲取變換后的矩形頂點
      - 通過透視變換,獲取目標圖像區域,與目標圖像進行模板匹配,計算置信度
    • 有大于等于4組特征點BaseKeypoint._handle_many_good_points
      - 使用單矩陣映射BaseKeypoint._find_homography,獲取變換后的矩形頂點
      - 通過透視變換,獲取目標圖像區域,與目標圖像進行模板匹配,計算置信度

    6.刪除特征點
    匹配完成后,如果識別成功,則刪除目標區域的特征點,然后進入下一次循環

    4)基準測試

    設備環境:

    • i7-9700k 3.6GHz
    • NvidiaRTX 3080Ti
    • cuda版本11.3
    • opencv版本:4.5.5-dev(從源碼編譯)

    測試內容: 循環50次,獲取目標圖片和模板圖片的特征點.

    注:沒有進行特征點的篩選, 特征點方法沒有進行模板匹配計算置信度,因此實際速度會比測試的速度要慢

    從圖中可以看出cuda方法的速度最快,同時cpu的占用也小,原因是這部分算力給到了cuda

    因為沒有用代碼獲取cuda使用率,這邊在任務管理器看的,只能說個大概數

    • cuda_orb: cuda占用在35%~40%左右
    • cuda_tpl: cuda占用在15%~20%左右
    • opencl_surf: cuda占用在13%左右
    • opencl_akaze: cuda占用在10%~15%左右

    還有其他的算法,opencv沒有提供cuda或者是opencl的實現,只能用cpu加速


    5)怎么優化速度

  • airtest慢的一個原因在于,只用了cpu計算.如果能釋放算力到gpu上,速度就會有成倍的增長.

    opencv已經給我們做好了很多接口.我們可以通過cv2.cuda.GpuMat, cv2.UMat調用cuda和opencl的算法.

    通過baseImage可以快速的創建對應格式的圖像
  • from baseImage import Image from baseImage.constant import PlaceImage('tests/images/1.png', place=Place.GpuMat) Image('tests/images/1.png', place=Place.UMat)

    可以用cuda加速的識別方法, 需要調用其他的類函數,且圖片格式需要是cv2.cuda.GpuMat

    • surf: 沒寫,下次再補
    • orb: 對應函數image_registration.matching.keypoint.orb.CUDA_ORB
    • matchTemplateimage_registration.matching.template.matchTemplate.CudaMatchTemplate

    可以用opencl加速的識別方法, 只需要傳圖像參數的時候,格式是UMat,opencv會自動的調用opencl方法

    • surf
    • orb
    • matchTemplate

    這邊只講了特征點獲取/模板匹配的方法,在其他的圖像處理函數中cuda和opencl也能有一定的加速,但是不如以上方法明顯

  • 從框架設計上進行加速.(可能只限于游戲應用,傳統app用不了)
    • 從游戲上講,我們預先知道一些控件,在屏幕中的坐標位置.分辨率進行轉換時,我們可以通過計算控件的位置,裁剪對應位置的圖像,通過模板匹配進行快速的識別.
      • 舉個例子,下面兩張圖,一個是1280x720下的截圖,一個是2532x1170下的截圖
      • 1280x720下郵件控件的坐標范圍是Rect(372,69,537,583)
      • 通過下面的計算方式,我們可以得出2532x1170下,范圍是Rect(828,110,874,949),通過裁剪軟件取得的范圍是Rect(830,112,874,948)
      • 具體的原理是利用了,引擎的縮放和錨點原理,反向求出坐標范圍.去適應一些黑邊,劉海的情況.
      • 求出范圍后,裁剪范圍的圖片,和模板去做匹配,就可以快速的識別一些固定位置的控件
    from baseImage import Rect from baseImage.coordinate import Anchor, screen_display_type, scale_mode_typeanchor = Anchor(dev=screen_display_type(width=1280, height=720),cur=screen_display_type(width=2532, height=1170, top=0, bottom=0, left=84, right=84),orientation=1, mainPoint_scale_mode=scale_mode_type(), appurtenant_scale_mode=scale_mode_type() )rect = Rect(371, 68, 538, 584) point = anchor.point(rect.x, rect.y, anchor_mode='Middle') size = anchor.size(rect.width, rect.height) print(Rect.create_by_point_size(point, size)) # <Rect [Point(828.9, 110.5), Size[874.2, 949.0]]


  • 建立模板庫,預先加載模板,得到屏幕圖片后,通過一些相似度計算baseImage.utils.ssim對場景進行識別與分類,然后去識別相應場景的特征點.用這樣的方法去減少計算量
    • 這邊其實有想法去擴展到深度學習,比如之前說的圖像分類.首先我們建立了一個很大的模板庫,可以拆分出來界面1, 界面2,界面3和一些通用控件
    • 再通過分類去獲得當前在什么界面,然后只識別這個界面的控件,達到減少計算量的作用

    #6)備注
    有其他疑問的話,可以在testerhome的游戲測試qq群里找到我581529846

    原文由hakaboom發表于TesterHome社區,點擊原文鏈接可與作者直接交流。


    今日份的知識已攝入~
    想了解更多前沿測試開發技術:歡迎關注「第十屆MTSC大會·上海」>>>
    1個主會場+12大專場,大咖云集精英齊聚
    12個專場包括:
    知乎、OpenHarmony、開源、游戲、酷家樂、音視頻、客戶端、服務端、數字經濟、效能提升、質量保障、智能化測試

    總結

    以上是生活随笔為你收集整理的谈谈如何使用 opencv 进行图像识别的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。