.. _Template_Matching: テンプレートマッチング ********************** 目的 ========= このチュートリアルでは * テンプレートマッチングを使った画像中の物体検出について学びます. * 以下の関数の使い方を学びます : **cv2.matchTemplate()**, **cv2.minMaxLoc()** 理論 ======== テンプレートマッチングは画像中に存在するテンプレート画像の位置を発見する方法です.OpenCvは **cv2.matchTemplate()** 関数を用意しています.この関数はテンプレート画像を入力画像全体にスライド(2D convolutionと同様に)させ,テンプレート画像と画像の注目領域とを比較します.OpenCVではテンプレートと注目領域を比較する方法を幾つか用意しています(詳細についてはドキュメントを参照してください).出力は各画素がテンプレート画像と注目画素の近傍領域の類似度を表すグレースケール画像になります. 入力画像のサイズが `(WxH)` ,テンプレート画像のサイズが `(wxh)` の時,出力画像のサイズは `(W-w+1, H-h+1)` になります.テンプレートマッチングをした後は **cv2.minMaxLoc()** 関数を使って,類似度が最大/最小となる画素の位置を調べます.テンプレート画像に最も似ている領域を表す矩形の左上の画素は類似度が最大となる画素の位置,領域のサイズは `(w,h)` となります. .. note:: 比較方法に ``cv2.TM_SQDIFF`` を指定した場合,結果の値が小さければ小さいほどテンプレート画像と注目領域が似ていることになるので,使用する時は気を付けてください. OpenCVを使ったテンプレートマッチング ====================================== ここでは例として,サンプル画像からメッシの顔を探してみます.以下のようなテンプレート画像を作成しました: .. image:: images/messi_face.jpg :alt: Template Image :align: center 全ての比較方法を試し,結果がどのようになるか見てみましょう: :: import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('messi5.jpg',0) img2 = img.copy() template = cv2.imread('template.jpg',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'] for meth in 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(121),plt.imshow(res,cmap = 'gray') plt.title('Matching Result'), plt.xticks([]), plt.yticks([]) plt.subplot(122),plt.imshow(img,cmap = 'gray') plt.title('Detected Point'), plt.xticks([]), plt.yticks([]) plt.suptitle(meth) plt.show() 以下に示した結果を見てみましょう: * cv2.TM_CCOEFF .. image:: images/template_ccoeff_1.jpg :alt: Template Image :align: center * cv2.TM_CCOEFF_NORMED .. image:: images/template_ccoeffn_2.jpg :alt: Template Image :align: center * cv2.TM_CCORR .. image:: images/template_ccorr_3.jpg :alt: Template Image :align: center * cv2.TM_CCORR_NORMED .. image:: images/template_ccorrn_4.jpg :alt: Template Image :align: center * cv2.TM_SQDIFF .. image:: images/template_sqdiff_5.jpg :alt: Template Image :align: center * cv2.TM_SQDIFF_NORMED .. image:: images/template_sqdiffn_6.jpg :alt: Template Image :align: center 結果を見ると **cv2.TM_CCORR** で指定した結果は期待するような結果にはなりませんでした. 複数物体のテンプレートマッチング ========================================== 前章ではメッシの顔を探しましたが,検出対象は画像中に一つしかないという状況でした.対象物体が画像中に複数個出現するような状況を考えてみてください. **cv2.minMaxLoc()** 関数を使っても全ての物体の位置は検出できません.ここでは有名なゲーム **Mario** のスクリーンショットからコインを検出します. :: import cv2 import numpy as np from matplotlib import pyplot as plt img_rgb = cv2.imread('mario.png') img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) template = cv2.imread('mario_coin.png',0) w, h = template.shape[::-1] 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), (0,0,255), 2) cv2.imwrite('res.png',img_rgb) 結果: .. image:: images/res_mario.jpg :alt: Template Matching :align: center 補足資料 ===================== 課題 ============