OpenCVで遊んだ
同期とOpenCVやらARToolkitやらの環境を構築して遊んでみた.以下はWebカメラから動画を取り込み,リアルタイムに顔検出するコード.
参考:カメラ利用の基本形 - OpenCV@Chihara-Lab.
opencv.jp - OpenCV-1.0:CV 物体検出(Object Detection)リファレンス マニュアル -
#include <iostream> #include <opencv/cv.h> #include <opencv/highgui.h> int main(int argc, char *argv[]){ int key = 0; CvCapture* capture = NULL; IplImage* captureImage; IplImage* outputImage; if(NULL==(capture = cvCaptureFromCAM(-1))) { std::cout << "カメラが見つかりません" << std::endl; return -1; } char* captureWindow = "Capture"; char* outputWindow = "Output"; cvNamedWindow(captureWindow, CV_WINDOW_AUTOSIZE); cvNamedWindow(outputWindow, CV_WINDOW_AUTOSIZE); // 正面顔検出器の読み込み CvHaarClassifierCascade* cvHCC = (CvHaarClassifierCascade*)cvLoad("haarcascade_frontalface_default.xml"); // 検出に必要なメモリストレージを用意する CvMemStorage* cvMStr = cvCreateMemStorage(0); // 検出情報を受け取るためのシーケンスを用意する CvSeq* face; while(1){ captureImage = cvQueryFrame(capture); outputImage = cvCloneImage(captureImage); // 画像中から検出対象の情報を取得する face = cvHaarDetectObjects (outputImage, cvHCC, cvMStr, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(50.0, 50.0)); for (int i = 0; i < face->total; i++) { //検出情報から顔の位置情報を取得 CvRect* faceRect = (CvRect*)cvGetSeqElem(face, 0); // 取得した顔の位置情報に基づき、矩形描画を行う cvRectangle(outputImage, cvPoint(faceRect->x, faceRect->y), cvPoint(faceRect->x + faceRect->width, faceRect->y + faceRect->height), CV_RGB(255, 0 ,0), 3, CV_AA); } cvShowImage(captureWindow, captureImage); cvShowImage(outputWindow, outputImage); cvReleaseImage(&outputImage); key = cvWaitKey(1); if (key == 0x1b) break; } // 用意したメモリストレージを解放 cvReleaseMemStorage(&cvMStr); // カスケード識別器の解放 cvReleaseHaarClassifierCascade(&cvHCC); // キャプチャの解放 cvReleaseCapture(&capture); // ウィンドウの破棄 cvDestroyWindow(captureWindow); cvDestroyWindow(outputWindow); return 0; }
第一感はお手軽!そしてメモリリークこえー!みたいな感じ.これは慣れだろうなあ.コードは殆どサンプルのまんま.一点だけ注意が必要なのは,cvHaarDetectObjectsの引数.静止画を弄るときと同様に引数の一部を省略し
// 画像中から検出対象の情報を取得する
face = cvHaarDetectObjects(outputImage, cvHCC, cvMStr);
と書くと,割と使い物にならないくらい遅くなる.第4〜第6引数を適切に与えることで,枝刈りが効果的に働く(詳細はリファレンス参照).調子に乗って,顔を宮崎あおいに置き換えるバージョンも作ってみた.矩形描画を画像のリサイズと合成に書き換えればOK.ちゃんと動画で実用(??)レベルの速度が出る.
参考:OpenCVで顔認識→笑い男アイコン貼り付け - ぬいぐるみライフ(仮)
for (int i = 0; i < face->total; i++) { //検出情報から顔の位置情報を取得 CvRect* faceRect = (CvRect*)cvGetSeqElem(face, 0); IplImage* aoi_resized = cvCreateImage(cvSize(faceRect->width, faceRect->height), outputImage->depth, outputImage->nChannels) cvResize(aoi, aoi_resized, CV_INTER_CUBIC); int x = faceRect->x, y = faceRect->y; const int i_max = ((x + aoi_resized->width ) > outputImage->width ) ? outputImage->width - x : aoi_resized->width; const int j_max = ((y + aoi_resized->height) > outputImage->height) ? outputImage->height - y : aoi_resized->height; for (int j = 0; j < j_max; ++j){ for (int i = 0; i < i_max; ++i){ int r = aoi_resized->imageData[aoi_resized->widthStep * j + i * 3]; int g = aoi_resized->imageData[aoi_resized->widthStep * j + i * 3 + 1]; int b = aoi_resized->imageData[aoi_resized->widthStep * j + i * 3 + 2]; if (r || g || b) { outputImage->imageData[outputImage->widthStep * (y+j) + (x+i) * 3] = r; outputImage->imageData[outputImage->widthStep * (y+j) + (x+i) * 3 + 1] = g; outputImage->imageData[outputImage->widthStep * (y+j) + (x+i) * 3 + 2] = b; } } } cvReleaseImage(&aoi_resized); }