/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.util.Log;
import java.lang.IllegalArgumentException;
/**
* Identifies the faces of people in a
* {@link android.graphics.Bitmap} graphic object.
*/
public class FaceDetector {
/**
* A Face contains all the information identifying the location
* of a face in a bitmap.
*/
public class Face {
/** The minimum confidence factor of good face recognition */
public static final float CONFIDENCE_THRESHOLD = 0.4f;
/** The x-axis Euler angle of a face. */
public static final int EULER_X = 0;
/** The y-axis Euler angle of a face. */
public static final int EULER_Y = 1;
/** The z-axis Euler angle of a face. */
public static final int EULER_Z = 2;
/**
* Returns a confidence factor between 0 and 1. This indicates how
* certain what has been found is actually a face. A confidence
* factor above 0.3 is usually good enough.
*/
public float confidence() {
return mConfidence;
}
/**
* Sets the position of the mid-point between the eyes.
* @param point the PointF coordinates (float values) of the
* face's mid-point
*/
public void getMidPoint(PointF point) {
// don't return a PointF to avoid allocations
point.set(mMidPointX, mMidPointY);
}
/**
* Returns the distance between the eyes.
*/
public float eyesDistance() {
return mEyesDist;
}
/**
* Returns the face's pose. That is, the rotations around either
* the X, Y or Z axis (the positions in 3-dimensional Euclidean space).
*
* @param euler the Euler axis to retrieve an angle from
* (<var>EULER_X</var>, <var>EULER_Y</var> or
* <var>EULER_Z</var>)
* @return the Euler angle of the of the face, for the given axis
*/
public float pose(int euler) {
// don't use an array to avoid allocations
if (euler == EULER_X)
return mPoseEulerX;
else if (euler == EULER_Y)
return mPoseEulerY;
else if (euler == EULER_Z)
return mPoseEulerZ;
throw new IllegalArgumentException();
}
// private ctor, user not supposed to build this object
private Face() {
}
private float mConfidence;
private float mMidPointX;
private float mMidPointY;
private float mEyesDist;
private float mPoseEulerX;
private float mPoseEulerY;
private float mPoseEulerZ;
}
/**
* Creates a FaceDetector, configured with the size of the images to
* be analysed and the maximum number of faces that can be detected.
* These parameters cannot be changed once the object is constructed.
* Note that the width of the image must be even.
*
* @param width the width of the image
* @param height the height of the image
* @param maxFaces the maximum number of faces to identify
*
*/
public FaceDetector(int width, int height, int maxFaces)
{
if (!sInitialized) {
return;
}
fft_initialize(width, height, maxFaces);
mWidth = width;
mHeight = height;
mMaxFaces = maxFaces;
mBWBuffer = new byte[width * height];
}
/**
* Finds all the faces found in a given {@link android.graphics.Bitmap}.
* The supplied array is populated with {@link FaceDetector.Face}s for each
* face found. The bitmap must be in 565 format (for now).
*
* @param bitmap the {@link android.graphics.Bitmap} graphic to be analyzed
* @param faces an array in which to place all found
* {@link FaceDetector.Face}s. The array must be sized equal
* to the <var>maxFaces</var> value set at initialization
* @return the number of faces found
* @throws IllegalArgumentException if the Bitmap dimensions don't match
* the dimensions defined at initialization or the given array
* is not sized equal to the <var>maxFaces</var> value defined
* at initialization
*/
public int findFaces(Bitmap bitmap, Face[] faces)
{
if (!sInitialized) {
return 0;
}
if (bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
throw new IllegalArgumentException(
"bitmap size doesn't match initialization");
}
if (faces.length < mMaxFaces) {
throw new IllegalArgumentException(
"faces[] smaller than maxFaces");
}
int numFaces = fft_detect(bitmap);
if (numFaces >= mMaxFaces)
numFaces = mMaxFaces;
for (int i=0 ; i<numFaces ; i++) {
if (faces[i] == null)
faces[i] = new Face();
fft_get_face(faces[i], i);
}
return numFaces;
}
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
fft_destroy();
}
/*
* We use a class initializer to allow the native code to cache some
* field offsets.
*/
private static boolean sInitialized;
native private static void nativeClassInit();
static {
sInitialized = false;
try {
System.loadLibrary("FFTEm");
nativeClassInit();
sInitialized = true;
} catch (UnsatisfiedLinkError e) {
Log.d("FFTEm", "face detection library not found!");
}
}
native private int fft_initialize(int width, int height, int maxFaces);
native private int fft_detect(Bitmap bitmap);
native private void fft_get_face(Face face, int i);
native private void fft_destroy();
private long mFD;
private long mSDK;
private long mDCR;
private int mWidth;
private int mHeight;
private int mMaxFaces;
private byte mBWBuffer[];
}