FaceDetectorといっても、android.media.FaceDetectorのほうです。Google開発者サービスがない機種でも使えます。
使い方
FaceDetector | Android Developer
Bitmapの幅、高さ、maxFacesごとに、FaceDetectorのインスタンスを作ります。
maxFaces長さの結果用配列を用意しておきます。
Bitmapは、RGB_565限定です。
findFacesメソッドの戻り値は、検出した顔の個数です。結果用配列に顔検出データが入ってきます。
結果用配列の長さが3で、検出した顔の個数が1のとき、結果用配列[1]、結果用配列[2]はnullのままです。配列として使うにしろ、Listにして使うにしろ、長さを切り詰めたほうが使いやすいでしょう。
public List<FaceDetector.Face> findFaces(final Bitmap bmp, final int maxFaces) {
final int w = bmp.getWidth();
final int h = bmp.getHeight();
FaceDetector faceDetector = new FaceDetector(w, h, maxFaces);
final FaceDetector.Face[] faces = new FaceDetector.Face[maxFaces];
final int nFaces = faceDetector.findFaces(bmp, faces);
faceDetector = null;
final List<FaceDetector.Face> list = Arrays.asList(faces).subList(0, nFaces);
return list;
}
Code language: Java (java)
戻り値
戻り値のFaceDetector.Faceからは、左右の目の中間点、両目の距離がわかります。残念ながら、顔の向きはわかりません。
face.confidence() は、検出結果の精度で、0.0〜1.0です。0.4以上なら、良好な検出結果とあります。アプリの対象画像によって、調整してください。
face.eyesDistance()は、両目の距離です。ピクセル単位です。
face.getMidPoint()は、左右の目の中間点です、ピクセル単位です。
face.pose()は、EULER_X、EULER_Y、EULER_Z、どれを与えても、常に0が返ってきます。
Android Facedetector pose values are always 0
https://stackoverflow.com/questions/9960837/android-facedetector-pose-values-are-always-0
public static void debug(final FaceDetector.Face face) {
PointF midPoint = new PointF();
face.getMidPoint(midPoint);
Log.d(TAG, "----------");
Log.d(TAG, "confidence " + face.confidence());
Log.d(TAG, "getMidPoint " + midPoint);
Log.d(TAG, "eyesDistance " + face.eyesDistance());
Log.d(TAG, "pose.X " + face.pose(FaceDetector.Face.EULER_X));
Log.d(TAG, "pose.Y " + face.pose(FaceDetector.Face.EULER_Y));
Log.d(TAG, "pose.Z " + face.pose(FaceDetector.Face.EULER_Z));
}
Code language: Java (java)
マルチスレッド
Bitmapのサイズにもよりますが、処理に1〜2秒かかります。例えば5個のBitmapを処理すると約10秒かかってしまうで、可能ならマルチスレッドで処理したいところです。
1つのインスタンスを作り、2スレッドからfindFaces()を呼ぶと、どちらのスレッドの戻り値も0でした。マルチスレッド対応ではないようです。
2つのインスタンスを作り、2スレッドからfindFaces()を呼ぶと、正しい戻り値が返ってきました。
メモリーリーク?
次のようなことがありました。FaceDetectorの問題ではなく、アプリ側の構成や実装の問題だと思います。
あるアプリが、このFaceDetectorを使っていました。ストレステストとして、起動〜顔検出〜閉じるを100回繰り返したところ、Out Of Memoryが発生したり、無言でテスト途中終了しました。
原因がわからないので、いろいろな処理をコメントにしてみました。すると、FaceDetectorの処理をコメントにすると、100回成功しました。
FaceDetectorの使い方は、あるメソッドのローカル変数として宣言していて、外部には渡していません。問題ないように見えます。
ストレステストをしながら、android studioのProfilerでandroid.media.FaceDetectorのインスタンスを表示すると、3〜10個まで増えることがありますが、それ以上は増えません。ガベージコレクションで解放されていて、問題ないように見えます。
次にターミナルで、adb shell dumpsys meminfo com.example.myapp を表示すると、次のことがわかりました。
ストレステストの初回は、TOTALが、200MB前後、10回後には、約300MB、20回後には、約400MB、と、TOTALが増えていきました。
70回後には、約900MBまで増えて、このあたりでOut Of Memoryか、無言でテスト途中終了しました。
手動で100回繰り返してみました。70回すぎにTOTALが900MBぐらいまで増えました。そして、アプリを起動したとき、adb shell dumpsys meminfo com.example.myappの表示に、
com.example.myappが2個表示されました。その直後に、TOTALが200MBに戻っていました。自動でアプリ再起動したように見えました。
原因がわからないので、(幅、高さ、maxFaces)をキーにして、FaceDetectorのインスタンスプールを用意して、使いまわすことにしました。
なお、FaceDetectorを使った最小アプリで、再現を試しました。最初のTOTALは45MB、100回後のTOTALは48MB、ほとんど増えませんでした。なので、FaceDetectorの問題ではないと思います。