2015. 2. 20.
2017.10. 4. OpenCV3.3.0とVisual C++ 2017で動作確認済み、ただし、文末の付記参照
石立 喬
OpenCVとVisual C++による画像処理と認識(12)
----- テンプレートマッチングで道路標識と数字を検出する -----
テンプレートマッチングは古くから使われてきた探索手法であるが、最近では、学習を利用した、より高性能な探索技法が主流になっている。しかし、幸いに、OpenCVにはテンプレートマッチング用の優れた関数が用意されているので、道路標識や数字の検出を試み、マッチング相関性の計算方法の比較も行った。
テンプレートマッチングとは
テンプレートとは型紙のことで、見本とも言える。見本と比べて、同じか違うかを判断することである。テンプレートマッチングは、被探索画像の隅から隅まで、順次にテンプレート画像をずらしながら重ねて行き、相関性を求めて、一定値よりも大であれば、そこに一致した図形があると判断する。
この操作は、テンプレートと大きさが異なったり、傾いていたりすると、一致を判断できない欠点がある。計算量も多いので、実用的には、敬遠されることが多い。ただし、大きさが同じで、方向を揃えることができ、ベルトコンベアなどで被検査物が移動する製品検査などには、有用である。
OpenCVを用いたテンプレートマッチング
大きく分けて、二つの場合が考えられる。
◎画像の中から最も良く一致した対象物を一個だけ検出する
たとえば、視野の中に「止まれ」の標識が一個でもあれは、それに反応するようなシステムの場合で、 matchTemplate関数を用いてマッチング結果配列resultを求め、minMaxLoc関数を用いてその最大(相関計算方法によっては、最小)値とその位置を取得する。最大(最小)値が一定の基準を満たしていれば、マッチングしたと判断する。
◎画像の中から、一致した対象物を一個以上検出する
プリント基板などにある多数の同じパターンで、不良個所が、どことどこにあるかを探して、そのすべてを表示させるような場合に使用する。 matchTemplate関数を用いてマッチング結果配列resultを求め、その配列を二次元的にスキャンして、相関値を順次取得し、一定の基準を満たしていれば、マッチングがあったとして、その座標位置を合致配列に格納する。最終的に、合致配列から読み取って使用する。
カラー画像での簡単なテンプレートマッチング
テンプレートマッチングは、カラー画像でも使用できる。ただし、特定の色相を抜き出すなどの前処理の後ではグレイスケール画像になることが多く、また処理速度が遅いので、グレイスケール画像でテンプレートマッチングを取るのが一般的である。
ここでは、カラー画像の例として、被探索画像の一部をそのままテンプレート画像として用いる場合を示す。一番簡単で、探索精度も高い。いわば、うまくいって当たり前の非現実的なケースである。、
使用したプログラムを図1に示す。このプログラムは、下記から成る。
1)原画像(被探索画像)src_imageとテンプレート画像temp_imageを読み込み、表示する。
2)テンプレートマッチングを取る。
3)結果のマッチング配列(相関値の分布を示す)resultを画像として表示する。
4)マッチング点(座標)maxPtを取得する。不必要な値に対しては、引数として0またはNULLを入れる。
5)原画像上に、黄色の矩形でマッチング箇所を表示する。
図2はテンプレート画像と原画像を表示した結果で、図3は、マッチング結果を画像として表示したものと、マッチング箇所を表示した結果である。マッチング画像は、テンプレートのサイズだけ原画像より小さく、この範囲内でしかマッチングを検出できない。マッチング画像において、最も白い部分がマッチング点で、その位置がテンプレート画像の左上に当たる。
使用したOpenCV関数の説明
◎matchTemplate関数
引数は
matchTemplate(被探索図形、テンプレート画像、マッチング結果配列、比較方法);
となり、被探索図形は、カラーでもよい。テンプレート画像は、被探索画像と同タイプとする。マッチング結果配列はCV_32Fである。
比較方法には下記がある(= で示したのは、代用できる整数値である)。
TM_SQDIFF = 0 ------------- 差の二乗の合計、小さいほど良くマッチしている
TM_SQDIFF_NORMED = 1 ---- 同上の正規化
TM_CCORR = 2 ------------- 乗算したものの合計、大きいほど良くマッチしている
TM_CCORR_NORMED = 3 ---- 同上の正規化
TM_CCOEFF = 4 ------------ 相関係数であり、正に大きいほど良くマッチしている
TM_CCOEFF_NORMED = 5 --- 同上の正規化
結果配列resultから特定の座標位置の相関値を得るには、
result.at<float>(y, x)
を用いる。
◎minMaxLoc関数
下記のように、マッチング結果配列を引数として与え、相関値の最大値、最小値、最小位置、最大位置を参照渡しで受け取る。
minMaxLoc(マッチング結果配列、&最小値、&最大値 = 0、&最小位置
= 0、&最大位置 = 0、マスク = noArray());
あらかじめ、double minVal、double maxVal、Point minLoc、Point maxLoc の入れ物を用意しておく(必要あるもののみ)。resultはfloatであるのに、ここでのminMaxとmaxValはdoubleであることに注意されたい。

図1 カラー画像の簡単なプログラム
図2 テンプレート画像と共に原画像を示す
図3 マッチング結果と最大マッチング点の表示
道路標識に対するテンプレートマッチング(同一種類に対して一個のみ検出の例)
道路標識を例に、簡単なテンプレートマッチングを行う。図4は、そのプログラムで、原画像の読込み部分は省略してある。
ここでは、下記の処理を行う。原画像bin_imageは、「画像処理と認識(10)」で既に示した赤色抽出によって、道路標識の部分だけを二値化したものである。
1)二種類のテンプレート画像temp_stopとtemp_noentryをグレイスケールとして読み込む。
2)テンプレートtemp_stopで原画像bin_imageとマッチングをとり、相関値の最大値maxValとその位置maxPtを取得する。
3)相関値の最大がしきい値thresholdより大きければ、その位置を黄色の矩形で原画像に描き、表示する。
4)テンプレートtemp_noentryで原画像とマッチングをとり、相関値の最大値とその位置を取得する。
5)相関値の最大がしきい値より大きければ、その位置をシアンの矩形で原画像に描き、表示する。
図5は正しく検出された例を、図6は、二個の標識がある合成した原画像で、二種類の道路標識を同時に検出できることを示す。
図4 「止まれ」と「進入禁止」を検出するプログラム
図5 正しく検出された例

図6 実際にはない合成画像で、異なる二つの道路標識を同時に検出した
図7はその他の結果の一部を紹介したもので、左二つは、少し斜めになっていても検出できた例、三番目は、大きい「止まれ」を下の部分だけで「止まれ」と判断した例、右端は小さい「進入禁止」を「止まれ」と間違えた例である。
図7 少し斜めでも検出できたが、「進入禁止」を「止まれ」と間違えた例も
数字の二値画像に対するテンプレートマッチング(複数個を同時に検出の例)
被検索画像から複数の図形を検出するためには、最大値のみを取得するminMaxLoc関数は使えない。マッチング配列resultから、自分で探し出す必要がある。図8はそのプログラムで、下記を行う。
1)原画像をカラー画像src_imageとして読み込む(後に、マッチング結果を赤色で描くため)。
2)原画像をグレイスケール化してbin_imageとする。
3)数字「3」のテンプレートtemp_threeと原画像bin_imageとでテンプレートマッチングを行う。
4)マッチング結果配列resultをスキャンして、値がしきい値thresholdより大きければ、その座標位置を検出点配列detected_pointに入れる。
5)配列detected_pointを順次読み取って、原画像上に赤枠でマッチング位置を表示する。
図9は得られた結果で、5個の「3」がすべて検出されている。しきい値thresholdは試行錯誤で決定したが、0.94fまで下げると、「8」も検出されるようになったので、余裕を見て、0.96fとした。
図8 複数個を検出するプログラム
図9 数字を検出した結果
マッチング評価法(相関性の計算方法)の比較
テンプレートと被検索画像の間での演算方法に下記のタイプ(各方法に割り当てられた整数で示す)がある。
タイプ0 ---- TM_SQDIFF
タイプ1 ---- TM_SQDIFF_NORMED
タイプ2 ---- TM_CCORR
タイプ3 ---- TM_CCORR_NORMED
タイプ4 ---- TM_CCOEFF
タイプ5 ---- TM_CCOEFF_NORMED
◎所要時間
テンプレートマッチングの実施に当って、その所要時間が気になるので、数字「3」を検出するプログラムを使って、上記各タイプの比較を行った。そのプログラムを図10に、結果を図11に示す。プログラムでは、TM_SQDIFF等の文字列で指定する代わりに、対応する整数値を用いた。
インクルードファイルの追加、名前空間の宣言など、作表方法については「Visual
Studio Express 2013(Visual C++)の新しい使い方(3)」を、時間測定方法については「・・・の新しい使い方(5)」を参照されたい。
図10 所要時間を測定するためのプログラム
図11 タイプ2(TM_CCORR)がやや早いが、他は大差がない
◎出力データ
結果として得られる最小値、最大値の値は、タイプによって異なる。そこで、これを確認した。そのプログラムを図12に、結果を図13に示す。タイプ1と2は、数値が小さい方がマッチングの度合いが高い。タイプ1のminValが0.000704になっているのはかなり良好な一致を示している。タイプ2~5は、数値が大きい方が相関性が高い。タイプ3のmaxValが0.999652であるのも素晴らしい。プログラムで設定したthreshold
= 0.96fは甘すぎたかもしれない。
図12 最小値と最大値を具体的に知るためのプログラム
図13 タイプ3(TM_CCORR_NORMED)の最大値が1に一番近い
結 論
実用的には、あまり使われなくなったテンプレートマッチングであるが、被検索画像から抜き取ったままのテンプレート画像に対しては、非常に良い成績を示した。道路標識検出の場合も、テンプレート画像に近いサイズの標識には十分な性能が得られた。
マッチングの度合いを評価する計算方法が6種類、OpenCVに用意されているので、これらで得られる数値と所要時間を確認した。ここで用いた簡単な例に限っての評価であるが、最大値、最小値などの数値は、いずれの方法も十分な精度を持っていた。所要時間では、TM_CCORRが36msecと早く、他は40msec~50msecであった。ただし、これはDebugモードでビルドしたので、Releaseモードでは、もっと早いかもしれない。
付 記
OpenCV3.3.0とVisual C++ 2017を使用するにあたり、下記を参照されたい。それ以外は、古いプログラムをそのまま使用できる。
1)従来のインクルードファイルをそのまま使用しても良いが、#include <opencv2/opencv.hpp>と変更しても問題はない。
2)従来のint _tmain(int argc, _TCHAR* argv[])をそのまま使用しても良いが、int main()にしても差支えない。
3)vectorを使用するのに、using namespace std;の追加が必要である(従来は無くても良かったような気がする)。
「Visual C++の勉強部屋」(目次)へ