【OpenCV】matchTemplate関数による物体検出

【OpenCV】matchTemplate関数による物体検出 プログラミング

「OpenCVのmatchTemplate関数が使えるのか?使えないのか?」
使い方にもよりますが、使えないという結論になるでしょう。

おそらく、matchTemplate関数を調べている人が、望んでいるモノとは違います。
少なくとも、私が望んでいたモノとは違いました。

でも、それなりの使い道もあるでしょう。
かなり限定されるとは思いますが。

本記事の内容

  • OpenCVのmatchTemplate関数
  • 【使えるパターン】matchTemplate関数による物体検出
  • 【使えないパターン】matchTemplate関数による物体検出

それでは、上記に沿って解説していきます。

OpenCVのmatchTemplate関数

関数の定義は、以下。

matchTemplate(image, templ, method, result=None, mask=None)

引数

imageテンプレートの探索対象となる画像
templ探索されるテンプレート
method比較手法の指定
result比較結果のマップ
maskテンプレートの一部マスク化

戻り値は、多次元配列データです。
そのデータには、各座標毎に類似度が格納されています。

matchTemplate関数がやっていることは、テンプレートをひたすら探しているだけです。
テンプレートを探索対象画像の範囲内でスライドしながら、比較して類似度を算出します。
下記のイメージです。

このときの比較して類似度を算出する方法には、以下だけ存在しています。

  • cv2.TM_CCOEFF
  • cv2.TM_CCOEFF_NORMED
  • cv2.TM_CCORR
  • cv2.TM_CCORR_NORMED
  • cv2.TM_SQDIFF
  • cv2.TM_SQDIFF_NORMED

これらをmethodに指定します。
それぞれの詳細は、ここでは説明しません。
いろいろと難しい計算式で説明がされています。

ちなみに、「NORMED」は正規化を表しているのでしょう。
計算する際に、数値を標準化して云々のことだと思います。

なお、resultとmaskはオプションです。
デフォルトでは、Noneを指定しています。

maskは使い方次第で、matchTemplate関数の価値をUPさせます。
これに関しては、別の記事で解説を行いたいと思います。

以上より、OpenCVのmatchTemplate関数についてのイメージはつかめたはずです。
では、実際に動かしていきましょう。

【使えるパターン】matchTemplate関数による物体検出

もしOpenCVをインストールしていない場合は、次の記事が参考になります。

まずは、画像を用意します。

探索対象画像「input.jpg」(597 x 800)

テンプレート画像「target.jpg」(48 x 57)

binary comment

テンプレート画像は、探索対象画像から切り取った画像です。
切り取った後に、サイズの変更はしていません。
これが、重要なポイントとなります。

サンプルコード

上記で挙げたすべての比較方法で探索を行います。
以下が、Pythonのサンプルコードです。

import cv2

# 探索対象画像読み込み(オリジナル=カラーあり)
src = cv2.imread('input.jpg')
# 探索対象画像のグレースケール化
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# テンプレート画像の読み込み(グレースケール画像として)
template = cv2.imread('target.jpg', 0)
w, h = template.shape[::-1]

# 比較するための6つの方法のすべて
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:
    tmp_src = src.copy()
    tmp_gray = gray.copy()
    method = eval(meth)

    # マッチング適用
    # マッチング自体グレースケール化した画像同士で行う
    res = cv2.matchTemplate(tmp_gray, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    # TM_SQDIFF・TM_SQDIFF_NORMEDの場合は最小を取る
    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(tmp_src, top_left, bottom_right, 255, 2)

    # 比較方法名を出力ファイル名にする
    output_name = meth.replace('.', '_') + ".jpg"
    # マッチングした結果の画像を保存
    cv2.imwrite(output_name, tmp_src)

上記コードを実行した結果、6個の画像が作成されます。

上の画像は、左から下記の順番で作成した画像を結合したモノとなっています。

  • cv2.TM_CCOEFF
  • cv2.TM_CCOEFF_NORMED
  • cv2.TM_CCORR
  • cv2.TM_CCORR_NORMED
  • cv2.TM_SQDIFF
  • cv2.TM_SQDIFF_NORMED

cv2.TM_CCORRだけが失敗しています。
それ以外は、すべて同じように成功です。

公式サイトのサンプルを見ても、同じような結果となっています。
つまり、cv2.TM_CCORRだけは上手くいかないようです。

以上、matchTemplate関数による物体検出が上手くいったケースです。
これなら、matchTemplate関数は使えますよね。

では、次は使えないmatchTemplate関数による物体検出を見ていきます。

【使えないパターン】matchTemplate関数による物体検出

テンプレート画像が次のケースだと、matchTemplate関数が役に立ちません。

  • サイズの異なる菅首相
  • 違う菅首相

下記で説明します。

サイズの異なる菅首相

「target_big.jpg」(96 x 114)

上の画像は、上記で用いた「target.jpg」を2倍のサイズにしたモノです。
これをサンプルコードで動かすと、全くマッチングしません。

同じサイズの対象物でないとマッチングしてくれないと言うことです。
これは、そういう仕様なのであきらめるしかありません。

ちなみに、探索対象画像「input.jpg」を2倍のサイズにするとどうなると思いますか?

はい、上手くマッチングできました。

違う菅首相

「target_2.jpg」(48 x 57)

同じサイズのテンプレート画像を用意したケースです。
ダメもとで試した結果、全滅となりました。

OpenCVのmatchTemplate関数は、データ的に全く同じでないとダメということでしょう。
つまり、見た目が似ていてもマッチングはしないということです。

まとめ

上記で二つのダメなケースを見てきました。
でも、基本はダメなケースになります。

上手くいくケースは、たった一つだけです。
元画像が同じで、かつ、 テンプレートがその画像から切り取った画像であるケースだけです。
このケース以外は、すべてダメなケースとなります。

どうでしょうか?
OpenCVのmatchTemplate関数は使えると感じましたか?
今のままなら難しいですよね。

ただ、引数maskを利用すれば、使えるモノになるかもしれません。
というか、引数maskを利用しないと matchTemplate関数に使い道はないのではないでしょうか?

タイトルとURLをコピーしました