python 模板匹配原理與實戰

1. 簡述

爲了讓大家不至於看到後面的公式就退卻,我先簡單描述一下模板匹配的原理:

  • 簡單來說,模板匹配就是你拿一個模板(圖片)在目標圖片上依次滑動,每次計算模板與模板下方的子圖的相似度,最後我們就計算出了非常多的相似度;
  • 如果你只是單個目標的匹配,那你只需要取相似度最大值所在的位置就可以得出匹配位置;
  • 如果你要匹配多個目標,那就設定一個閾值,就是說,只要咋倆的相似度大於比如0.8,我就認爲你是我要匹配的目標。

2. 原理

2.1 計算步驟

  • 你有一張模板圖像TT和一張較大的待搜索圖像II,模板匹配是一種用於在較大圖像中搜索和查找模板圖像位置的方法。
  • 具體就是將模板圖​​像滑動到輸入圖像上(就像在卷積操作一樣),然後在模板圖像下比較模板和輸入圖像的子圖的相似度(以下會介紹幾種不同的相似度度量方法)。
  • 它返回一個灰度圖像,其中每個像素表示該像素的鄰域與模板匹配的相似度。如果輸入圖像的大小(WxH)和模板圖像的大小(wxh),則輸出圖像的大小將爲(W-w + 1,H-h + 1)
  • 獲得相似度圖像之後,在其上查找最大相似度所在的像素。將其作爲匹配區域矩形的左上角,並以(w,h)作爲矩形的寬度和高度。該矩形是與模板匹配的區域。
  • 下面放da張圖,免得大家看得太枯燥。大概用省略號的方式表示滑動操作:
    在這裏插入圖片描述

2.2 相似度度量指標

  • 差值平方和匹配 CV_TM_SQDIFF
  • 標準化差值平方和匹配 CV_TM_SQDIFF_NORMED
  • 相關匹配 CV_TM_CCORR
  • 標準相關匹配 CV_TM_CCORR_NORMED
  • 相關匹配 CV_TM_CCOEFF
  • 標準相關匹配 CV_TM_CCOEFF_NORMED

下面的描述多數是公式,其中TT表示模板圖像,II表示目標圖像$, RR表示相似度矩陣
爲了簡化表示,下述公式僅表示模板與圖像中某個位置(x,y)(x, y)的相似度函數

2.2.1 差值平方和匹配 CV_TM_SQDIFF

  • 原理: 計算模板與某個子圖的對應像素的差值平方和。
  • 越相似該值越小
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))2R(x, y) = \sum_{x',y'}{(T(x', y') - I(x+x', y+y'))^2}

其中,(x,y)(x, y)是相對於目標大圖I左上角的座標,(x,y)(x', y')是相對於模板或者當前子圖的左上角的座標,那麼(x+x,y+y)(x+x', y+y')就是當前子圖上的某個點(x,y)(x', y')相對於目標大圖的絕對座標。

2.2.2 標準化差值平方和匹配 CV_TM_SQDIFF_NORMED

  • 標準化的差值平方和
  • 越相似該值越小
  • R(x,y)=x,y(T(x,y)I(x+x,y+y))2x,yT(x,y)2x,yI(x+x,y+y)2R(x, y) = \frac{\sum_{x',y'}{(T(x', y') - I(x+x', y+y'))^2}}{\sqrt{\sum_{x', y'}{T(x', y')^2}\sum_{x', y'}{I(x+x', y+y')^2}}}

這種標準化操作可以保證當模板和圖像各個像素的亮度都乘上了同一個係數時,相關度不發生變化。也就是說當I(x,y)I(x,y)T(x,y)T(x,y)變爲k×I(x,y)k×I(x,y)k×T(x,y)k×T(x,y)時,R(x,y)R(x,y)不發生變化。

2.2.3 相關匹配 CV_TM_CCORR

  • 模板與子圖對應位置相乘,可以將其看作是差值平方和平方項展開之後中間的那個2ab2ab
  • 越相似該值越大
  • R(x,y)=x,y(T(x,y)×I(x+x,y+y))R(x, y) = \sum_{x',y'}{(T(x', y') \times I(x+x', y+y'))}

2.2.4 標準相關匹配 CV_TM_CCORR_NORMED

  • 標準化的相關匹配,
  • 和標準化差值平方和匹配類似,都是去除了亮度線性變化對相似度計算的影響。可以保證圖像和模板同時變亮或變暗k倍時結果不變。
  • 越相似該值越大
  • R(x,y)=x,y(T(x,y)×I(x+x,y+y))x,yT(x,y)2x,yI(x+x,y+y)2R(x, y) = \frac{\sum_{x',y'}{(T(x', y') \times I(x+x', y+y'))}}{\sqrt{\sum_{x', y'}{T(x', y')^2}\sum_{x', y'}{I(x+x', y+y')^2}}}

2.2.5 相關匹配 CV_TM_CCOEFF

  • 這種方法也叫做相關匹配,但是,這裏是把圖像和模板都減去了各自的平均值,使得這兩幅圖像都沒有直流分量。
  • 越相似該值越大

T(x,y)=T(x,y)x,yT(x,y)w×hT'(x,y)=T(x,y)-\frac{\sum_{x', y'}{T(x', y')}}{w \times h}

I(x,y)=I(x,y)x,yI(x,y)w×hI'(x,y)=I(x,y)-\frac{\sum_{x', y'}{I(x', y')}}{w \times h}

R(x,y)=x,y(T(x,y)×I(x+x,y+y))R(x, y) = \sum_{x',y'}{(T'(x', y') \times I'(x+x', y+y'))}

2.2.6 標準相關匹配 CV_TM_CCOEFF_NORMED

  • 具體的說,就是在減去了各自的平均值之外,還要各自除以各自的方差。
  • 經過減去平均值和除以方差這麼兩步操作之後,無論是我們的目標圖像還是模板都被標準化了,這樣可以保證圖像和模板分別改變光照不影響計算結果。
  • 越相似該值越大
  • 計算出的相關係數被限制在了 -1 到 1 之間,1 表示完全相同,-1 表示兩幅圖像的亮度正好相反,0 表示兩幅圖像之間沒有線性關係。

T(x,y)=T(x,y)x,yT(x,y)w×hx,yT(x,y)2T'(x,y)=\frac{T(x,y)-\frac{\sum_{x', y'}{T(x', y')}}{w \times h}}{\sqrt{\sum_{x', y'}{T(x', y')^2}}}

I(x,y)=I(x,y)x,yI(x,y)w×hx,yI(x,y)2I'(x,y)=\frac{I(x,y)-\frac{\sum_{x', y'}{I(x', y')}}{w \times h}}{\sum_{x', y'}{I(x', y')^2}}

R(x,y)=x,y(T(x,y)×I(x+x,y+y))R(x, y) = \sum_{x',y'}{(T'(x', y') \times I'(x+x', y+y'))}

3. python實戰

3.1 單模板匹配單個目標(同時測試不同的相似度度量指標)

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('image3.png', 0)
img2 = img.copy()
template = cv2.imread('roi3.png', 0)
w, h = template.shape[::-1]

# All the 6 methods for comparison in a list
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
            'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']

plt.subplot(4, 2, 1)
plt.imshow(template, cmap='gray')
plt.title('Template Image'), plt.xticks([]), plt.yticks([])
plt.subplot(4, 2, 2)
plt.imshow(img, cmap='gray')
plt.title('Target Image'), plt.xticks([]), plt.yticks([])

for i, meth in enumerate(methods):
    img = img2.copy()
    method = eval(meth)

    # Apply template Matching
    res = cv2.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    # If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)

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

    plt.subplot(4, 2, i+3)
    plt.imshow(img, cmap='gray')
    plt.title('Matching Result by {}'.format(meth)), plt.xticks([]), plt.yticks([])

plt.show()

在這裏插入圖片描述

3.2 單模板匹配多個目標

import cv2
import numpy as np
from matplotlib import pyplot as plt

img_rgb = cv2.imread('image1.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('roi1.png', 0)
w, h = template.shape[::-1]

plt.subplot(131)
plt.imshow(template)
plt.subplot(132)
plt.imshow(img_rgb)

res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
    cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (255, 255, 255), 2)

plt.subplot(133)
plt.imshow(img_rgb)
plt.show()

在這裏插入圖片描述

3.3 多模板匹配多個目標

import cv2
import numpy as np
from matplotlib import pyplot as plt

img_rgb = cv2.imread('image4.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template1 = cv2.imread('roi4_1.png', 0)
template2 = cv2.imread('roi4_2.png', 0)
template3 = cv2.imread('roi4_3.png', 0)
template_list = ['roi4_1.png', 'roi4_2.png', 'roi4_3.png']

w1, h1 = template1.shape[::-1]
w2, h2 = template1.shape[::-1]
w3, h3 = template1.shape[::-1]

plt.subplot(131)
plt.imshow(template1)
plt.subplot(132)
plt.imshow(template2)
plt.subplot(133)
plt.imshow(template3)
plt.show()


for i in range(len(template_list)):
    template = cv2.imread(template_list[i], 0)
    w, h = template.shape[::-1]
    res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
    threshold = 0.65
    loc = np.where(res >= threshold)
    for pt in zip(*loc[::-1]):
        gray = int(255/(i+1))
        cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (gray, gray, gray), 2)

plt.imshow(img_rgb)
plt.show()

在這裏插入圖片描述
在這裏插入圖片描述

4. 算法優化

4.1 速度優化

這一塊我沒有看過opencv的優化方法,但是可想而知,直接滑動窗口效率是極其低下的,他們一定不會用這種方法。

4.2 精度優化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章