1. 程式人生 > 實用技巧 >通過對比圖片RGB值返回目標圖片位置的技術調研報告

通過對比圖片RGB值返回目標圖片位置的技術調研報告

一、相關術語

縮寫 全稱 描述
opencv Open Source Computer Vision Library 是一個開源的計算機視覺庫
RGB 紅(R)、綠(G)、藍(B) RGB即是代表紅、綠、藍三個通道的顏色,

強度值為0~255,比如:白色(0 0 0),黑色(255 255 255)

二、問題

在UI自動化測試過程中,核心內容包含了元素的定位和操作,以及斷言。傳統web自動化測試的斷言方法大多是採用獲取文字的方法,但對於成都多媒體&文管自動化測試,桌面斷言方法,一般是採用影象識別、dbus屬性獲取的方法,這裡面用的最多的是影象識別的斷言方法,即判斷某個影象在螢幕中是否存在。

目前,在我們自動化測試專案是採用opencv的模板匹配的技術,但opencv的python庫在ARM和MIPS兩個平臺上無法安裝,所以,為了實現我們自動化測試覆蓋全平臺,需要在ARM和MIPS上實現影象識別的功能。

三、現狀

網上能查到的資料,大多是通過原始碼編譯安裝的方式進行(可以參考“七、參考資料”的1~3),但是在ARM上使用原始碼編譯的方法安裝後,發現只支援python2,在python3無法使用,因為我們自動化測試專案是基於python3編寫的,因此無法應用到我們專案中。

另外,使用原始碼編譯安裝的耗時比較長,搭建自動化測試環境的時間會變得不可接受,而且自動化測試會頻繁的更換系統映象,如果採用原始碼編譯安裝,會耗費大量的時間在測試環境的搭建上,因此這個方案不可行。

那麼,我們是否可以通過其他方案實現影象識別?

四、技術方案

在自動化測試的過程中,通常將需要識別的按鈕或控制元件區域擷取為一個小圖,然後在整個螢幕中對小圖進行匹配。為了實現識別影象的目的,我們可以通過將圖片的每個畫素的RGB值,與整個螢幕中的RGB進行對比,如果小圖上的RGB值與對應大圖位置的RGB都相等,則匹配成功,即可返回小圖在大圖中的中心座標點。

如下圖所示:

1.整體設計

  • 分別擷取整個螢幕和目標小圖為png格式的圖片,並儲存。擷取的螢幕圖片稱為“大圖”,目標圖片稱為“小圖”。
  • 分別讀取大圖和小圖的RGB值。
  • 將小圖在大圖中進行比對,大圖的畫素座標從(0,0)到(1920,1080),每移動一個畫素,就比對一次。當小圖的所有RGB值在大圖中匹配到,則返回小圖的在大圖中的中心座標。

1.1 擷取目標小圖和整個螢幕的大圖

(1)目標小圖採用UOS多媒體應用-截圖錄屏工具擷取為png格式,並儲存到本地路徑。使用python三方庫pillow開啟。

from PIL import Image

small_pic = Image.open('small_pic.png')

(2)螢幕的大圖是在隨時變化的,需要在進行匹配的時候擷取,所以只能通過程式碼進行實現。匯入python三方庫pyscreenshot,擷取整個螢幕的大圖,解析度為(1920, 1080)。

import pyscreenshot

big_pic = pyscreenshot.grab() # grab()會擷取整個螢幕的圖片,賦值給big_pic變數儲存在記憶體中

(3)同時可以獲取到小圖和大圖的寬和高的px

small_pic.width # 小圖的寬
small_pic.height # 小圖的高
big_pic.width # 小圖的寬
big_pic.height # 小圖的高

1.2 讀取小圖和大圖的RGB值

(1)小圖的RGB值

small_data = small_pic.load() # load()會將圖片的RGB值獲取到,資料格式為一個二維列表,賦值給一個變數small_data。

(2)大圖的RGB值

big_data = big_pic.load()

1.3 將小圖與大圖的RGB值進行匹配

(1)匹配從大圖的座標(0, 0)開始匹配,匹配所有小圖為(0,0)—(small_pic.width,small_pic.height)

(2)如果在大圖的(0, 0)對應的RGB值不相等,則移動到下一個座標點(1,0),同樣匹配所有小圖為(0,0)—(small_pic.width,small_pic.height)

(3)按照這樣的規律將這一行每移動一個座標點,都將小圖所有的RGB與對應大圖的值進行匹配。

(4)如果在大圖的其中一個座標點上匹配到了小圖的所有RGB值,則此時返回小圖在大圖中的座標點。

(5)如果匹配了大圖所有的座標點,都沒有匹配到,則說明大圖中不存在小圖,匹配失敗。

2.關鍵技術

2.1 小圖在大圖中匹配的邏輯演算法

smallRGB[i, j] = bigRGB[x + i, y + j]

其中,x是大圖的橫座標的遍歷的值,y是大圖的縱座標的遍歷的值,i是小圖的橫座標的遍歷的值,j是小圖的縱座標的遍歷的值,RGB的取值是在(0, 0 ,0)~(255, 255, 255)之間。

2.2 匹配小圖與大圖的RGB值

在大圖中每移動一個座標,都對小圖和大圖對應位置的RGB值進行匹配,最終返回中心座標。

import pyscreenshot
from PIL import Image

def match_image():
    '''
        將小圖在大圖中進行迴圈匹配,返回中心座標
    '''

    # 獲取小圖的RGB值
    small_pic = Image.open('/home/uos/Documents/small_pic.png')
    small_data = small.load() 

    # 獲取大圖的RGB
    big_pic = pyscreenshot.grab()
    big_data = big.load()

    # 遍歷大圖的橫座標
    for x in range(big.width - small.width):
        # 遍歷大圖的縱座標
        for y in range(big.height - small.height):
           # 遍歷小圖的橫座標
           for i in range(small.width):
                # 遍歷小圖的縱座標
                for j in range(small.height):
                    # 判斷:如果對應座標的RGB值是否相等
                    if big_data[x + i, y + j] != small_data[i, j]:
                        # 當出現有RGB值不相等的時候,就終止
                        break
	# 如果所有RGB都相等,則返回中心座標                        
	return (x + small.width / 2, y + small.height / 2)

	

2.3 效能優化—快速滑動匹配

(1)問題

我們可以做一個思想實驗,要匹配一個(100 x 100)的目標小圖,大圖為(1920 x 1080),那麼,小圖的座標點有10000個,大圖的座標點有1920 x 1080 = 2,073,600個,而每個座標點的RGB值由3個數組成,因此要將大圖中的所有座標點匹配完,計算機需要計算的次數是:10000*2073600 * 3 = 62,208,000,000,這個數量級已經達到了千億級別,而這僅僅是去匹配一張很小的圖而已,很明顯計算機處理壓力太大了,所以需要對演算法進行優化。

(2)優化

在小圖中隨機選取一些座標點,如果選取的這些座標點存在與大圖中RGB值不相等的情況,則直接跳出本次匹配,進行大圖的下一個座標點的匹配,如果選取的座標點都匹配成功,再進行剩餘座標點的匹配,這樣可以實現小圖在大圖中快速滑動的效果。

import random

def random_point(small_pic):
    '''
    	每次隨機取10-20個點,並在小圖中隨機取座標
    '''
    point_list = []
    # 每次隨機取10-20個點
    count = random.randrange(10, 20)
    for i in range(count):
        # 小圖中隨機取座標
        sx = random.randrange(0, small.width)
        sy = random.randrange(0, small.height)
        point_list.append([sx, sy])
    return point_list


def random_match(x, y, point_list, big_data, small_data):
    '''
    	在小圖中隨機取幾個點進行匹配
    '''
     for point in point_list:
         if big_data[x + point[0], y + point[1]] != small_data[point[0], point[1]]:
             return False
    return True

2.4 效能優化—匹配度

(1)問題

UI自動化測試中,介面在我們操作的過程中是不斷變化的,如果要求小圖的RGB值完全的100%與大圖相應位置的RGB相等,容易出現匹配失敗的情況。

(2)優化

在實際應用中,我們需要對錯誤匹配的座標點有一定的容忍度,這裡我稱為”匹配度“。比如,匹配度為90%,也就是說,只要小圖中90%的座標點的RGB,與大圖中對應位置能匹配上,那麼就判定為匹配成功。

def check_match(x, y, small_pic, big_data, small_data):
    '''
   		設定匹配度90%
    '''
    same = 0
    diff = 0
    for i in range(small_pic.width):
        for j in range(small_pic.height):
            if big_data[x + i, y + j] == small_data[i, j]:
                same = same + 1
            else:
                diff = diff + 1
    # 設定匹配度0.9
    similarity = same / (same + diff)
    if similarity >= 0.9:
        return True
    else:
        return False

這是針對所有座標點設定了一個匹配度,其實我們之前隨機選取做”預匹配“的幾個點也可以設定一個匹配度(2.效能優化—快速滑動匹配裡面的random_match()),這樣可以進一步提升匹配效率。

def random_match(x, y, point_list, big_data, small_data):
    '''
    	在小圖中隨機取幾個點進行匹配,隨機點同樣設定匹配度90%
    '''
    same = 0
    diff = 0
    for point in point_list:
        if big_data[x + point[0], y + point[1]] == small_data[point[0], point[1]]:
            same = same + 1
        else:
            diff = diff + 1
	# 設定匹配度0.9
    if same / (same + diff) >= 0.9:
        return True
    else:
        return False

2.4 關鍵技術整合

將以上關鍵技術做整合

import pyscreenshot
import random
from PIL import Image

def check_match(x, y, small_pic, big_data, small_data):
    '''
    	設定匹配度0.9
    '''
    same = 0
    diff = 0
    for i in range(small_pic.width):
        for j in range(small_pic.height):
            if big_data[x + i, y + j] == small_data[i, j]:
                same = same + 1
            else:
                diff = diff + 1
    # 設定匹配度0.9
    similarity = same / (same + diff)
    if similarity >= 0.9:
        return True
    else:
        return False


def random_point(small_pic):
    '''
    	每次隨機取10-20個點,並在小圖中隨機取座標
    :return:
    '''
    point_list = []
    count = random.randrange(10, 16)
    for i in range(count):
        sx = random.randrange(0, small.width)
        sy = random.randrange(0, small.height)
        point_list.append([sx, sy])
    return point_list


def random_match(x, y, point_list, big_data, small_data):
    '''
    	在小圖中隨機取幾個點進行匹配,隨機點同樣設定匹配度0.9
    '''
    same = 0
    diff = 0
    for point in point_list:
        if big_data[x + point[0], y + point[1]] == small_data[point[0], point[1]]:
            same = same + 1
        else:
            diff = diff + 1

    if same / (same + diff) >= 0.8:
        return True
    else:
        return False


def match_image(pic_name):
    '''
    	通過一張小圖,找到對應當前螢幕中的位置
    '''
    # 開啟小圖和大圖,獲取RGB值
    file_path = "/home/uos/Documents/" + pic_name
    small_pic = Image.open(file_path)
    samll_data = small_pic.load()
    big_pic = pyscreenshot.grab()
    big_data = big_pic.load()
    
    # 迴圈匹配,返回中心座標
    point_list = random_point(small_pic)
	# 遍歷大圖和小圖的橫縱座標
    for x in range(big_pic.width - small_pic.width):
        for y in range(big_pic.height - small_pic.height):
            # 判斷隨機點都相等
            if random_match(x, y, point_list, big_data, small_data):
                # 判斷匹配度
                if check_match(x, y, small_pic, big_data, small_data):
                    # 返回中心座標
                    return (x + small_pic.width / 2, y + small_pic.height / 2)
    return False

整合之後,通過match_image()這個函式,傳入目標小圖的檔名稱,即可返回在當前螢幕中的中心座標。

在我們的UI自動化測試專案中,測試用例如果需要通過影象識別去獲取目標控制元件的座標,只需呼叫這個函式即可。

五、實驗驗證

經過對關鍵技術的整合,我們設計實驗的之後只需要呼叫match_image()這個函式,在括號內傳入小圖的檔名稱即可。

1.功能

實驗目標:在桌面尋找”計算機“圖示的中心座標位置

(1)擷取小圖,小圖檔名稱為:small_pic.png

(2)呼叫函式match_image()並執行

if __name__ = '__main__':
    print(match_image('small_pic'))

執行結果:

(3)結果驗證

通過UOS截圖錄屏工具,通過滑鼠大致定位一下計算機圖示在螢幕中的位置。

可以看出,滑鼠游標的位置顯示為(596, 279),中心座標點返回正確

2.效能

通過在程式中開始和結束位置加入時間戳

import time

if __name__ == '__main__':
    print('開始時間:' + str(time.time()))
    print(match_image('small_pic.png'))
    print('結束時間:' + str(time.time()))

time.time()返回當前時間的時間戳。

時間戳:時間戳是指格林威治時間1970年01月01日00時00分00秒(背景時間1970年01月01日08時00分00秒)起至現在的總秒數。

結果:

可以看出,在x86平臺的執行時間不到2秒,經過在ARM和MIPS平臺的同樣測試,執行時間不到3秒,在UI自動化測試中是可以接受的。

3.相容

目前演算法所依賴的python庫,不存在平臺架構之間的差異,經過實際測試,AMD/ARM/MIPS三個平臺均可以正常使用,相容性符合要求。

六、小結

在UOS桌面應用自動化測試中,很大程度上依賴與影象識別進行用例的斷言,三個平臺使用的同一套自動化測試程式碼,如果ARM和MIPS上無法使用opencv的影象識別技術,用例指令碼根本跑不起來,那麼整個自動化測試專案的意義將大打折扣。

在技術實現上,通過圖片的RGB值去做等值運算,從而計算出在圖片的位置。在應用的時候,通過在返回座標位置進行滑鼠和鍵盤的操作,我們就可以模擬出手工測試的一些操作步驟,在斷言的時候也就可以進行True或False的判斷。

有了自研的這套演算法,我們就可以實現在x86上採用opencv進行影象識別,在ARM和MIPS上採用這套演算法做影象識別,不用去改造專案的用例指令碼程式碼,同時搭建測試環境的時候,ARM和MIPS少安裝一個庫,這樣也提升了測試準備的效率。

未來優化的方向,這套技術演算法與opencv模板匹配的技術演算法還是存在差距,比如,UI顯示失去焦點後,顏色會變淡,因為顯示的RGB值已經變化了,此時這套方法是無法識別的,但是通過opencv模板匹配是可以找到的。

七、參考資料

ARM 64架構編譯安裝python opencv:

https://blog.csdn.net/qq_31261509/article/details/81734859

Arm架構安裝opencv(python3)

https://www.e-learn.cn/topic/3846246

opencv移植到mips開發板的完整過程——及測試樣例
https://blog.csdn.net/weixin_38251305/article/details/108406201

RGB:

https://baike.baidu.com/item/RGB/342517?fr=aladdin

向量圖、點陣圖、RGB、YUV、JPEG、PNG的理解:

https://blog.csdn.net/kcstrong/article/details/81705693