1. 程式人生 > >OpenCV3——matchTemplate影象模板匹配

OpenCV3——matchTemplate影象模板匹配

參考文章:

其他:

1,功能介紹

matchTemplate函式主要應用於,尋找一張圖片在另一張圖片中的位置。

先看一個實際測試示例,在一張普通圖片中找到其中的水印位置。下圖依次為水印template圖——圖1,測試圖——圖2,在原圖中根據結果畫出的水印位置圖——圖3。

圖1:

圖2:

圖3:

2,函式分析

2.1引數分析

該函式第一個引數是源影象,第二個引數是模板影象,第三個引數是匹配的結果影象,第四個引數是用於指定比較的方法。

2.2實現原理

  • 我們需要2幅影象:

    1. 原影象 (I): 在這幅影象裡,我們希望找到一塊和模板匹配的區域
    2. 模板 (T): 將和原影象比照的影象塊

    我們的目標是檢測最匹配的區域:

    ../../../../../_images/Template_Matching_Template_Theory_Summary.jpg
  • 為了確定匹配區域, 我們不得不滑動模板影象和原影象進行 比較 :

    ../../../../../_images/Template_Matching_Template_Theory_Sliding.jpg
  • 通過 滑動, 我們的意思是影象塊一次移動一個畫素 (從左往右,從上往下). 在每一個位置, 都進行一次度量計算來表明它是 “好” 或 “壞” 地與那個位置匹配 (或者說塊影象和原影象的特定區域有多麼相似).

  • 對於 T 覆蓋在 I 上的每個位置,你把度量值 儲存 到 結果影象矩陣 (R) 中. 在 R 中的每個位置 (x,y) 都包含匹配度量值:

    ../../../../../_images/Template_Matching_Template_Theory_Result.jpg

    上圖就是 TM_CCORR_NORMED 方法處理後的結果影象 R . 最白的位置代表最高的匹配. 正如您所見, 紅色橢圓框住的位置很可能是結果影象矩陣中的最大數值, 所以這個區域 (以這個點為頂點,長寬和模板影象一樣大小的矩陣) 被認為是匹配的.

  • 實際上, 我們使用函式 minMaxLoc 來定位在矩陣 R 中的最大值點 (或者最小值, 根據函式輸入的匹配引數) .

2.3 支援的演算法

OpenCV通過函式 matchTemplate 實現了模板匹配演算法. 可用的方法有6個。通常,隨著從簡單的測量(平方差)到更復雜的測量(相關係數),我們可獲得越來越準確的匹配(同時也意味著越來越大的計算代價). 最好的辦法是對所有這些設定多做一些測試實驗,以便為自己的應用選擇同時兼顧速度和精度的最佳方案.

1、cv::TM_SQDIFF:該方法使用平方差進行匹配,因此最佳的匹配結果在結果為0處,值越大匹配結果越差。

R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2

2、cv::TM_SQDIFF_NORMED:該方法使用歸一化的平方差進行匹配,最佳匹配也在結果為0處。

R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}

3、cv::TM_CCORR:相關性匹配方法,該方法使用源影象與模板影象的卷積結果進行匹配,因此,最佳匹配位置在值最大處,值越小匹配結果越差。

R(x,y)= \sum _{x',y'} (T(x',y')  \cdot I(x+x',y+y'))

4、cv::TM_CCORR_NORMED:歸一化的相關性匹配方法,與相關性匹配方法類似,最佳匹配位置也是在值最大處。

R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I'(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}

5、cv::TM_CCOEFF:相關性係數匹配方法,該方法使用源影象與其均值的差、模板與其均值的差二者之間的相關性進行匹配,最佳匹配結果在值等於1處,最差匹配結果在值等於-1處,值等於0直接表示二者不相關。

R(x,y)= \sum _{x',y'} (T'(x',y')  \cdot I(x+x',y+y'))

在這裡

\begin{array}{l} T'(x',y')=T(x',y') - 1/(w  \cdot h)  \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w  \cdot h)  \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}

6、cv::TM_CCOEFF_NORMED:歸一化的相關性係數匹配方法,正值表示匹配的結果較好,負值則表示匹配的效果較差,也是值越大,匹配效果也好。

R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }

匹配方法的選取根據實際情況而定,這裡我們選擇的方法是cv::TM_CCOEFF_NORMED,源影象和匹配的相似度圖如下:

因此,我們若想找到最佳匹配位置,只需要找到匹配結果影象的最大值點即可,這裡我們使用cv::minMaxLoc()函式(具體請參考cv::Mat中最值和均值的求解)來找這個最大值點。找到結果後,將其繪製到原影象上,效果如下圖所示(圓等圖形的繪製請參考OpenCV3中的繪圖詳解),這裡注意匹配結果影象與原影象之間的大小關係,他們之間差了一個模板大小。

測試程式碼:

1.匹配單個物件

def MatchOne(image,template):
    # w, h = template.shape[::-1]
    h ,w = template.shape[0],template.shape[1]
    res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
    print(type(res), res.shape)
    # print(res)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    print(type(max_loc), type(max_val))
    top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)

    cv2.rectangle(image, top_left, bottom_right, 255, 2)

    cv2.imshow('res', res)
    cv2.imshow('result', image)
    cv2.waitKey(0)
    if 0xFF == ord('q'):
        cv2.destroyAllWindows()

2,匹配多個物件

def MultiObjMatch1(image,template):
    print(type(image), image.shape)
    print(type(template), template.shape)
    # image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    # template = cv2.cvtColor(template,cv2.COLOR_BGR2GRAY)
    res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
    h, w = template.shape[0], template.shape[1]
    # res = cv2.resize(res,(image.shape[1],image.shape[0]))
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)


    print('maxval:',max_val,'max_loc_val:',res[max_loc[1]][max_loc[0]])
    # print(len(res),len(res[0]))
    print('type of res value:',type(res[0][0]), res.shape)
    print('type of min_val:',type(min_val))
    print('type of min_val convert to numpy:',type(np.float32(min_val)))
    # print('min:', min_val, 'max:', max_val)

    top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    cv2.rectangle(image, top_left, bottom_right, 255, 2)

    res = (res - min_val) / (max_val - min_val)
    for i in range(len(res)):
        for j in range(len(res[i])):
            if res[i][j] >0.95:
                print(i,j,res[i][j])
                top_left = (j,i)
                bottom_right = (top_left[0] + w, top_left[1] + h)
                cv2.rectangle(image, top_left, bottom_right, 255, 2)

    cv2.imshow('res', res)
    cv2.imshow('compare', image)
    res = res*255
    cv2.imwrite('./images/fangmaskreslut_'+str(int(time.time()))+'.jpg',image)
    cv2.imwrite('./images/fangmaskreslut_res_'+str(int(time.time()))+'.jpg',res)
    cv2.waitKey(0)
    if 0xFF == ord('q'):
        cv2.destroyAllWindows()
    pass

方法2


#將上一個max位置矩陣內的值都置為最小值,然後重新找最大值及其位置
def getNextMax(res,max_loc,min_val,h, w):
    for i in range(max_loc[0],min(max_loc[0]+w,res.shape[1])):
        for j in range(max_loc[1],min(max_loc[1]+h,res.shape[0])):
            res[j][i]=min_val
            # print('change value')
    res = np.float32(res)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    # print('max_val:',max_val)
    return min_val, max_val, min_loc, max_loc,res
    pass


def MultiObjMatch2(image,template):
    print(type(image), image.shape)
    print(type(template), template.shape)
    # image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    # template = cv2.cvtColor(template,cv2.COLOR_BGR2GRAY)
    res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
    h, w = template.shape[0], template.shape[1]
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    print('start while')
    while max_val>0.6:
        top_left = max_loc
        bottom_right = (top_left[0] + w, top_left[1] + h)
        cv2.rectangle(image, top_left, bottom_right, 255, 2)
        min_val, max_val, min_loc, max_loc ,res= getNextMax(res,max_loc,min_val,h, w)
        print(max_val)

    cv2.imshow('res', res)
    cv2.imshow('compare', image)
    # cv2.imwrite('./images/fangmaskreslut.jpg',image)
    # cv2.imwrite('./images/fangmaskreslut_res.jpg',res)
    cv2.waitKey(0)
    if 0xFF == ord('q'):
        cv2.destroyAllWindows()
    pass

效果圖:


實景圖測試效果: