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

Perlでアニメ顔を検出&解析するImager::AnimeFace

というのを作ったので自己紹介します。
2月頃から、コンピュータでアニメ顔を検出&解析する方法をいろいろ試しつつ作っていて、その成果のひとつとして、無理やり出力したライブラリです。

はじめに

はじめにざっとライブラリの紹介を書いて、あとのほうでは詳細な処理の話を僕の考えを超交えつつグダグだと書きたいと思います。

Imager::AnimeFaceでできること

Imager::AnimeFaceは、画像に含まれるアニメキャラクター的な人物の顔の位置を検出し、さらに目や口など顔を構成する部品位置や大きさの推定、肌や髪の色の抽出を簡単に行うことができるライブラリです。
これらが可能になると、

  • 画像から自動でいい感じのサムネイルを作成できる
  • 動画から自動でいい感じのサムネイルを作成できる
  • 自動的にぐぬぬ画像が作れる
  • 自動的に全員の顔を○○にできる
  • 顔ベースのローカル画像検索

など、最新鋭のソリューションが次元を超え可能になる……が、正直まだまだな感じで、あまりいいものではないです。

実際の機能は、このライブラリを用いたデモページを置いたので、見ていただければ分かりやすいですが、キャプチャーを引用すると、入力された画像から次のような情報を得ることができます。
f:id:ultraist:20090412063320p:image
見るからに分かる機能として、

  • 与えられた画像の中からキャラクターの顔の位置を求める(検出)
  • 顔の部品である「目」「鼻」「口」「あご先」の位置を"推定"する
  • 「髪の色」を抽出する
  • 検出された顔について顔らしさのスコアを求める

があります。
他にも、

  • 「肌の色」、「右目の代表的な4色」、「左目の代表的な4色」を抽出する

などができます。この情報はちょっと量が多すぎて表示が汚くなるのでデモには載せていません。ライブラリとしては他の情報と同じように得ることが出ます。
f:id:ultraist:20090412063319p:image
開発環境のキャプチャーだと上のような結果になります。
(画像はヤスヒロさん撮影の写真)
これらの機能中で特徴的なものとして、顔部品の推定があります。詳細はあとのほうで書きますが、大まかに書くと、顔の局所的な特徴を細かくチェックしながら部品を検出しているのではなく、顔全体の特徴から各部品の大体の位置と大きさを求めています。人間の場合と違って、アニメ顔は鼻や口が無い「顔」が多く存在することや、絵師によって目の書き方が大きく違うなど、個体差が激しいのでこのような実装になっています。これによって、実際には口や鼻がなくても口や鼻があった場合の妥当な位置をアプリケーションが得ることが出ます。ただし、あくまで計算によって得られた大体の値なので間違える場合もあります。精度についてはデモを使ってもらうと分かると思います。ライブラリでは肌や髪の色を抽出をしているので、これらの情報を使ってより精度のよい部品位置情報をアプリケーション側で独自に計算するもできます。

機能的にかぶっているものとして僕が以前から公開しているOpenCVのcvHaarDetectObjectsによるアニメ顔を検出する分類器のXMLがありますが、よりよい点として、

  1. アニメ顔検出の精度が高い(OpenCV版では検出できない顔も検出できる)
  2. 顔の部品位置や色情報もアプリケーションから簡単に取得できる
  3. OpenMPによる並列化に対応しており実行速度をコア数に対してほぼ線形に向上できる
  4. 特徴抽出から学習・検出にいたるまでほぼオレ実装(C言語)
  5. CUDAにも対応しており一般的なPCでもCore2Quad 4コアのさらに4倍の速度で実行できる(オレ専用)

があります。下ふたつは僕しかうれしくないことですが、僕にとってはとてもうれしいことです。

デモ

デモのアドレスは、

Imager::AnimeFace - anime face detection demo

です。
パラメーターminimum face sizeというのは、検出窓の初期サイズで、ようは検出できる顔の最小サイズです。小さくすると小さい顔も検出できるようになりますが、その分遅くなります。デフォルトの42.592はさまざまな状況を考慮して求められた究極の値なので、できるだけこのままが望ましいです。

デモを晒しつつ言い訳したい箇所を列挙すると

  1. 顔は±30〜40°以上傾いていると意図的に検出していない
  2. 結構遅い
  3. 結構間違う

があります。
全方向検出はできるようにしたかったのですが、速度的に問題があって、今ははずしています。設計上は、ある領域が顔であった場合の顔の傾きを予測して、傾きごとに識別器を切り替えるようにしていますが、実装上は傾きが±30°を超えてそうだったら、「顔以外」と判定して捨てています。
デモの動作が遅いのは、現在デモを置いているサーバの環境の問題でOpenMPモードで動いていないからです。設置しているさくらインターネットFreeBSDではセマフォの機能がカーネルに読み込まれていないらしく、libgompを動的ロードするとBad system callエラーで落ちてしまうので、OpenMPを有効にできていません。なので2GHzという比較的遅いCPUのシングルスレッド動いています。現在OS移行中らしいので、もしかしたらそれによって有効にできるかもしれない……と期待を持っています。
3については(^o^)\
あと、デモのバグっぽいものとして、右の画像検索で別ページのリンクが動かなくことがありますが、Google的ななにかです。

Imager::AnimeFaceの簡単な使い方

Imager::AnimeFaceは、Imagerで読み込んだ画像を渡すと検出と解析を同時に行って結果の配列(のリファレンス)を返す「detect_animeface」というひとつの関数を持っています。
結果を表示するだけのサンプルは次のようになります。

use Imager;
use Imager::AnimeFace;
use Data::Dumper;

# 画像を読み込む
my $im = Imager->new();
$im->read(file => 'test.jpg');

# 検出
my $results = detect_animeface($im);

# 結果表示!
foreach my $face (@{$results}) {
    print Dumper($face);
}

非常にシンプルで簡単で、とても簡単ですね。
ここでの$resultsの要素である$faceはひとつの顔情報を表す連想配列のリファレンスになっています。メンバーは次のとおり。

$face = {
  # 顔らしさを表す尤度的なもの。
  # 0.0〜1.0範囲で1に近いほど顔らしい
  # 0.5以下は顔以外と判断して捨てているので0.5以上しか返ってこない
  # あまり信憑性のある値では…ない…
  'likelihood' => '0.99777740240097',
  # 顔の位置と大きさの情報
  'face' => {
      'x' => 207,       # 顔領域のX位置(左上の点)
      'y' => 282,       # 顔領域のY位置(左上の点)
      'width' => 43,    # 顔領域の横幅 
      'height' => 43    # 顔領域の縦幅(現在width=height)
    },
  # 鼻の位置の情報
  # 大きさ情報は含まれずwidth, heightは常に1
  'nose' => {
      'x' => 229,
      'y' => 305,
      'width' => 1,
      'height' => 1
    },
  # 口の位置と大きさの情報
  'mouth' => {
       'x' => 223,
       'y' => 308,
       'width' => 13,
       'height' => 9
     },
  # あご先の位置の情報
  # width,heightは常に1
  'chin' => {
      'x' => 229,
      'y' => 320,
      'width' => 1,
      'height' => 1
    },
  # 目の情報
  'eyes' => {
      # 左目の位置と大きさの情報
      'left' => {
          'y' => 294,
          'x' => 234,
          'width' => 11,
          'height' => 9,
          # 左目の代表色(Imager::Color)
          'colors' => [
                bless( do{\(my $o = 134529520)}, 'Imager::Color' ),
                bless( do{\(my $o = 134529536)}, 'Imager::Color' ),
                bless( do{\(my $o = 134529552)}, 'Imager::Color' ),
                bless( do{\(my $o = 134529568)}, 'Imager::Color' )
              ]
        },
      # 右目の位置と大きさの情報
      'right' => {
           'x' => 215,
           'y' => 295,
           'width' => 10,
           'height' => 8,
           # 左目の代表色(Imager::Color)
           'colors' => [
                 bless( do{\(my $o = 134529584)}, 'Imager::Color' ),
                 bless( do{\(my $o = 134529600)}, 'Imager::Color' ),
                 bless( do{\(my $o = 134529616)}, 'Imager::Color' ),
                 bless( do{\(my $o = 134529632)}, 'Imager::Color' )
               ]
         }
    },
  # 髪の色 (Imager::Color)
  'hair_color' => bless( do{\(my $o = 134529504)}, 'Imager::Color' ),
  # 肌の色 (Imager::Color)
  'skin_color' => bless( do{\(my $o = 134529488)}, 'Imager::Color' )
};

detect_animefaceの第2引数以下は、オプション引数になっていて、次のオプションを指定できます。

detect_animeface(
  # Imagerのオブジェクト
  $im,
  # 検出窓の初期サイズ(デフォルト42.592)
  window => 42.592,
  # 検出窓の移動幅(デフォルト:4.0)
  step => 4.0,
  # 検出窓のアップスケール比(デフォルト:1.095)
  scale_factor => 1.095
);

まず、検出窓とは、画像の中をスキャンしている小さな四角い領域です。検出処理ではこの領域に顔が含まれるか?を判定しながら、少しずつ領域を動かして顔を探していきます。あるサイズの検出窓で画像全体をスキャンしたら、検出窓を少し大きくして再度全体をスキャンします。これを検出窓が画像に入りきらなくなるまで続けます。
このとき、windowが検出窓のスキャン開始時のサイズ(px)です。これが小さいと小さな顔も検出できますが、スキャンの回数が増えるので実行速度は遅くなります。大きいと実行速度は速くなりますが、小さな顔が検出できなくなります。あらかじめ画像に含まれる顔のサイズが予測できない場合は、デフォルトの42.592がオススメです。基本的には32以下にできない設計ですが、スーパーハカーテクニックにより16まで小さくできます。ただし32以下では一部の特徴が破滅するため精度はかなり落ちます。
stepは、検出窓を移動させる移動量です。単位は検出窓を32pxとした場合のピクセルです。これを小さくすると検出精度が向上し検出される顔の位置がより正確になりますが、X,Yと2軸あるので実行速度はかなり遅くなります。大きくすると実行速度は飛躍的に向上しますが、検出精度は下がり顔の検出位置もズレて部品位置の推定が残念な結果になりやすくなります。
scale_factorは、検出窓を大きくする倍率です。たとえば、1.1を指定すると、検出窓は1.1倍ずつ大きくなります。

Imager::AnimeFaceのインストール方法

最新版はこちらにあります。(2009/09/08追記)
最新版はCLAPACKを含んでいるため1の手順は不要になっています(2009/11/24追記)
http://anime.udp.jp/imager-animeface.html

手順としては、糞メンドウクサくて、

  1. CLAPACKのインストール (数値計算ライブラリ)
  2. nvxsのインストール (XS用にまとめた顔検出部分のオレ画像処理ライブラリ)
  3. Imagerのインストール (Perlの画像処理ライブラリ)
  4. Imager::AnimeFaceのインストール

となっています。
ここではさくらインターネットレンタルサーバーのホームディレクトリにインストールした手順を書きます。

環境変数の設定

インストール先を$HOME/localとすると、tcshの場合、

setenv PREFIX "$HOME/local"
setenv LD_RUN_PATH "$PREFIX/lib"
setenv CFLAGS "-I$PREFIX/include"
setenv LDFLAGS "-L$PREFIX/lib"

などと設定しておきます。
このとき、OpenMPを使う場合は、-fopenmpをCFLAGSに追加しておきます。(OpenMPに対応したgccが必要)

CLAPACKのインストール

固有値の計算にCLAPACKという数値計算ライブラリを使っているためインストールします。
http://www.netlib.org/clapack/から
http://www.netlib.org/clapack/clapack.tgz
を取ってきて、tar -xzvf clapack.tgzなどと展開します。
中に移動して、make.inc.exampleというファイルをmake.incリネームしてmakeします。
makeすると次のファイルができているので、$PREFIX/libにリネームして移動します。

mv ./lapack_LINUX.a $PREFIX/lib/liblapack.a
mv ./tmglib_LINUX.a $PREFIX/lib/libtmglib.a
mv ./blas_LINUX.a $PREFIX/lib/libblas.a
mv ./F2CLIBS/libf2c.a $PREFIX/lib/libf2c.a

ヘッダーファイルも移動します。

mv ./INCLUDE/* $PREFIX/include
nvxsのインストール

http://www.udp.jp/software/nvxs-1.0.tar.gz
を取ってきて、解凍して、中で
./configure --prefix=$PREFIX
make install

で終わりのはず…。
SSE2に対応していないCPUの場合は、nv_core/nv_config.hのNV_ENABLE_SSE2を0にして、-msse2などのオプションを削除します。

Imagerのインストール

CPANライブラリをホームディレクトリにインストールする方法をググって設定する。
その後、
cpan
install Imager

Imager::AnimeFaceのインストール

http://www.udp.jp/software/Imager-AnimeFace-1.00.tar.gz
を取ってきて、解凍して、中で
perl Makefile.PL PREFIX=$PREFIX
make install
で終わりのはず…。
examplesの下に適当なサンプルとデモで使っているCGIが入ってます。

Imager::AnimeFaceについて

Perlのライブラリ配布は初めてです。変かもしれません。
Cライブラリのほうはautotoolsが無チェックで進んでいるので、環境によってはこけるかもしれません。基本的にはC言語の標準ライブラリ以外を使っていないですが、ごく一部でSSE2というCPU固有の命令を使っています。コンパイラgcc以外を考えていません。
ライセンスは…どうでもいい…、と思っていますが、一応、NYSLと宣言しておきます。
いきなりバージョン1.0なのは、金or時間があればここ2ヶ月で得た知識を元にガッとやりなおしたい、もうコレはダメダ、限界だ、という強い思いからです。

処理の詳細

ここからが本番だ……。



と思ってましたが、体力の限界なのでまた今度にします。
簡単に書くと、

  • 全体的に古い手法で構成されている(最新手法を使えるようがんばりたいデス)
  • 色抽出以外はニューラルネットワーク
  • エッジを使った枝狩りで高速化
  • Integral Imageを使った高速な特徴抽出
  • 顔の判定は、7万件の顔パターンと3億件の顔以外パターンから選別した17万件のパターンでオレ学習した多層パーセプトロン(2クラス分類器x3)
  • 顔部品の推定は、6千件の教師データで学習した多層パーセプトロン(非線形回帰)
  • 色の抽出は部品位置 + 正規分布 + k-means
  • SSE2によるベクトル演算の高速化
  • OpenMPのダイナミックスケジューリングによる検出処理の高速化
  • CUDA(オレ専用)
  • etc

というのを合体させた感じです。
統計&機械学習は入門レベルだけど、コンピュータにはわりと強い人間ががんばって勉強しながら作りました的なものになっているので、よく分からないなりにこう考えると分かった気がしてなんかできたぜ、などという話をそのうち…。

追記 4/13

detect_animefaceのオプション「min_windows_size」を「window」に修正。

追記 5/9

注: 最新版はこっちにあります。近いうちに公式ページを作る予定です。
nvxs-1.0.1とImager::AnimeFace-1.01 - デー