Pynote

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

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

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

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

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

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

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

matchTemplate

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

result = cv2.matchTemplate(image, templ, method[, result[, mask]])

引数

  • image: 入力画像
  • method: 類似度を計算する方法
  • mask: マスク

返り値

  • result: マッチング結果

入力画像 sample.png

テンプレート画像 template.png

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

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

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

返り値 result の解釈

返り値 result は検索窓を動かした際の各位置での類似度を表す2次元配列である。

# 検索窓の範囲を描画する。
fig, ax = plt.subplots(facecolor="w")
ax.matshow(result, cmap="magma")

plt.show()

入力画像の大きさを (W, H)、テンプレート画像の大きさを (w, h) とする。
検索窓の左上の点を  (x, y) としたとき、その点が動く範囲は  (x, y) \in (W - w + 1, H - h + 1) である。

H, W, C = img.shape
h, w, c = template.shape
print("img.shape", img.shape)  # (380, 694, 3)
print("template.shape", template.shape)  # (30, 31, 3)

print(H - h + 1, W - w + 1)  # 351 664
print("result.shape", result.shape)  # (351, 664)

検索窓の左上が位置  (x, y) のとき、類似度は result[x, y] を参照すれば得られる。

x, y = 100, 150

# 検索窓の範囲を描画する。
fig, ax = plt.subplots(facecolor="w")
ax.add_patch(plt.Rectangle((x, y), w, h, ec="r", fc="none"))
ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

plt.show()

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


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

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

# 描画する。
fig, ax = plt.subplots(facecolor="w")
ax.add_patch(plt.Rectangle(maxLoc, w, h, ec="r", lw=2, fc="none"))
ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

plt.show()


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

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

# 描画する。
fig, ax = plt.subplots(facecolor="w")
for y, x in locs:
    ax.add_patch(plt.Rectangle((x, y), w, h, ec="r", lw=2, fc="none"))
ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

plt.show()


類似度の計算方法

method 引数で類似度の計算方法を指定する。

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

cv2.TM_SQDIFF

類似度を二乗差の合計 (sum of square difference) で計算する。


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

cv2.TM_SQDIFF_NORMED

類似度を二乗差 (square difference) の合計で計算し、正規化する (normalize)。


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

cv2.TM_CCORR


result(x, y)= \sum_{x', y'} (T(x', y') 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}}