カメラプレビューで顔検出を行う


Androidでは以前からFaceDetectorクラスを用いることで画像に対して顔検出を行うことが可能でした。Android4.0(ICS)からは、画像(静止画)だけでなく、カメラのプレビュー(動画)に対して顔検出を行うことが新たに可能となりました。

実際に顔検出の処理を行う方法は以下の通りです。1から3の処理を記述する必要があります。

  1. FaceDetectionListenerを実装(onFaceDetectionメソッドでCamera.Faceクラスが渡ってくるので必要な処理を記述)
  2. CameraクラスにFaceDetectionListenerを登録
  3. Cameraクラスに検出開始を指示
  4. 顔が検出されるとFaceDetectionListenerのonFaceDetectionメソッドが呼ばれる

カメラの基本的な使い方は「カメラの使用方法(1)」を参照してください。

※静止画の顔検出については「FaceDetectorを使って顔認識を行う」が参考になります。

それでは続きで説明していきます。

FaceDetectionListenerインタフェース

Cameraクラスに顔検出が行われた時に通知してもらうためのインタフェースがFaceDetecitonListenerです。

FaceDetectionListenerインタフェースのメソッド

メソッド内容
abstract void onFaceDetection(Face[] faces, Camera camera)顔が検出された時に呼び出される。FaceクラスとCameraクラスのインスンスが渡ってくるので必要な処理を記述する。

Cameraクラスにリスナーを登録することで、顔が検出された時にonFaceDetectionメソッドが呼ばれます。顔検出の結果として検出された顔の数分のCamera.Faceクラスのインスタンスが配列で渡ってくるので必要な処理を記述しましょう。

Camera.Faceクラス

Camera.Faceクラスは検出された顔の情報が入っているクラスです。FaceDetectorクラスを用いて静止画の顔検出を行うときと比べて取得することが出来る情報が増えています。

ただし、顔の信頼度(score)と顔の範囲(rect)以外のフィールドは、実行する端末によって対応していない場合があります。端末が対応していない場合は、ユニークIDのフィールドには-1が、目と口の位置のフィールドにはnullが入って返ってきます。実際にアプリケーションに組み込む際には注意した方が良いでしょう。

フィールドの一覧は下記を参照してください。

Camera.Faceクラスのフィールド

フィールド内容
public int id複数の顔が検出された時にそれらを識別するためのユニークなID。端末が対応していない場合は-1を返す
public int score顔検出の信頼度。1〜100の値で表され、100に近いほど、顔であるという信頼度が高い
public Rect rect検出された顔の範囲
public Point leftEye左目の座標。端末が対応していない場合はnullを返す
public Point rightEye右目の座標。端末が対応していない場合はnullを返す
public Point mouth口の座標。端末が対応していない場合はnullを返す

サンプルソース

それでは実際に顔検出を行うサンプルを作ってみましょう。カメラの基本的な使い方は冒頭に示したように「カメラの使用方法(1)」を参考にしてください。

■src/MainActivity.java

public class MainActivity extends Activity {
    private static final String TAG = "CameraFaceDetect";

    private Camera mCamera;
   // SurfaceViewの生成時、破棄時、変更時に処理を行うためのリスナー
    private SurfaceHolder.Callback mSurfaceListener = new SurfaceHolder.Callback() {
        〜省略〜
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            mCamera.startPreview();

            // 顔検出用のリスナーを登録する
            mCamera.setFaceDetectionListener(new FaceDetectionListener() {

                // 顔が検出された時の処理を記述する
                @Override
                public void onFaceDetection(Face[] faces, Camera camera) {
                    Log.d(TAG, "faces count: " + faces.length);
                    for (Face face : faces) {
                        // サポートされていなければ-1が常に返ってくる
                        Log.d(TAG, "face id: " + face.id);

                        // 顔検出の信頼度 1から100までの値が入っており、100が顔として信頼度が一番高い
                        Log.d(TAG, "face score: " + face.score);

                        // 検出された顔の範囲
                        Log.d(TAG, "face rect: " + face.rect.left + "," + face.rect.top + " - "
                                + face.rect.right + "," + face.rect.bottom);

                        // 以下はサポートされていなければnullが入ってくる
                        if (face.mouth != null) {
                            Log.d(TAG, "face mouth: " + face.mouth.x + "," + face.mouth.y);
                            Log.d(TAG, "face leftEye: " + face.leftEye.x + "," + face.leftEye.y);
                            Log.d(TAG, "face rightEye: " + face.rightEye.x + "," + face.rightEye.y);
                        }
                    }
                }
            });

            // カメラに顔検出開始を指示する
            try {
                mCamera.startFaceDetection();
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "IllegalArgumentException.");
            } catch (RuntimeException e) {
                Log.e(TAG, "the method fails or the face detection is already running.");
            }
        }
    };

今回のサンプルソースは「カメラの使用方法(1)」のサンプルをベースに顔検出の処理を追加しています。

ActivityにSurfaceViewを追加して、そこにカメラのプレビューを表示しています。SurfaceHolder.CallbackのsurfaceCreatedメソッドでカメラの起動を行うので、今回新たに顔検出の処理を追加します。その他は「カメラの使用方法(1)」と一緒のため省略します。 ここで改めて顔検出の手順を確認しましょう。

  1. FaceDetectionListenerを実装(onFaceDetectionメソッドでCamera.Faceクラスが渡ってくるので必要な処理を記述)
  2. CameraクラスにFaceDetectionListenerを登録
  3. Cameraクラスに検出開始を指示
  4. 顔が検出されるとFaceDetectionListenerのonFaceDetectionメソッドが呼ばれる

11行目から46行目までが今回、顔検出用に追加した箇所になります。

12行目から37行目で上記の1であるFaceDetetionListenerの実装(匿名クラスで実装)と、2であるリスナーの登録を行っています。

onFaceDetectionメソッドでは17行目で検出された顔の数(Faceクラスの配列のサイズ)をログに出力し、18行目からのfor文で検出された顔の数だけ処理を行います。検出された顔の情報(Faceクラスのフィールド)を20、23、26、27、31、32、33行目でログに出力しています。

ポイントは30行目で、口の位置情報(Face.mouth)がnullの場合は端末が口の位置の検出に対応していないということです。nullの場合にフィールドにアクセスを行うとNullPointerExceptionが発生してしまうため、事前にNULLチェックを行っています。口と目の位置検出はセットになっており、どちらかのみ対応しているということはないため、今回は口の位置情報のみNULLチェックを行っています。

また、端末が顔検出自体に対応していない or すでに顔検出を実行している時にはstartFaceDetectionメソッドがExceptionを投げるのでtry~catchしています。

以上でカメラプレビューで顔検出を行う方法の説明を終わります。アイデア次第でいろいろ活用できるのではないでしょうか?