【今更だけどandroid.hardware.Cameraを使う】顔検出を行う

このエントリーを Google ブックマーク に追加
Pocket
[`yahoo` not found]

android.hardware.Cameraを使うことで、Android端末にあるカメラデバイスにアクセスして使うことができます。
android.hardware.Cameraは、現在では非推奨となっていますがOpenCV等ではまだまだ使われています。

Android端末に実装されているカメラデバイスは固有の性能があります。
そのため、ある端末では出来るのに別の端末では出来ないということが多々あります。
今回は顔検出する方法を説明します。
下記の実装はすべて前回までに作ったクラスを修正しています。

顔検出の使い方

カメラデバイスの中には顔検出をサポートしているものがあります。
しかし、カメラデバイスによってどこまで実装されていて、どのような検出をするかが変化するので注意が必要です。
  1. 顔検出の実装方式(ソフトウェア方式かハードウェア方式)
  2. 顔の最大検出数
  3. 顔の領域
  4. 右目・左目の位置座標
  5. 口の中心位置座標
  6. 顔のトラッキング

顔検出を始める

final void startFaceDetection()は顔検出を開始するためのメソッドです。顔検出を既に始めている状態でさらにCamera#startFaceDetectionを使うと例外が発生します。
一方、final void stopFaceDetection()は顔検出を終了するためのメソッドです。
また、final void setFaceDetectionListener(Camera.FaceDetectionListener listener)は顔検出時のコールバックインターフェースを設定するメソッドです。
Camera.FaceDetectionListener#onFaceDetection(Face[] faces, Camera camera)はカメラプレビューフレーム内に顔が存在する時にコールバックされるメソッドです。
第一引数のFace[] facesは検出した顔の配列です、Camera.Faceは顔を表すクラスです。

    public void startFaceDetection() {
        stopFaceDetection();
        mCamera.setFaceDetectionListener(new Camera.FaceDetectionListener() {
            @Override
            public void onFaceDetection(Camera.Face[] faces, Camera camera) {
                for (int i = 0; i < faces.length; i++) {
                    Camera.Face face = faces[i];
                    Log.d(TAG, "onFaceDetection:" + i);
                    Log.d(TAG, "id: " + face.id);
                    Log.d(TAG, "score: " + face.score);
                    Log.d(TAG, "leftEye: " + face.leftEye);
                    Log.d(TAG, "rightEye: " + face.rightEye);
                    Log.d(TAG, "mouth: " + face.mouth);
                    Log.d(TAG, "rect: " + face.rect);
                    Log.d(TAG, "PixelRect: " + faceRectToPixelRect(face.rect));
                }
            }
        });
        mCamera.startFaceDetection();
    }

    public void stopFaceDetection() {
        mCamera.stopFaceDetection();
        mCamera.setFaceDetectionListener(null);
    }

Camera.Face

Camera.Faceは顔を表すクラスです。
Camera.Faceには下記のフィールドが存在します。
public int id同一の顔を追跡している間に割り当てられるIDを表します。一度画面から消えた場合は新しいIDが割り当てられます。このフィールドはオプションであり、顔を追跡する機能のサポートされていない場合は-1が格納されます。
public int score顔検出の信頼度を1〜100の数値で表します。100が最高の信頼度となり、50以上の値の場合は信頼できます。
public Rect rect顔検出によって識別された顔領域の矩形を表します。カメラデバイスの座標系なのでディスプレイの座標系に変換する必要があります。
public Point leftEye左目の位置を表します。このフィールドはオプションであり、サポートされていない場合はnullが格納されます。
public Point rightEye右目の位置を表します。このフィールドはオプションであり、サポートされていない場合はnullが格納されます。
public Point mouth口の中心位置を表します。このフィールドはオプションであり、サポートされていない場合はnullが格納されます。

    private RectF faceRectToPixelRect(Rect faceRect) {
        Matrix matrix = new Matrix();
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId , cameraInfo);
        boolean mirror = (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT);
        // フロントカメラは左右反転しているため、反転させる。
        matrix.setScale(mirror ? -1 : 1, 1);
        // カメラデバイスの向きを考慮する。Camera#setDisplayOrientationで設定した値と同様の値を設定する。
        matrix.postRotate(getCameraDisplayOrientation());
        // カメラデバイスの座標系[左上(-1000, -1000):右下(1000, 1000)]をディスプレイの座標系[左上(0, 0):右下(width, height)]に変換する。
        int previewWidth = this.getWidth();
        int previewHeight = this.getHeight();
        matrix.postScale(previewWidth / 2000f, previewHeight / 2000f);
        matrix.postTranslate(previewWidth / 2f, previewHeight / 2f);
        RectF rect = new RectF(faceRect);
        //座標を変換する。
        matrix.mapRect(rect);
        return rect;
    }