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 
17 package android.media;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.PointF;
21 import android.util.Log;
22 
23 import java.lang.IllegalArgumentException;
24 
25 /**
26  * Identifies the faces of people in a
27  * {@link android.graphics.Bitmap} graphic object.
28  */
29 public 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          */
confidence()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          */
getMidPoint(PointF point)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          */
eyesDistance()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          */
pose(int euler)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
Face()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      */
FaceDetector(int width, int height, int maxFaces)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      */
findFaces(Bitmap bitmap, Face[] faces)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
finalize()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;
nativeClassInit()176     native private static void nativeClassInit();
177 
178     static {
179         sInitialized = false;
180         try {
181             System.loadLibrary("FFTEm");
nativeClassInit()182             nativeClassInit();
183             sInitialized = true;
184         } catch (UnsatisfiedLinkError e) {
185             Log.d("FFTEm", "face detection library not found!");
186         }
187     }
188 
fft_initialize(int width, int height, int maxFaces)189     native private int  fft_initialize(int width, int height, int maxFaces);
fft_detect(Bitmap bitmap)190     native private int  fft_detect(Bitmap bitmap);
fft_get_face(Face face, int i)191     native private void fft_get_face(Face face, int i);
fft_destroy()192     native private void fft_destroy();
193 
194     private long    mFD;
195     private long    mSDK;
196     private long    mDCR;
197     private int     mWidth;
198     private int     mHeight;
199     private int     mMaxFaces;
200     private byte    mBWBuffer[];
201 }
202 
203