読者です 読者をやめる 読者になる 読者になる

OpenCV 2.4.2で分類器を作る

Python OpenCV

最近、OpenCVで遊んでいて、付属の顔検出用の分類器の精度があまり良くないので、自分で作ってみることにした。ドキュメントがとっ散らかっているので、メモとして残す。

次の画像は付属の分類器を使って検出したもの。False-Positiveも多くて、うまく顔を捕捉できていないのがわかる。

付属分類器

OpenCVはバージョン2.4.2を使った。

手順としては

  1. サンプルの収集
  2. ラベル付け
  3. 分類器の生成

となる。

1. サンプルの収集

今回は、ポジティブサンプルとネガティブサンプル用にあわせて10392枚集めた。

finder

画像検索のAPIでは、Bing Search API on Azureが一番いいと思う。Googleは制限がきつくて、Yahooはクレカの登録が要る。 Bingはこちらで無料プランを登録すれば良い。使うときはAuthヘッダにアカウントキーを入れてやれば良い。スクリプトは以下にあげておいた。

https://github.com/shkh/TrainingAssistant/blob/master/collect_samples.py

2. ラベル付け

集めたサンプルを一枚一枚、ポジティブとネガティブに手作業で分類する。ポジティブサンプルなら対象(顔)の座標を記録する。 そのためのツールを今回は自分で作った。

TrainingAssistant

ボタンはそれぞれ以下のとおり

  • RESET - その画像の座標選択をリセット
  • SKIP - ポジティブにもネガティブにもしない
  • NEXT - 座標が選択されていたらポジティブ、選択されていなければネガティブに分類

その結果、10392枚あったサンプルは

  • ポジティブ : 3302枚
  • ネガティブ : 2561枚

になった。男の顔とか検出しても誰も得しないと思ったので、女性だけポジティブサンプルに入れた。それから極端な横顔などを除いたら、この数になった。 本当はポジティブだけで5000枚くらいが良いらしい。

この作業が大体10時間くらいかかる。ツールはgithubにあげておいた。

https://github.com/shkh/TrainingAssistant

3. 分類器の生成

上のツールでラベル付けをすると、ポジティブサンプルは info.dat に、ネガティブサンプルは bg.txt に一覧が出力される。 中身は下のような書式になっている。

info.dat

static/img/1004.jpg  1  334 58 485 567
static/img/1005.jpg  1  235 173 657 574
static/img/1006.jpg  2  318 38 103 128  179 49 136 152
static/img/1008.jpg  1  270 81 271 365
static/img/1009.jpg  1  816 41 212 235

bg.txt

static/img/10004.jpg
static/img/10005.jpg
static/img/10007.jpg
static/img/10008.jpg
static/img/10009.jpg

これをもとに分類器を生成する。 はじめにポジティブサンプルだけを opencv_createsamples に渡して良い感じに加工する。

opencv_createsamples -info info.dat -vec kawaii.vec -num 3302

(その他の引数の詳細は http://www.makelinux.net/man/1/O/opencv_createsamples)

これで、kawaii.vec が出力されるので、次に、この kawaii.vecbg.txtopencv_traincascade に渡す。その前に出力ファイルが kawaii ディレクトリに保存されるので kawaii ディレクトリを作っておく。

opencv_traincascade -data kawaii/ -vec kawaii.vec -bg bg.txt -numPos 2971 -numNeg 2561 -featureType HAAR -mode ALL

(その他の引数の詳細はhttp://www.makelinux.net/man/1/O/opencv_traincascade)

ここで、-numPos に渡す値に注意が必要で、上の 3302 を指定するとサンプル数が足りなくなって以下のエラーが出る。

OpenCV Error: Bad argument (Can not get new positive sample. The most possible reason is 
insufficient count of samples in given vec-file.

こちらによると、 サンプル数 × 0.9 で大概うまくいくらしいのでそうする。あとは分類器を作ってくれるのを待つ。

それからどうでもいいけど、opencv_haartraining というツールもあるが、これは古いので、若い人は opencv_traincascade を使ったほうが良い。 特徴量も Haar-Like だけじゃなくて、LBP と HOG が新たに使えるようになってる。

最終的な分類器は kawaii/cascade.xmlとして出力される。この他にステージごとの分類器も出力されている。

> tree kawaii
kawaii
├── cascade.xml
├── params.xml
├── stage0.xml
├── stage1.xml
├── stage10.xml
├── stage11.xml
├── stage12.xml
├── stage13.xml
├── stage14.xml
├── stage15.xml
├── stage16.xml
├── stage17.xml
├── stage18.xml
├── stage19.xml
├── stage2.xml
├── stage3.xml
├── stage4.xml
├── stage5.xml
├── stage6.xml
├── stage7.xml
├── stage8.xml
└── stage9.xml

この作業は以下の環境で、プロセスの優先度を-20にして丸2日くらい掛かった。

顔を検出してみる

最後に、先ほど作った分類器を使って実際に顔を検出してみる。OpenCVにはPython用のラッパがついているし、C++とかあまり書きたくないのでPythonを使った。

#!/usr/bin/env python
import cv, cv2

tar = 'sample.jpg'
im = cv2.imread(tar)
cascade = cv2.CascadeClassifier('cascade.xml') #分類器の指定
faces = cascade.detectMultiScale(im, 1.1, 3) #物体の検出

for (x, y, w, h) in faces:
    print x, y, w, h
    center = (int(x+w/2), int(y+h/2))
    radius = int(w/2+5)
    cv2.circle(im, center, radius, cv.RGB(255, 20, 147), thickness=3) #円の描画

cv2.imwrite('result.jpg', im)

という具合で、簡単に書けるので遊べる。

結果は、まあまあ改善した気がする。

mine

おしまい。

  

記事中の画像は modelpiece から拝借した

広告を非表示にする