Face APIで顔検出を試してみる


先日リリースされたGoogle Play Serivces 7.8ですが、新しく Face APIという顔検出用APIが追加されました。

*  [Face Detection in Google Play services](http://android-developers.blogspot.jp/2015/08/face-detection-in-google-play-services.html)

このAPIは *Mobile Vision API* としてバーコードAPIなどと合わせて提供されます。
7.8の新機能に *Nearby Messages* など面白そうなものがありますが、顔検出は前々から興味があったので、このような形でAPIがリリースされ嬉しく思います。

元々Android APIにも [FaceDetector](http://developer.android.com/reference/android/media/FaceDetector.html)クラスがあったのですが、顔の矩形が取れず、いまいち使いづらかったのですが、Face APIでは取得できるようになりました。
このあたり、自分でやるのはめんどいですからね('A`) 

## Face APIの使い方

基本的な使い方はチュートリアルとサンプルから知ることができます。

* [チュートリアル](https://developers.google.com/vision/detect-faces-tutorial)
* [サンプル(phot-demoプロジェクト)](https://github.com/googlesamples/android-vision)

### ライブラリ導入

Face APIを使用するためにbuild.gradleに次のコードを追加します。

``` groovy
dependencies {
    compile 'com.google.android.gms:play-services-vision:7.8.0'
}
```

### FaceDetectorクラスの生成

画像から顔を検出するためのオブジェクトを生成します。

```java
FaceDetector FaceDetector = new FaceDetector.Builder(this)
        									.setTrackingEnabled(false) // 顔検出するだけなので無効にしておく
                                            .setLandmarkType(FaceDetector.ALL_LANDMARKS)
                                            .setMode(FaceDetector.ACCURATE_MODE)
                                            .build();

```

パラメータの補足。

* #setLandmarkType(int)  
デフォルトでは *NO_LANDMARKS* ですが、 *ALL_LANDMARKS* を設定することで<del>複数の顔を検出できるようになります</del>検出精度が向上します。
* #setMode(int)  
デフォルトでは *FAST_MODE* ですが、 *ACCURATE_MODE* に設定することでより多くの顔を検出できます。

その他Builderに設定できるパラメータはリファレンスを参照。

* [FaceDetector](https://developers.google.com/android/reference/com/google/android/gms/vision/face/FaceDetector.Builder.html)

検出の精度をある程度上げたい場合は設定を変えることができますが、検出時間も長くなりますので注意です。

### 顔検出

最後に顔検出を実行します。

```java
Bitmap bitmap = ... // 検出対象の画像を準備

Frame frame = new Frame.Builder().setBitmap(bitmap).build();
SparseArray<Face> faces = detector.detect(frame);

for (int i = 0, size = face.size(); i < size; i++) {
	// 何かする
}
```

検出できる項目はリファレンスを参照。

* [Face](https://developers.google.com/android/reference/com/google/android/gms/vision/face/Face)

このように書くだけで簡単に画像から顔検出が行えます。サンプルの検出結果は次のようになります。

![Sample](https://developers.google.com/vision/images/face-with-landmarks.png)

サンプルでは検出した顔のランドマークを視覚化したものになります。かなり精度がよさそうですね。

Face APIの初回使用時なると思われるのですが、ネイティブライブラリをダウンロードするので、終わっていないるかどうか判定するためのメソッドも用意されています。

```java
if (!detector.isOperational()) {
    // ライブラリがダウンロードされていない場合の処理
}
```

アプリに組み込む場合は念のため対策していたほうがよさそうですね。

## 顔検出の精度

Googleのサンプルではアップのお姉さんの画像を使用していましたが、ソレ以外の場合も検出してくれるのかというのが気になったので、サンプルアプリを作ってみました。

その際、人物の画像が必要でしたので次のサイトのコスプレイヤーの方の画像を使用させていただきました。
画像を公開してくださった方にはこの場を借りてお礼申し上げます。（CCライセンスはアプリ内の画面に表示しています）

*  [フリー画像・写真素材集](http://free-images.gatag.net/tag/cosplay)

サンプルアプリはコスプレイヤーの方をリスト表示する際に、顔検出していい感じに表示されるか確認することができるものになります。ソースコードはGitHubにあります。

* [FaceDetection](https://github.com/droibit/facedetection)

### 検出結果

検出する際のパラメータは高精度になるようにしています。

```java
#setLandmarkType(FaceDetector.ALL_LANDMARKS);
#setMode(FaceDetector.ACCURATE_MODE);
``` 

検出結果は次のようになります（画面を一部抜粋）。

![結果](http://cl.ly/image/0E2d1s1H1X1c/download/image.jpg)

### パラメータによる違い

`#setMode(int)` と `#setLandmarkType(int)` を変更してみての違いは次のようになります。

![パラメータの違い](http://cl.ly/image/262j3i220h2v/download/image2.jpg)

精度的には `ACTUAL_MODE + ALL_LANDMARKS` > `ACTUAL_MODE` > `ALL_LANDMARKS` になりました。 

### 所感

今回試した限りでは一番精度がでるパラメータの場合 **約85%** の顔が検出されました（画像内に複数の場合も含む）ので精度は良いといえるのではないでしょうか。

見栄え良く表示するため、検出された顔の矩形が髪のことを考慮しない場合があるので、少し大きめに矩形をとるようにしています。

しかし、顔の向きだったり、離れていたり、被り物があったり、メガネ？ だったりすると検出されないことがありますが、これは仕方がない部分かと思います。

また、今回のようにリスト表示するとアイコンが表示されるまでの待ち時間が気になります（Nexus5使用）。リスト表示する場合に、その場で顔検出するのは向いていないようですので、予め検出したサムネイル画像を作っておくのがいいのではないでしょうか。

以上、顔検出APIの使い方でした。