模板匹配

wancy發表於2024-04-20

1. 模板匹配步驟

  模板匹配是一種基於影像的技術,用於在影像中尋找與給定模板影像相似的部分。由於模板影像的尺寸小於待匹配影像的尺寸,同時又需要比較兩幅影像的每一個畫素的灰度值,因此常採用在待匹配影像中選擇與模板相同的尺寸的滑動視窗,透過比較滑動視窗與模板的相似程度,判斷待匹配影像中是否含有與模板影像相同的內容。

以下是模板匹配的基本步驟:

  1. 準備模板和目標影像:首先,需要準備一個模板影像(即要在目標影像中尋找的圖案)和一個目標影像(即待搜尋的影像)。

  2. 選擇匹配方法:選擇適當的匹配方法來度量模板和影像區域性區域之間的相似度。常用的匹配方法包括平方差匹配、歸一化平方差匹配、相關係數匹配等。

  3. 滑動模板:在目標影像上滑動模板,並在每個位置上應用所選的匹配方法計算相似度得分。

  4. 找到最佳匹配位置:根據相似度得分找到目標影像中與模板最匹配的位置。

  5. 繪製結果:可選的步驟,可以將最佳匹配位置標記在目標影像上,並進行視覺化展示。

2. 常見的模板匹配方法

  opencv中的標誌引數與方法名稱如下:

標誌引數
簡記 方法名稱
TM_SQDIFF 0 平方差匹配法
TM_SQDIFF_NORMED 1 歸一化平方差匹配法
TM_CCORR 2 相關匹配法
TM_CCORR_NORMED 3 歸一化相關匹配法
TM_CCOEFF 4 係數匹配法
TM_CCOEFF_NORMED 5 歸一化相關係數匹配法

3. 公式及其說明

  T表示模板影像,I表示原始影像。

  TM_SQDIFF:平方差匹配法,公式如下:

  計算了影像中每個位置的畫素值與模板對應位置的畫素值之間的差的平方,並對所有差值求和。值越小表示匹配程度越高。值為0時,表示完全匹配。

  TM_SQDIFF_NORMED:歸一化平方差匹配法,公式如下:

  將平方差方法進行歸一化,使得輸入結果歸一化到0~1,當模板與華東視窗完全匹配時,計算值為0.

  TM_CCORR:相關匹配法,公式如下:

  採用模板和影像間的乘法操作,值越大,匹配效果越好,0表示最壞匹配結果。

  TM_CCORR_NORMED:歸一化相關匹配法,公式如下:

  結果為0~1範圍,完全匹配時,值為1,完全不匹配時,值為0.

  TM_CCOEFF:係數匹配法,公式如下:

  其中,

  這種方法是將相關匹配法對模板減去均值的結果和原始影像減去均值的結果進行匹配,可以很好的解決模板影像和原始影像之間由於亮度不同而產生的影響。值可以為負數,值越大匹配越好,越小,匹配越差。

  TM_CCOEFF_NORMED:歸一化相關係數匹配法,公式如下:

  值為-1~1,為1時表示完全匹配,為-1時,表示完全不匹配。TM_CCOEFF_NORMED也是用得最常用的一種方法。

4. python程式碼示例

  原圖如下:

  模板圖如下(是從上圖中裁剪下來的最右邊的那個格子。)

  現在使用模板匹配TM_CCOEFF_NORMED方法尋找最優的匹配位置。

import cv2
import numpy as np

# 讀取原始影像和模板影像
original_image = cv2.imread(r'example.png', 1)
gray_image=cv2.cvtColor(original_image,cv2.COLOR_BGR2GRAY)
template = cv2.imread(r'template.png', 0)#模板圖以灰度讀取
template_height, template_width = template.shape[:2]# 獲得模板影像的高度和寬度
result = cv2.matchTemplate(gray_image, template, cv2.TM_CCOEFF_NORMED)# 使用 TM_CCOEFF_NORMED 方法進行模板匹配
threshold = 0.85# 設定匹配閾值,這裡設定為0.85
locations = np.where(result >= threshold)# 獲取匹配結果大於閾值的位置
#獲取最優
best_score = -np.inf
best_loc = None
for loc in zip(*locations[::-1]):
    score = result[loc[1], loc[0]]  # loc[1] 對應行座標,loc[0] 對應列座標
    if score > best_score:
        best_score = score
        best_loc = loc

print("Best score:", best_score)#0.99999994
print("Best location:", best_loc)# (237, 83)左上角x,y
if best_loc is not None:
    bottom_right = (best_loc[0] + template_width, best_loc[1] + template_height)
    cv2.rectangle(original_image, best_loc, bottom_right, (0,255,0), 1)#1表示繪製線條的寬度
    cv2.imwrite('./test.png',original_image)

  匹配結果儲存的圖如下:

  獲取全部滿足閾值的程式碼及匹配結果如下:

import cv2
import numpy as np

original_image = cv2.imread(r'example.png', 1)
gray_image=cv2.cvtColor(original_image,cv2.COLOR_BGR2GRAY)
template = cv2.imread(r'template.png', 0)
template_height, template_width = template.shape[:2]# 獲得模板影像的高度和寬度
result = cv2.matchTemplate(gray_image, template, cv2.TM_CCOEFF_NORMED)# 使用 TM_CCOEFF_NORMED 方法進行模板匹配
threshold = 0.85# 設定匹配閾值,這裡設定為0.85
locations = np.where(result >= threshold)# 獲取匹配結果大於閾值的位置
for loc in zip(*locations[::-1]):# 在原始影像中標記匹配的位置
    bottom_right = (loc[0] + template_width, loc[1] + template_height)
    cv2.rectangle(original_image, loc, bottom_right, (0,255,0), 1)
cv2.imwrite('./test.png',original_image)

  仔細觀察,發現這個圖比上面的獲得最優位置的匹配圖繪製的綠色矩形框的線條變粗了,這是因為匹配過程中,滿足閾值的匹配結果很多,也就是說圖中並不只是三個位置匹配到了,對於同一個位置,匹配的多個結果矩形框之間位置捱得很近,所以繪製綠色矩形框就呈現粗的現象。實際上在for迴圈中列印loc就知道繪製了多少次了。

小結:如果匹配不需要那麼嚴格,TM_CCOEFF_NORMED模板匹配可以透過調小閾值來達到要求,但是由於模板匹配是由公式計算來完成的,對於背景差異很大,儘管前景目標一致,也很難匹配到。就比如上圖中如果這個棋子在圖中白色格子也出現了,可能就匹配不到了。

  若存在不足或錯誤之處,歡迎指出與評論,謝謝!

相關文章