Pynote

Python、機械学習、画像処理について

OpenCV - テンプレートマッチングの仕組み及び方法について

テンプレートマッチングの仕組み

検出対象の物体が映るテンプレート画像を用意する。

入力画像に対して、テンプレート画像と同じ大きさの検索窓を左上からスライドさせながら動かしていく。

移動する検索窓の各位置において、その検索窓の範囲の画像とテンプレート画像の類似度を計算する。

類似度が高い場合は、その位置に物体があると判定する。

OpenCV でテンプレートマッチングを行う方法

OpenCVでは、matchTemplate() でテンプレートマッチングが行える。

関数

result = cv.matchTemplate(image, templ, method[, result[, mask]])
  • 引数
    • image: 入力画像
    • method: 類似度を計算する方法
    • result: マッチング結果 (引数経由で受け取る場合)
    • mask: マスク
  • 返り値
    • result: マッチング結果

コード

入力画像

テンプレート画像

テンプレートマッチング
import cv2
import matplotlib.pyplot as plt

# 入力画像、テンプレート画像を読み込む。
img = cv2.imread('mario.png')  # 入力画像
template = cv2.imread('template.png')  # テンプレート画像

# テンプレートマッチングを行う。
results = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)

result の解釈

result には検索窓を動かた際の各位置での類似度の値が入っている。
入力画像の大きさを (W, H)、テンプレート画像の大きさを (w, h) とする。
検索窓の左上の点を  (x, y) としたとき、 (x, y) \in (W - w + 1, H - h + 1) である。

実際、result の形状を確認すると、そうなっていることがわかる。

print('img.shape', img.shape)  # (380, 694, 3)
print('template.shape', template.shape) # (30, 31, 3)

H, W, C = img.shape
h, w, c = template.shape
print(H - h + 1, W - w + 1)  # 351 664

print('result.shape', result.shape) # (351, 664)

検索窓が位置  (x, y) にあるとき、その範囲に含まれる画像とテンプレート画像との類似度は以下で得られる。

# 検索窓の範囲を描画する。
def draw_window(img, x, y, w, h):
    '''
    Args:
        img: 描画対象の画像
        x: 検索窓の左上の x 座標
        y: 検索窓の左上の y 座標
        w: 検索窓の幅
        h: 検索窓の高さ
    '''
    tl = x, y  # 左上の頂点座標
    br = x + w, y + h  # 右下の頂点座標
    cv2.rectangle(img, tl, br, (0, 255, 0), 3)

x, y = 100, 150  # 検索窓の左上の座標
drawn = img.copy()
draw_window(drawn, x, y, w, h)

plt.imshow(cv2.cvtColor(drawn, cv2.COLOR_BGR2RGB))
plt.show()

print('similarity:', result[x, y])  # similarity: -0.0350026


類似度が最も高い位置を描画がする。

# 最も類似度が高い位置を取得する。
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result)
print('max value: {}, position: {}'.format(maxVal, maxLoc))
# max value: 0.9999998211860657, position: (392, 124)

# 描画する。
drawn = img.copy()
draw_window(drawn, maxLoc[0], maxLoc[1], w, h)

plt.imshow(cv2.cvtColor(drawn, cv2.COLOR_BGR2RGB))
plt.show()


類似度が高い位置を描画する。

# 最も類似度が 0.9 以上の位置を取得する。
locs = np.where(result >= 0.9)
drawn = img.copy()

# 描画する。
for tl in zip(*locs[::-1]):
    draw_window(drawn, tl[0], tl[1], w, h)

plt.imshow(cv2.cvtColor(drawn, cv2.COLOR_BGR2RGB))
plt.show()


類似度の計算方法

templateMatching() の method 引数で指定する。

入力画像を I、テンプレート画像を T とする。

cv2.TM_SQDIFF

result(x, y) = \sum_{x', y'}{(T(x', y') - I(x + x', y + y'))^2}

cv2.TM_SQDIFF_NORMED

result(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}}

cv2.TM_CCORR


result(x, y)= \sum_{x', y'} (T(x', y') \cdot I(x + x', y + y'))

cv2.TM_CCORR_NORMED

result(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}}

cv2.TM_CCOEFF

result(x, y) = \sum_{x', y'} (T'(x', y') \cdot I'(x + x', y + y'))
where


\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}

cv2.TM_CCOEFF_NORMED

result(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}}