1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.media; 18 19import android.graphics.Bitmap; 20import android.graphics.PointF; 21import android.util.Log; 22 23import java.lang.IllegalArgumentException; 24 25/** 26 * Identifies the faces of people in a 27 * {@link android.graphics.Bitmap} graphic object. 28 */ 29public class FaceDetector { 30 31 /** 32 * A Face contains all the information identifying the location 33 * of a face in a bitmap. 34 */ 35 public class Face { 36 /** The minimum confidence factor of good face recognition */ 37 public static final float CONFIDENCE_THRESHOLD = 0.4f; 38 /** The x-axis Euler angle of a face. */ 39 public static final int EULER_X = 0; 40 /** The y-axis Euler angle of a face. */ 41 public static final int EULER_Y = 1; 42 /** The z-axis Euler angle of a face. */ 43 public static final int EULER_Z = 2; 44 45 /** 46 * Returns a confidence factor between 0 and 1. This indicates how 47 * certain what has been found is actually a face. A confidence 48 * factor above 0.3 is usually good enough. 49 */ 50 public float confidence() { 51 return mConfidence; 52 } 53 /** 54 * Sets the position of the mid-point between the eyes. 55 * @param point the PointF coordinates (float values) of the 56 * face's mid-point 57 */ 58 public void getMidPoint(PointF point) { 59 // don't return a PointF to avoid allocations 60 point.set(mMidPointX, mMidPointY); 61 } 62 /** 63 * Returns the distance between the eyes. 64 */ 65 public float eyesDistance() { 66 return mEyesDist; 67 } 68 /** 69 * Returns the face's pose. That is, the rotations around either 70 * the X, Y or Z axis (the positions in 3-dimensional Euclidean space). 71 * 72 * @param euler the Euler axis to retrieve an angle from 73 * (<var>EULER_X</var>, <var>EULER_Y</var> or 74 * <var>EULER_Z</var>) 75 * @return the Euler angle of the of the face, for the given axis 76 */ 77 public float pose(int euler) { 78 // don't use an array to avoid allocations 79 if (euler == EULER_X) 80 return mPoseEulerX; 81 else if (euler == EULER_Y) 82 return mPoseEulerY; 83 else if (euler == EULER_Z) 84 return mPoseEulerZ; 85 throw new IllegalArgumentException(); 86 } 87 88 // private ctor, user not supposed to build this object 89 private Face() { 90 } 91 private float mConfidence; 92 private float mMidPointX; 93 private float mMidPointY; 94 private float mEyesDist; 95 private float mPoseEulerX; 96 private float mPoseEulerY; 97 private float mPoseEulerZ; 98 } 99 100 101 /** 102 * Creates a FaceDetector, configured with the size of the images to 103 * be analysed and the maximum number of faces that can be detected. 104 * These parameters cannot be changed once the object is constructed. 105 * Note that the width of the image must be even. 106 * 107 * @param width the width of the image 108 * @param height the height of the image 109 * @param maxFaces the maximum number of faces to identify 110 * 111 */ 112 public FaceDetector(int width, int height, int maxFaces) 113 { 114 if (!sInitialized) { 115 return; 116 } 117 fft_initialize(width, height, maxFaces); 118 mWidth = width; 119 mHeight = height; 120 mMaxFaces = maxFaces; 121 mBWBuffer = new byte[width * height]; 122 } 123 124 /** 125 * Finds all the faces found in a given {@link android.graphics.Bitmap}. 126 * The supplied array is populated with {@link FaceDetector.Face}s for each 127 * face found. The bitmap must be in 565 format (for now). 128 * 129 * @param bitmap the {@link android.graphics.Bitmap} graphic to be analyzed 130 * @param faces an array in which to place all found 131 * {@link FaceDetector.Face}s. The array must be sized equal 132 * to the <var>maxFaces</var> value set at initialization 133 * @return the number of faces found 134 * @throws IllegalArgumentException if the Bitmap dimensions don't match 135 * the dimensions defined at initialization or the given array 136 * is not sized equal to the <var>maxFaces</var> value defined 137 * at initialization 138 */ 139 public int findFaces(Bitmap bitmap, Face[] faces) 140 { 141 if (!sInitialized) { 142 return 0; 143 } 144 if (bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) { 145 throw new IllegalArgumentException( 146 "bitmap size doesn't match initialization"); 147 } 148 if (faces.length < mMaxFaces) { 149 throw new IllegalArgumentException( 150 "faces[] smaller than maxFaces"); 151 } 152 153 int numFaces = fft_detect(bitmap); 154 if (numFaces >= mMaxFaces) 155 numFaces = mMaxFaces; 156 for (int i=0 ; i<numFaces ; i++) { 157 if (faces[i] == null) 158 faces[i] = new Face(); 159 fft_get_face(faces[i], i); 160 } 161 return numFaces; 162 } 163 164 165 /* no user serviceable parts here ... */ 166 @Override 167 protected void finalize() throws Throwable { 168 fft_destroy(); 169 } 170 171 /* 172 * We use a class initializer to allow the native code to cache some 173 * field offsets. 174 */ 175 private static boolean sInitialized; 176 native private static void nativeClassInit(); 177 178 static { 179 sInitialized = false; 180 try { 181 System.loadLibrary("FFTEm"); 182 nativeClassInit(); 183 sInitialized = true; 184 } catch (UnsatisfiedLinkError e) { 185 Log.d("FFTEm", "face detection library not found!"); 186 } 187 } 188 189 native private int fft_initialize(int width, int height, int maxFaces); 190 native private int fft_detect(Bitmap bitmap); 191 native private void fft_get_face(Face face, int i); 192 native private void fft_destroy(); 193 194 private int mFD; 195 private int mSDK; 196 private int mDCR; 197 private int mWidth; 198 private int mHeight; 199 private int mMaxFaces; 200 private byte mBWBuffer[]; 201} 202 203