2016年1月19日火曜日

テンプレートマッチングをしてみた

サーバ実装のブログはだいぶあきたので、今日からしばらくは画像処理、かっこよくいうとコンピュータビジョン系のことでも書こうと思います。

コンピュータが機械学習して、人の顔とか猫とかアニメのキャラクターとかを識別するあれです。
いきなり顔認識や機械学習のソースコードをのっけると、話が飛びすぎるので、まず今日は画像処理の基礎から。一番簡単な画像認識であるテンプレートマッチングをやることにします。

そもそもテンプレートマッチングとは何かというと、ある画像から画像の一部を切り出して、その切り出した画像が元の画像のどこにあるのかを判別することです。

テンプレートマッチングがどのようにして画像を判別しているのかについて、ちょー簡単かつ適当に説明すると、地図で例えるならば、画像のそれぞれの点で明るいところを山、画像の暗いところを谷にして立体地図のようなものを作り、立体地図の一部の破片がどこにはまるかを比べていくような感じですね。


歯医者でたとえると、でこぼこの歯形があったとして、医者がどこの歯に合うか突き止めるような感じ。

数学的にいうと面のそれぞれの点で距離を図って差の平均をとる、つまり面の明るさで距離を測る感じですか。
自分でいうのもなんですが、「面の明るさで距離を測る」ってわけのわからないこと書いているなぁと。


こんなわけのわからないこともOpenCVというライブラリを使えば簡単に行うことができます。
C++言語のソースコードで書くとここんな感じになります。

-----------------------------------------------------
#include <stdio.h>
#include <opencv2/opencv.hpp>

#include "mygetopt.h"

#ifdef _WIN32
#pragma comment(lib,"opencv_core300")
#pragma comment(lib,"opencv_highgui300")
#pragma comment(lib,"opencv_hal300")
#pragma comment(lib,"opencv_world300")
//#pragma comment(lib,"opencv_ts300")
#pragma comment(lib,"ippicvmt")
#pragma comment(lib,"zlib")
#endif

int main(int argc,char* argv[])
{
cv::Mat src_img,tmp_img,dst_img,gray_img;
cv::Point min_loc=0.0,max_loc=0.0;
double min_val,max_val;
int c;
char* src_file="chrome.png";
char* ptn_file="google.png";

while (1)
{
int this_option_optind = myoptind ? myoptind : 1;
c = mygetopt (argc, argv, "s:p:");
if (c == EOF)break;

switch (c){
case 's':
src_file=myoptarg;
break;
case 'p':
ptn_file=myoptarg;
break;
}
}



src_img=cv::imread(src_file);
if(src_img.empty()){
printf("Error:src_img.imread() error\n");
exit(1);
}

tmp_img=cv::imread(ptn_file,0);
if(tmp_img.empty()){
printf("Error:tmp_img.imread() error\n");
exit(1);
}

cv::cvtColor(src_img,gray_img,cv::COLOR_RGB2GRAY);
cv::matchTemplate(gray_img,tmp_img,dst_img,cv::TM_CCOEFF_NORMED);
cv::minMaxLoc(dst_img,&min_val,&max_val,&min_loc,&max_loc);


printf("%d,%d,%d,%d,%d\n",(int)(max_val*100),max_loc.x,max_loc.y,max_loc.x+tmp_img.cols,max_loc.y+tmp_img.rows);

#if 0
cv::rectangle(src_img,max_loc,cv::Point(max_loc.x+tmp_img.cols,max_loc.y+tmp_img.rows),CV_RGB(255,0,0),3);
cv::namedWindow("image1",1);
cv::imshow("image1",src_img);
cv::waitKey();
#endif

return 0;
}

-----------------------------------------------------

Chromeブラウザ画像のなかからGoogleロゴの画像の位置を突き止めるプログラムです。

画像はこのようなものを使っています。


↑試験画像
↑発見したいパターン


cv::matchTemplateというところの一行でテンプレートマッチングの計算をしています。
一行でできるんですね。
ほかの行は画像を読み込んだり、白黒に変換したりしているだけです。


このテンプレートマッチング、先ほど型の合う部分を探すといった通り、発見したいパターンの画像が拡大縮小回転されたりすると元の画像に型がどこにも合わなくなって判別できなくなるんです。

だから、アプリケーションのテストでボタンの位置を特定して自動でボタンを押す、というような用途にはとても役立つんですが、顔認識するとか、物体認識するということにはほとんど役に立ちません。無理やり顔認識させることもできなくもないのですが、かなり精度が悪いです。

じゃあどうやって顔とか猫とかを認識するのかというと、カスケード分類器というのを使って機械学習させます。正確には誰かが学習させた結果のデータをどこかから持って来れば認識はできるので、学習させる必要は必ずしもないのですが。これもプログラムは全然簡単なので、次回に書きたいと思います。

0 件のコメント:

コメントを投稿