TadaoYamaokaの開発日記

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

DCNNによるマンガキャラクターの顔パーツ検出

以前の日記でdlibを使用してマンガのキャラクターの顔パーツ検出を行った。

しかし、dlibの方法は、マンガキャラクターには有効ではなく、顔パーツを検出できなかった。

そこで、DCNNを使用して顔パーツ検出(顔器官検出)ができるか試してみた。


dlibで実装されている方法は、くわしくは理解できていないが、論文のタイトルからは回帰木とアンサンブル学習を使用したアルゴリズムのようだ。
実際の人の写真に対してはうまく機能しているが、マンガキャラクターのような線画に対しては機能しないようである。

DCNNは様々な画像に対して汎用性があるため、マンガキャラクターに対しても有効に機能すると思われる。

DCNNは画像の分類に使用される例が多いが、画像分類の場合は、出力として画像の分類に対応するラベルごとの確率を出力することになり、出力層の関数には、softmaxが使用される。

一方、顔パーツを検出する場合は、パーツの検出点(ランドマーク)ごとの位置座標を実数で出力することになる。
そのような場合は、DCNNの出力層を恒等関数、誤差関数を平均2乗誤差(MSE)とし、回帰問題として学習する。

出力層を変えるだけで、画像分類とネットワーク構成は同じで、畳み込み層とプーリング層を複数層つなげる構成とする。

ここでは、ネットワーク構成を、入力層から順に以下の構成とした。

ネットワーク構成
  • フィルター16個、カーネルサイズ4×4、活性化関数をReLUとした畳み込み層
  • Maxプーリング2×2
  • フィルター32個、カーネルサイズ5×5、活性化関数をReLUとした畳み込み層
  • Maxプーリング2×2
  • フィルター64個、カーネルサイズ5×5、活性化関数をReLUとした畳み込み層
  • ノード数400、活性化関数をReLUとした全結合層
  • ノード数を検出点(ランドマーク)数×2とした全結合層

f:id:TadaoYamaoka:20170218110038p:plain

出力層のノード数は、検出点(ランドマーク)のx座標とy座標の値とするため、検出点(ランドマーク)の2倍のノード数となっている。

畳み込み層で、ゼロパティングは行わない。


入力画像は、グレイスケールの100×100の画像とした。

グレイスケールの値の範囲は、0~1(白を0、黒を1)とする。
画像の座標の値の範囲も0~1とする。
これは、値に0~255や0~100を使うと、学習時の計算でオーバーフローが起きるためである。
(実際に試したところ、学習開始後すぐにlossがnanになった。)

学習用とテスト用の画像の準備は、dlibのimglabツール(以前の日記参照)を使用して、手動でコミックから顔画像の矩形範囲と検出点(ランドマーク)を入力して、xmlファイルとして保存する。

これを、画像の中心が顔の中心(左右の眼の中間の位置)になり、画像の幅が左右の目の端から端までの距離の1.25倍となるように正規化する。

学習方法

準備した学習用データからランダムに16個選び、Data Augmentation(以前の日記参照)を行ったものをミニバッチとする。

学習アルゴリズムSGDを使用した。

ChainerとGPU(GeForce 1080)を使用して、ミニバッチを100イテレーション×1000エポック学習させた結果、以下の通りになった。

f:id:TadaoYamaoka:20170204150004p:plain

学習データの損失関数(MSE)の値は、0.00076901まで下がった。
非常に小さい値のようだが、座標の範囲が0~1で、平均2乗誤差であるため、座標のずれとして、100×100倍する必要がある。
つまり、平均2.77(\sqrt{7.69})ピクセルのずれがある。

学習したモデルを使用して、学習用データとは別のテスト用データで、顔パーツを予測すると以下の通りとなった。

f:id:TadaoYamaoka:20170204150240p:plain

検出点(ランドマーク)は、顔の輪郭3点、左右の目それぞれに4点、口に4点である。

目に関しては正確に検出できている。
輪郭の中心と、口の位置がずれているが、たいぶ近い位置が検出ができているのではないだろうか。

画像の傾きにも堅牢なようだ。

しかし、以下のようなデフォルメした表現の場合は、ずれが大きくなっている。

f:id:TadaoYamaoka:20170204150655p:plain

RMSpropで学習

上記ではSGDで学習を行ったが、SGDは収束が遅いため、他のアルゴリズムの方が有効な場合がある。

そこで、RMSpropを使用して学習を行ってみた。

学習係数をデフォルト(lr=0.01)のまま学習させると、以下のように値が発散した後、勾配消失してしまい固定座標しか出力しなくなってしまった。

f:id:TadaoYamaoka:20170204151056p:plain


学習係数を調整して、lr=0.0001としたところうまく学習できた。

f:id:TadaoYamaoka:20170204151302p:plain

学習データの損失関数の値は、0.0000306まで下がった。
SGDの損失関数の値の約0.04倍であり、より早く収束できている。


RMSpropで学習したモデルを使用して、テスト用データの顔パーツを予測すると以下のようになった。

f:id:TadaoYamaoka:20170204151816p:plain

SGDで学習したモデルではデフォルメした目では座標が大きくずれていたが、RMSpropで学習したモデルでは、より近い位置が検出できている。

f:id:TadaoYamaoka:20170204151955p:plain

目と口がはっきりした画像では、かなり正確な位置が検出できている。

まとめ

DCNNによる顔パーツ検出は、マンガキャラクターに対しても有効であることが確かめられた。

使用したソースコードは、GitHubで公開したので、同じことを試したい方は参考にしてほしい。(学習データはアップできないので、各自で用意してほしい。)
github.com


以上、DCNNと顔パーツ検出をオリジナルブレンドしてみた結果報告でした。