OpenCV

トップ > チップス > OpenCV
2014-03-07, opencv

HaarCascadeによる学習と物体認識

機械学習などなど、そこに出てくる理論の何たるかをさっぱり理解出来ていないのですが…便利な時代になったもので、ほとんど門外漢でも物体認識のプログラム(と、それに必要なアルゴリズムのトレーニング)が出来てしまいました。勿論、まだまだ改善点、チューニングの必要なところは沢山ありますし、そもそも勘違いしてる箇所もあるかもしれませんので、ご利用は計画的に。

今回、CentOS6.0を使いました。OpenCVのインストールが簡単だからですね。以下のコマンドで準備完了です。

# yum install opencv

認識させたい画像(=positive samples)を用意します。今回は自社のロゴマークを使いました。

それから、背景画像(=negative samples)です。どうやら数千枚という単位が必要らしく、ここで困ってしまったのですが、以下のURLでサンプルを配布してくれていたので借りることにしました。有難い…。

Subversionのコマンドで手元に取ってくることが出来ます(3000枚くらいです)。

$ svn export http://tutorial-haartraining.googlecode.com/svn/trunk/data/negatives/

以下のようなモノクロのJPEGファイルとして集められているようです(掲載用に縮小していますが、実際は横幅が640pxありました)。

これで材料が揃いましたので、Haarのトレーニングを開始します。ポジティブサンプルはlogo.png、ネガティブサンプルはnegatives/フォルダ内に格納されています。パラメタはほとんど指定していませんが、haartrainingの「-mem 4000」を指定しないと何故か「Segmentation fault」で落ちてしまう現象が発生してしまいました。

$ find negatives/ > negatives.dat
$ opencv_createsamples -img logo.png -bg negatives.dat -vec haar.vec
$ opencv_haartraining -data haar -vec haar.vec -bg negatives.dat -mem 4000

さくらのクラウドで8CoreCPU、メモリ16Gのマシンを調達して実行したところ、サンプルの作成から完了まで大体20分程度の所要時間でした。ネットで似たようなことをしている人の話を読むと「数日」という単位も珍しくなさそうでしたので、今回ははっきりいってトレーニング不足かもしれません…が、別にオリンピックを目指す訳ではないので、このまま進みます。

処理が正常に完了していれば、カレントディレクトリにhaar.xmlというファイルが生成されているはずです。これを使って次に進みます。

以下はiOSのプロジェクトにOpenCVのライブラリを組み込んで記述したコードの例です。まずは先程生成されたxmlを読み込んで分類器を作成します。

cv::CascadeClassifier* cascade;

NSString* haar = [[NSBundle mainBundle] pathForResource:@"haar" ofType:@"xml"];
cascade = new cv::CascadeClassifier();
cascade->load([haar UTF8String]);

次にこれを使って入力画像(Mat型、src_img)内を探索します。後半のループ部分では発見されたオブジェクトを円で囲む処理をしています。

double scale = 2.0;
cv::Mat w1, w2(cv::saturate_cast<int>(src_img.rows/scale),
                           cv::saturate_cast<int>(src_img.cols/scale),
                           CV_8UC1);
// グレースケールに変換
cv::cvtColor(src_img, w1, CV_RGB2GRAY);
// 処理時間短縮のために画像を縮小
cv::resize(w1, w2, w2.size(), 0, 0, cv::INTER_LINEAR);
cv::equalizeHist(w2, w2);

// 探索 引数は、画像,出力矩形,縮小スケール,最低矩形数,(フラグ),最小矩形
std::vector<cv::Rect> faces;
cascade->detectMultiScale(w2, faces, 1.1, 2, CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));

std::vector<cv::Rect>::const_iterator r = faces.begin();
for(; r != faces.end(); ++r) {
    // 検出結果の描画
    cv::Point face_center;
    int face_radius;
    face_center.x = cv::saturate_cast<int>((r->x + r->width*0.5)*scale);
    face_center.y = cv::saturate_cast<int>((r->y + r->height*0.5)*scale);
    face_radius = cv::saturate_cast<int>((r->width + r->height)*0.25*scale);
    cv::circle(src_img, face_center, face_radius, cv::Scalar(0,255,0), 3, 8, 0 );
}

しばしば誤検出もあるのですが、以下のように、探したいパターンが画面上に現れると反応してくれるようになりました。不思議ですね…。

参考URL

この記事は役に立ちましたか?