haarcascadeのテスト用コマンドラインツール
boost::program_optionsとOpenCVでhaarcascadeファイル、入力画像、出力画像、プレビューありなし等を選んで画像1枚から顔などの位置を認識するコマンドラインツール作った。サーバーに置いてスクリプト言語から呼び出せばいいのではないでしょうか。
- http://shokai.org/projects/opencv-study-mac/index.cgi/file/0e9a1f0e79cf/haar
- http://shokai.org/projects/opencv-study-mac/index.cgi/raw-file/0e9a1f0e79cf/haar/haartest
boostとSTLとOpenCVしか使ってないのでMakefile直せばどの環境でもコンパイルできると思う(あとで試す)
ついでにマスク画像を選べるようにしようかと思ったけどOpenCVは透過画像上手くいかないんだった。合成はImageMagick(RMagick)とかを使って自前でやればいいや
こんなふうに実行すると
./haartest -p -c /opt/local/share/opencv/haarcascades/haarcascade_frontalface_default.xml -i ~/Pictures/faces/masatooo-berger.jpg -o result.jpg
複数箇所認識したらそれぞれ位置と範囲が出力される。
x:91, y:169, width:39, height:39 x:93, y:183, width:41, height:41 x:105, y:84, width:85, height:85
この例ではhaarcascadeに顔正面にマッチするものを使ったけど、別のを指定すれば別の部位にマッチする。-oでファイル名を指定して結果をファイルに保存できるし、-pスイッチでOpenCV上でプレビューが出せる。このへんの引数の解析はboost::program_optionsが便利だった。
haartest.cpp
#include "cv.h" #include "highgui.h" #include <boost/program_options.hpp> #include <iostream> using namespace boost; using namespace std; int main(int argc, char* argv[]) { program_options::options_description opts("options"); opts.add_options() ("help,h", "ヘルプを表示") ("cascade,c", program_options::value<string>(), "haarcascade設定ファイル") ("input,i", program_options::value<string>(), "入力画像ファイル名") ("output,o", program_options::value<string>(), "出力ファイル名") ("preview,p", "プレビュー表示"); program_options::variables_map argmap; program_options::store(parse_command_line(argc, argv, opts), argmap); program_options::notify(argmap); if (argmap.count("help") || !argmap.count("cascade") || !argmap.count("input")) { cerr << "cascadeとinputが必要です" << endl; cerr << opts << endl; return 1; } CvHaarClassifierCascade *cascade; cascade = (CvHaarClassifierCascade*)cvLoad(argmap["cascade"].as<string>().c_str(), 0, 0, 0); if(!cascade){ cerr << "error! Cascade not Found" << endl; return -1; } IplImage *image = cvLoadImage(argmap["input"].as<string>().c_str()); if(!image){ cerr << "error! Image File not Found" << endl; return -11; } CvMemStorage *storage = 0; storage = cvCreateMemStorage(0); CvSeq* faces = cvHaarDetectObjects(image, cascade, storage, 1.1, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(30, 30)); bool isOutput = (argmap.count("preview")||argmap.count("output")); for(int i = 0; i < faces->total; i++){ CvRect *rect = (CvRect*)cvGetSeqElem(faces, i); cout << "x:" << rect->x << ", y:" << rect->y << ", width:" << rect->width << ", height:" << rect->height << endl; if(isOutput){ CvPoint center; center.x = rect->x + rect->width/2.0; center.y = rect->y + rect->height/2.0; int r = (rect->width + rect->height)/4.0; cvCircle(image, center, r, CV_RGB(255, 0, 0), 2, CV_AA, 0); } } if(argmap.count("output")){ string out_filename = argmap["output"].as<string>(); cout << "save! " << out_filename << endl; cvSaveImage(out_filename.c_str(), image); } if(argmap.count("preview")){ char winName[] = "haarcascade test"; cvNamedWindow(winName, CV_WINDOW_AUTOSIZE); cvShowImage(winName, image); while (1) { if (cvWaitKey(1) == 'q') break; } cvDestroyWindow(winName); } cvReleaseImage(&image); return 0; }
SRC = haartest.cpp DST = haartest prefix=/opt/local INCPATH=$(prefix)/include LIBPATH=$(prefix)/lib CV_LIBS= -lcv -lcvaux -lcxcore -lhighgui BOOST_LIBS= $(LIBPATH)/libboost_program_options-mt.dylib all: g++ -O $(SRC) -o $(DST) -I$(INCPATH)/opencv -L. -L$(LIBPATH) $(CV_LIBS) -I$(INCPATH)/boost $(BOOST_LIBS)
たとえばRubyで、ディレクトリ内の画像全ての顔をマークするならこう使える
ruby -e 'Dir.glob("*.jpg").each{|f| `~/src/cpp/opencv/study/haar/haartest -c /opt/local/share/opencv/haarcascades/haarcascade_frontalface_default.xml -i #{f} -o #{f}_detected.jpg`}'
上に貼った写真はこれで一発で作成した。