19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.media;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.PointF;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.IllegalArgumentException;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Identifies the faces of people in a
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@link android.graphics.Bitmap} graphic object.
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class FaceDetector {
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * A Face contains all the information identifying the location
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * of a face in a bitmap.
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public class Face {
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The minimum confidence factor of good face recognition */
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final float CONFIDENCE_THRESHOLD = 0.4f;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The x-axis Euler angle of a face. */
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final int EULER_X = 0;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The y-axis Euler angle of a face. */
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final int EULER_Y = 1;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /** The z-axis Euler angle of a face. */
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final int EULER_Z = 2;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns a confidence factor between 0 and 1. This indicates how
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * certain what has been found is actually a face. A confidence
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * factor above 0.3 is usually good enough.
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public float confidence() {
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mConfidence;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Sets the position of the mid-point between the eyes.
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param point the PointF coordinates (float values) of the
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              face's mid-point
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void getMidPoint(PointF point) {
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // don't return a PointF to avoid allocations
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            point.set(mMidPointX, mMidPointY);
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the distance between the eyes.
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public float eyesDistance() {
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mEyesDist;
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Returns the face's pose. That is, the rotations around either
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * the X, Y or Z axis (the positions in 3-dimensional Euclidean space).
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param euler the Euler axis to retrieve an angle from
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              (<var>EULER_X</var>, <var>EULER_Y</var> or
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *              <var>EULER_Z</var>)
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return the Euler angle of the of the face, for the given axis
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public float pose(int euler) {
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // don't use an array to avoid allocations
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (euler == EULER_X)
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mPoseEulerX;
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else if (euler == EULER_Y)
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mPoseEulerY;
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            else if (euler == EULER_Z)
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return mPoseEulerZ;
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project           throw new IllegalArgumentException();
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // private ctor, user not supposed to build this object
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Face() {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private float   mConfidence;
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private float   mMidPointX;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private float   mMidPointY;
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private float   mEyesDist;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private float   mPoseEulerX;
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private float   mPoseEulerY;
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private float   mPoseEulerZ;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a FaceDetector, configured with the size of the images to
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * be analysed and the maximum number of faces that can be detected.
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * These parameters cannot be changed once the object is constructed.
105d6ccc9bcf0bdbca315d0c57c69f26df975eb5264Gloria Wang     * Note that the width of the image must be even.
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param width  the width of the image
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param height the height of the image
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param maxFaces the maximum number of faces to identify
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public FaceDetector(int width, int height, int maxFaces)
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!sInitialized) {
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fft_initialize(width, height, maxFaces);
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mWidth = width;
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mHeight = height;
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMaxFaces = maxFaces;
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBWBuffer = new byte[width * height];
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Finds all the faces found in a given {@link android.graphics.Bitmap}.
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The supplied array is populated with {@link FaceDetector.Face}s for each
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * face found. The bitmap must be in 565 format (for now).
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param bitmap the {@link android.graphics.Bitmap} graphic to be analyzed
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param faces  an array in which to place all found
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *               {@link FaceDetector.Face}s. The array must be sized equal
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *               to the <var>maxFaces</var> value set at initialization
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the number of faces found
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throws IllegalArgumentException if the Bitmap dimensions don't match
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *               the dimensions defined at initialization or the given array
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *               is not sized equal to the <var>maxFaces</var> value defined
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *               at initialization
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int findFaces(Bitmap bitmap, Face[] faces)
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!sInitialized) {
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return 0;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException(
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    "bitmap size doesn't match initialization");
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (faces.length < mMaxFaces) {
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalArgumentException(
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    "faces[] smaller than maxFaces");
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int numFaces = fft_detect(bitmap);
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (numFaces >= mMaxFaces)
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            numFaces = mMaxFaces;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i=0 ; i<numFaces ; i++) {
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (faces[i] == null)
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                faces[i] = new Face();
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            fft_get_face(faces[i], i);
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return numFaces;
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* no user serviceable parts here ... */
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void finalize() throws Throwable {
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fft_destroy();
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /*
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * We use a class initializer to allow the native code to cache some
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * field offsets.
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean sInitialized;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    native private static void nativeClassInit();
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static {
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sInitialized = false;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.loadLibrary("FFTEm");
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            nativeClassInit();
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sInitialized = true;
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (UnsatisfiedLinkError e) {
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.d("FFTEm", "face detection library not found!");
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    native private int  fft_initialize(int width, int height, int maxFaces);
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    native private int  fft_detect(Bitmap bitmap);
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    native private void fft_get_face(Face face, int i);
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    native private void fft_destroy();
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
194075e9a19ce645752f8282bc19c91b25978a7dc52Ashok Bhat    private long    mFD;
195075e9a19ce645752f8282bc19c91b25978a7dc52Ashok Bhat    private long    mSDK;
196075e9a19ce645752f8282bc19c91b25978a7dc52Ashok Bhat    private long    mDCR;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int     mWidth;
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int     mHeight;
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int     mMaxFaces;
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private byte    mBWBuffer[];
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
203