谈谈如何使用 opencv 进行图像识别
原文由hakaboom發(fā)表于TesterHome社區(qū),點(diǎn)擊原文鏈接可與作者直接交流。
1)前言
從18年開(kāi)始,我接觸了叉叉助手(平臺(tái)已經(jīng)被請(qǐng)喝茶了),通過(guò)圖色識(shí)別,用來(lái)給常玩的游戲?qū)憭鞕C(jī)腳本,寫(xiě)了也有兩三年.也算是我轉(zhuǎn)行當(dāng)游戲測(cè)試的理由.
去年11月,也是用了這身技術(shù),混進(jìn)了外包,薪資還不錯(cuò),屬于是混日子了,崗位是在發(fā)行,接觸到很多游戲,因?yàn)榻硬涣藀oco,到手只有apk,
日積月累,游戲越來(lái)越多,項(xiàng)目組卻還是只有這點(diǎn)人.為了減輕自己的壓力,就開(kāi)始了UI自動(dòng)化的不歸路.
2)游戲UI自動(dòng)化
因?yàn)橛螒蛞?是無(wú)法通過(guò)appium等框架去獲取,如果不接入一些SDK,那么識(shí)別的方法只有圖像識(shí)別.現(xiàn)在常見(jiàn)的開(kāi)源框架
圖像相關(guān)的常見(jiàn)方法:
- 特征點(diǎn): SIFT, ORB
- 下文會(huì)詳細(xì)講
- 模板匹配: opencv的matchTemplate
- 最簡(jiǎn)單的方案,通過(guò)講模板在目標(biāo)圖像中平移,找到最符合的目標(biāo)
- 輪廓: HALCON Shape-based Matching, Canny
- 沒(méi)用過(guò),寫(xiě)不來(lái),halcon的要花錢(qián)
- 文字識(shí)別: PaddleOCR,tesseract
- paddleOCR基本上開(kāi)箱即用,但是對(duì)于游戲內(nèi)的藝術(shù)字,還需要額外的訓(xùn)練
- 圖像分類(lèi): paddleClas
- 沒(méi)有實(shí)際用過(guò),感覺(jué)可以用在區(qū)分場(chǎng)景,然后去做更加詳細(xì)的識(shí)別.比如識(shí)別彈窗
- 目標(biāo)檢測(cè): yolo
- 之前很火的Fps外掛,基本就是靠這個(gè)去識(shí)別人體
UI自動(dòng)化的核心在于查找元素,并且在什么位置.那么重點(diǎn)就會(huì)放在圖像識(shí)別上.
基于深度學(xué)習(xí)的方案,需要大量的正負(fù)樣本和標(biāo)注工作,因此只能放棄.取而代之的是傳統(tǒng)的識(shí)別方案.
在社區(qū)里、qq的測(cè)試群里就能發(fā)現(xiàn),大多數(shù)人對(duì)傳統(tǒng)圖像識(shí)別的印象是:慢,不準(zhǔn).
今年過(guò)年前,去張江面試過(guò)一家游戲公司,也是發(fā)行公司,聊了一個(gè)多小時(shí),聊下來(lái)他們的方案是airtest一種機(jī)型截一個(gè)圖去做適配.我大受震撼.
總結(jié)下來(lái)圖像識(shí)別的UI自動(dòng)化難點(diǎn):
3)怎么解決
那么我做了什么,項(xiàng)目就在這里:https://github.com/hakaboom/py_image_registration
目前也是在重構(gòu),重構(gòu)完成后可能起個(gè)好名字:https://github.com/hakaboom/image_registration
一開(kāi)始是參考了airtest的aircv部分,當(dāng)時(shí)不想有那么多依賴(lài),就拆出來(lái)了.
重構(gòu)之后,通過(guò)對(duì)opencv一些api的封裝,重新組織了構(gòu)架和算法.目前效果感覺(jué)不錯(cuò),也已經(jīng)給airtest提了pr,后續(xù)也會(huì)推進(jìn)合并.
安裝opencv-python
建議版本可以是4.5.5
- pip install opencv-python
- pip install opencv-contrib-python
- 先從opencv倉(cāng)庫(kù)克隆代碼
- 剩下的看這里 https://github.com/hakaboom/py_image_registration/blob/master/doc/cuda_opencv.md
什么是特征點(diǎn)
簡(jiǎn)單的理解: 用于描述圖像特征的關(guān)鍵點(diǎn)
常見(jiàn)的特征點(diǎn)提取算法:
他們的好處是什么: 尺度和旋轉(zhuǎn)不變性,說(shuō)白了就是兼容不同分辨率、旋轉(zhuǎn)、尺度的變換
速度排序: ORB(cuda)>SURF(cuda)>ORB>SURF>SIFT
效果排序(效果不止是特征點(diǎn)的數(shù)量,更重要的是特征點(diǎn)的質(zhì)量): SIFT>ORB>SURF
例子
- 6.png(2532x1170)iphone12pro上的截圖
- 4.png(1922x1118 實(shí)際游戲渲染是1920x1080,多出來(lái)的是windows邊框)崩三桌面端的截圖, 裁剪了右上角的藍(lán)色加號(hào)區(qū)域當(dāng)模板
結(jié)果可以得到三個(gè)加號(hào)的位置
[{'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} ]怎么進(jìn)行匹配
Airtest的aircv做了什么
https://github.com/AirtestProject/Airtest/blob/d41737944738e651dd29564c29b88cc4c2e71e2e/airtest/aircv/keypoint_base.py#L133
1.獲取特征點(diǎn)
2.匹配特征點(diǎn)
我們可以看到,這邊k=2代表,一個(gè)模板上的特征點(diǎn),去匹配兩個(gè)目標(biāo)圖像的特征點(diǎn)
3.篩選特征點(diǎn)
通過(guò)計(jì)算兩個(gè)描述符之間的距離差,來(lái)篩選結(jié)果
4.根據(jù)透視變換或坐標(biāo)計(jì)算,獲取矩形,然后計(jì)算置信度
那么以上步驟會(huì)存在什么問(wèn)題
的方法是不靠譜的
那么如果目標(biāo)圖片存在旋轉(zhuǎn)/形變,那么最后獲取的圖片會(huì)裁剪到多余目標(biāo),造成置信度降低
既然airtest存在這些問(wèn)題,那么我做了什么改動(dòng),我把步驟一個(gè)個(gè)拆分
我的特征點(diǎn)匹配
1.讀取圖片
from baseImage import Image im_source = Image('tests/image/6.png')這邊用到了我另外一個(gè)庫(kù) https://github.com/hakaboom/base_image
主要的用處對(duì)opencv的圖像數(shù)據(jù)進(jìn)行格式和類(lèi)型的轉(zhuǎn)換,以及一些接口的包裝
- 使用place參數(shù),修改數(shù)據(jù)格式
- Ndarray: 格式為numpy.ndarray格式
- Mat: 和numpy基本一致
- Umat: python的綁定不多,沒(méi)有ndarray靈活,可以用于opencl加速
- GpuMat: opencv的cuda格式,需要注意顯存消耗
2.創(chuàng)建特征點(diǎn)檢測(cè)類(lèi)
這邊會(huì)有一些參數(shù),除了threshold(過(guò)濾閾值)、rgb(是否通過(guò)rgb通道檢測(cè))以為,還有可以加入特征點(diǎn)提取器的一些配置,一般默認(rèn)就好,具體可以查opencv文檔
3.識(shí)別
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里找到基類(lèi)
- 第一步: 創(chuàng)建特征點(diǎn)提取器BaseKeypoint.create_matcher
例:image_registration.matching.keypoint.sift
- 第二步: 創(chuàng)建特征點(diǎn)匹配器BaseKeypoint.create_detector用于匹配模板和目標(biāo)圖片的特征點(diǎn)
有兩種匹配器,- BFMatcher: 暴力匹配, 總是嘗試所有可能的匹配
- FlannBasedMatcher: 算法更快,但是也能找到最近鄰的匹配
- 第三步: 提取特征點(diǎn)BaseKeypoint.get_keypoint_and_descriptor
用第一步創(chuàng)建的提取器去獲取特征點(diǎn).ORB這種,還需要額外的去增加描述器.具體就看代碼實(shí)現(xiàn)吧. - 第四步: 匹配特征點(diǎn)
用第二步創(chuàng)建的匹配器,獲取特征點(diǎn)集 - 第五步: 篩選特征點(diǎn)BaseKeypoint.filter_good_point
- cv2.DMatchopencv的匹配關(guān)鍵點(diǎn)描述符類(lèi)
- distance: 兩個(gè)描述符之間的距離(歐氏距離等),越小表明匹配度越高
- imgIdx: 訓(xùn)練圖像索引
- queryIdx: 查詢(xún)描述符索引(對(duì)應(yīng)模板圖像)
- trainIdx: 訓(xùn)練描述符索引(對(duì)應(yīng)目標(biāo)圖像)
- cv2.Keypointopencv的特征點(diǎn)類(lèi)
- angle: 特征點(diǎn)的旋轉(zhuǎn)方向(0~360)
- class_id: 特征點(diǎn)的聚類(lèi)ID
- octave:特征點(diǎn)在圖像金字塔的層級(jí)
- pt: 特征點(diǎn)的坐標(biāo)(x,y)
- response: 特征點(diǎn)的響應(yīng)強(qiáng)度
- size: 特征點(diǎn)的直徑大小
知道了這兩種類(lèi)之后,我們就可以通過(guò)第四步獲取的特征點(diǎn)集進(jìn)行篩選
- 步驟1: 根據(jù)queryIdx的索引對(duì)列表進(jìn)行重組,主要目的是,讓一個(gè)模板的特征點(diǎn)只可以對(duì)應(yīng)一個(gè)目標(biāo)的特征點(diǎn)
- 步驟2: 根據(jù)distance的升序,對(duì)特征點(diǎn)集進(jìn)行排序,提取出第一個(gè)點(diǎn),也就是當(dāng)前點(diǎn)集中,distance數(shù)值最小的點(diǎn),為待匹配點(diǎn)A
- 步驟3. 獲取點(diǎn)待匹配點(diǎn)A對(duì)應(yīng)的queryIdx和trainIdx的keypoint(query_keypoint,train_keypoint,通過(guò)兩個(gè)特征點(diǎn)的angle可以計(jì)算出,特征點(diǎn)的旋轉(zhuǎn)方向
- 步驟4. 計(jì)算train_keypoint與其他特征點(diǎn)的夾角,根據(jù)旋轉(zhuǎn)不變性,我們可以根據(jù)模板上query_keypoint的夾角,
去篩選train_keypoint的夾角 - 步驟5. 計(jì)算以query_keypoint為原點(diǎn),其他特征點(diǎn)的旋轉(zhuǎn)角,還是根據(jù)旋轉(zhuǎn)不變性,我們可以再去篩選以train_keypoint原點(diǎn),其他特征的的旋轉(zhuǎn)角
- 最后,我們就可以獲取到,所有匹配的點(diǎn)、圖片旋轉(zhuǎn)角度、基準(zhǔn)點(diǎn)(待匹配點(diǎn)A)
- cv2.DMatchopencv的匹配關(guān)鍵點(diǎn)描述符類(lèi)
5.篩選完點(diǎn)集后,就可以進(jìn)行匹配了,這邊會(huì)有幾種情況BaseKeypoint.extract_good_points
- 沒(méi)有特征點(diǎn),其實(shí)肯定會(huì)有一個(gè)特征點(diǎn)
- 有1組特征點(diǎn)BaseKeypoint._handle_one_good_points
- 根據(jù)兩個(gè)特征點(diǎn)的size大小,獲取尺度的變換
- 根據(jù)步驟4中返回的旋轉(zhuǎn)角度,獲取變換后的矩形頂點(diǎn)
- 通過(guò)透視變換,獲取目標(biāo)圖像區(qū)域,與目標(biāo)圖像進(jìn)行模板匹配,計(jì)算置信度 - 有2組特征點(diǎn)BaseKeypoint._handle_two_good_points
- 計(jì)算兩組特征點(diǎn)的兩點(diǎn)之間距離,獲取尺度的變換
- 根據(jù)步驟4中返回的旋轉(zhuǎn)角度,獲取變換后的矩形頂點(diǎn)
- 通過(guò)透視變換,獲取目標(biāo)圖像區(qū)域,與目標(biāo)圖像進(jìn)行模板匹配,計(jì)算置信度 - 有3組特征點(diǎn)BaseKeypoint._handle_three_good_points
- 根據(jù)三個(gè)特征點(diǎn)組成的三角形面積,獲取尺度的變換
- 根據(jù)步驟4中返回的旋轉(zhuǎn)角度,獲取變換后的矩形頂點(diǎn)
- 通過(guò)透視變換,獲取目標(biāo)圖像區(qū)域,與目標(biāo)圖像進(jìn)行模板匹配,計(jì)算置信度 - 有大于等于4組特征點(diǎn)BaseKeypoint._handle_many_good_points
- 使用單矩陣映射BaseKeypoint._find_homography,獲取變換后的矩形頂點(diǎn)
- 通過(guò)透視變換,獲取目標(biāo)圖像區(qū)域,與目標(biāo)圖像進(jìn)行模板匹配,計(jì)算置信度
6.刪除特征點(diǎn)
匹配完成后,如果識(shí)別成功,則刪除目標(biāo)區(qū)域的特征點(diǎn),然后進(jìn)入下一次循環(huán)
4)基準(zhǔn)測(cè)試
設(shè)備環(huán)境:
- i7-9700k 3.6GHz
- NvidiaRTX 3080Ti
- cuda版本11.3
- opencv版本:4.5.5-dev(從源碼編譯)
測(cè)試內(nèi)容: 循環(huán)50次,獲取目標(biāo)圖片和模板圖片的特征點(diǎn).
注:沒(méi)有進(jìn)行特征點(diǎn)的篩選, 特征點(diǎn)方法沒(méi)有進(jìn)行模板匹配計(jì)算置信度,因此實(shí)際速度會(huì)比測(cè)試的速度要慢
從圖中可以看出cuda方法的速度最快,同時(shí)cpu的占用也小,原因是這部分算力給到了cuda
因?yàn)闆](méi)有用代碼獲取cuda使用率,這邊在任務(wù)管理器看的,只能說(shuō)個(gè)大概數(shù)
- cuda_orb: cuda占用在35%~40%左右
- cuda_tpl: cuda占用在15%~20%左右
- opencl_surf: cuda占用在13%左右
- opencl_akaze: cuda占用在10%~15%左右
還有其他的算法,opencv沒(méi)有提供cuda或者是opencl的實(shí)現(xiàn),只能用cpu加速
5)怎么優(yōu)化速度
opencv已經(jīng)給我們做好了很多接口.我們可以通過(guò)cv2.cuda.GpuMat, cv2.UMat調(diào)用cuda和opencl的算法.
通過(guò)baseImage可以快速的創(chuàng)建對(duì)應(yīng)格式的圖像
可以用cuda加速的識(shí)別方法, 需要調(diào)用其他的類(lèi)函數(shù),且圖片格式需要是cv2.cuda.GpuMat
- surf: 沒(méi)寫(xiě),下次再補(bǔ)
- orb: 對(duì)應(yīng)函數(shù)image_registration.matching.keypoint.orb.CUDA_ORB
- matchTemplateimage_registration.matching.template.matchTemplate.CudaMatchTemplate
可以用opencl加速的識(shí)別方法, 只需要傳圖像參數(shù)的時(shí)候,格式是UMat,opencv會(huì)自動(dòng)的調(diào)用opencl方法
- surf
- orb
- matchTemplate
這邊只講了特征點(diǎn)獲取/模板匹配的方法,在其他的圖像處理函數(shù)中cuda和opencl也能有一定的加速,但是不如以上方法明顯
- 從游戲上講,我們預(yù)先知道一些控件,在屏幕中的坐標(biāo)位置.分辨率進(jìn)行轉(zhuǎn)換時(shí),我們可以通過(guò)計(jì)算控件的位置,裁剪對(duì)應(yīng)位置的圖像,通過(guò)模板匹配進(jìn)行快速的識(shí)別.
- 舉個(gè)例子,下面兩張圖,一個(gè)是1280x720下的截圖,一個(gè)是2532x1170下的截圖
- 1280x720下郵件控件的坐標(biāo)范圍是Rect(372,69,537,583)
- 通過(guò)下面的計(jì)算方式,我們可以得出2532x1170下,范圍是Rect(828,110,874,949),通過(guò)裁剪軟件取得的范圍是Rect(830,112,874,948)
- 具體的原理是利用了,引擎的縮放和錨點(diǎn)原理,反向求出坐標(biāo)范圍.去適應(yīng)一些黑邊,劉海的情況.
- 求出范圍后,裁剪范圍的圖片,和模板去做匹配,就可以快速的識(shí)別一些固定位置的控件
- 這邊其實(shí)有想法去擴(kuò)展到深度學(xué)習(xí),比如之前說(shuō)的圖像分類(lèi).首先我們建立了一個(gè)很大的模板庫(kù),可以拆分出來(lái)界面1, 界面2,界面3和一些通用控件
- 再通過(guò)分類(lèi)去獲得當(dāng)前在什么界面,然后只識(shí)別這個(gè)界面的控件,達(dá)到減少計(jì)算量的作用
#6)備注
有其他疑問(wèn)的話(huà),可以在testerhome的游戲測(cè)試qq群里找到我581529846
原文由hakaboom發(fā)表于TesterHome社區(qū),點(diǎn)擊原文鏈接可與作者直接交流。
今日份的知識(shí)已攝入~
想了解更多前沿測(cè)試開(kāi)發(fā)技術(shù):歡迎關(guān)注「第十屆MTSC大會(huì)·上海」>>>
1個(gè)主會(huì)場(chǎng)+12大專(zhuān)場(chǎng),大咖云集精英齊聚
12個(gè)專(zhuān)場(chǎng)包括:
知乎、OpenHarmony、開(kāi)源、游戲、酷家樂(lè)、音視頻、客戶(hù)端、服務(wù)端、數(shù)字經(jīng)濟(jì)、效能提升、質(zhì)量保障、智能化測(cè)試
總結(jié)
以上是生活随笔為你收集整理的谈谈如何使用 opencv 进行图像识别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: AppUI自动化中的图像识别的使用
- 下一篇: uni-app+flask 快速开发图像