TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

dlibでマンガの顔認識をやってみた

昨日の日記Bash on Windowsを使ってdlibで顔検出を行うことができたので、自分で用意した画像を使って、顔検出ができるか試してみた。

↓この記事のようにアニメの画像での顔認識を試した例はいくつかありましたが、マンガでの例がなかったので試してみました。

kivantium.hateblo.jp


マンガの場合、吹き出しやコマ、集中線、オノマトペなどがあり、学習データで顔の領域を囲うときにノイズを含むことになります。認識対象の画像についても同様です。
HOG特徴とSVMによる手法とdlibの実装がどれくらいノイズに対して頑強か興味があるところです。

では、試した手順と結果について以下に示します。

学習データの準備

学習データとして、画像ファイルと顔の領域を記述したxmlデータを用意する必要があります。

xmlデータは、dlibに付属するimglabツールで作成できます。

まず、imglabツールをビルドします。
dlibのソースをダウンロードして解凍します。(昨日の日記の手順と同じなので、実施済みの場合は不要)

wget https://github.com/davisking/dlib/archive/v19.1.zip
unzip v19.1.zip

imglabのビルドに必要なパッケージをインストールします。

sudo apt-get install libopenblas-dev liblapack-dev

imglabをビルドします。README.txtに記載の通りの手順です。

cd dlib-19.1/tools/imglab
mkdir build
cd build
cmake ..
cmake --build . --config Release
sudo make install

これで、imglabコマンドが使用可能になります。


適当な場所に学習データを格納するディレクトリを作成し、imagesサブディレクトリを作成し、その中に学習対象の画像ファイル(.jpg)を格納します。
imagesディレクトリに移動して、以下のオプションでimaglabコマンドを実行します。

imglab -c mydataset.xml .
imglab mydataset.xml

ウィンドウが起動するので、学習対象の画像の顔の領域をShiftを押しながらマウスをドラッグして囲っていきます。
領域はおおざっぱで大丈夫ですが、縦や横に長すぎると学習時にエラーになるので、正方形に近い形で囲います。

全てのファイルについて顔の領域を囲ったら、メニューからFile→Saveをクリックします。

mydataset.xmlとimage_metadata_stylesheet.xslが作成されます。

mydataset.xmlをブラウザで表示すると、囲った領域が赤枠で表示された形で確認できます。(Chromeだとうまく表示できませんでしたが、FireFoxだと表示できました。)

領域を囲うのに失敗した場合は、mydataset.xml

<box top='233' left='256' width='83' height='73'/>

といった行を削除して、もう一度「imglab mydataset.xml」コマンドを実行すれば、追加で領域を囲うことができます。
※2016/9/23追記:XMLを編集しなくてもダブルクリックしてDeleteキーで削除できました。

学習

上記で作成したディレクトリ(imagesの親ディレクトリ)に、dlibのサンプル(dlib-19.1/python_examples/train_object_detector.py)を修正したスクリプト(train_object_detector.py)を作成します。

train_object_detector.py
#!/usr/bin/python
import os
import sys
import glob

import dlib
from skimage import io

if len(sys.argv) != 2:
    print(
        "Give the path to the examples/faces directory as the argument to this "
        "program. For example, if you are in the python_examples folder then "
        "execute this program by running:\n"
        "    ./train_object_detector.py ../examples/faces")
    exit()
faces_folder = sys.argv[1]

options = dlib.simple_object_detector_training_options()
options.add_left_right_image_flips = True
options.C = 5
options.num_threads = 4
options.be_verbose = True

training_xml_path = os.path.join(faces_folder, "mydataset.xml")
dlib.train_simple_object_detector(training_xml_path, "detector.svm", options)

※長くなるので元のサンプルからコメントを除いています。

元のサンプルでは、学習の後、検出まで行うようになっていますが、学習のみ行うようにしています。

以下のコマンドで学習を行います。

./train_object_detector.py images

今回は10ページ分の画像で学習しましたが、学習に数分かかりました。
学習が完了すると、detector.svmが作成されます。

顔検出

学習に使用した画像とは異なる画像で、顔検出が行えるか確認します。

上記と同じディレクトリに、dlibのサンプルを修正して顔検出のみを行うスクリプト(detect_object_detector.py)を作成します。

detect_object_detector.py
#!/usr/bin/python
import os
import sys
import glob

import dlib
from skimage import io

if len(sys.argv) != 2:
    exit()
faces_folder = sys.argv[1]

detector = dlib.simple_object_detector("detector.svm")

win_det = dlib.image_window()
win_det.set_image(detector)

print("Showing detections on the images in the faces folder...")
win = dlib.image_window()
for f in glob.glob(os.path.join(faces_folder, "*.jpg")):
    print("Processing file: {}".format(f))
    img = io.imread(f)
    dets = detector(img)
    print("Number of faces detected: {}".format(len(dets)))
    for k, d in enumerate(dets):
        print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format(
            k, d.left(), d.top(), d.right(), d.bottom()))

    win.clear_overlay()
    win.set_image(img)
    win.add_overlay(dets)
    dlib.hit_enter_to_continue()


検出対象の画像ファイル(.jpg)を、targetsサブディレクトリを作成して格納します。

以下のコマンドで検出を実行します。

./detect_object_detector.py targets

ウィンドウが起動し、顔の領域が赤枠で囲われて表示されます。
コンソールでEnterを押すと次の画像が表示されます。

結果

以下の通り、マンガの画像でも、ほぼ正しく顔検出が行えました。

f:id:TadaoYamaoka:20160921183221p:plain

一部検出できていない領域もありますが、吹き出しがかぶっている領域でも正しく検出できています。
学習データに使用したのは10ページ分ですが、ここまで検出できるのはすごいですね。
ひと目で、尋常でない検出率だと見抜いたよ。