Home | History | Annotate | Download | only in neven
      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 #include <stdlib.h>
     18 #include <stdio.h>
     19 #include <fcntl.h>
     20 #include <unistd.h>
     21 
     22 #include <utils/misc.h>
     23 #include <utils/String8.h>
     24 #include <utils/Log.h>
     25 
     26 #include <core/SkBitmap.h>
     27 
     28 #include "jni.h"
     29 #include "JNIHelp.h"
     30 #include "android_runtime/AndroidRuntime.h"
     31 
     32 using namespace android;
     33 
     34 extern "C"
     35 {
     36     #include <fd_emb_sdk.h>
     37 }
     38 
     39 struct FaceData
     40 {
     41     float confidence;
     42     float midpointx;
     43     float midpointy;
     44     float eyedist;
     45 };
     46 
     47 struct FaceOffsets
     48 {
     49     jfieldID    confidence;
     50     jfieldID    midpointx;
     51     jfieldID    midpointy;
     52     jfieldID    eyedist;
     53     jfieldID    eulerx;
     54     jfieldID    eulery;
     55     jfieldID    eulerz;
     56 } gFaceOffsets;
     57 
     58 struct FaceDetectorOffsets
     59 {
     60     jfieldID    fd;
     61     jfieldID    sdk;
     62     jfieldID    dcr;
     63     jfieldID    width;
     64     jfieldID    height;
     65     jfieldID    maxFaces;
     66     jfieldID    bwbuffer;
     67 } gFaceDetectorOffsets;
     68 
     69 jfieldID nativeBitmapID;
     70 
     71 // ---------------------------------------------------------------------------
     72 
     73 static void getFaceData(btk_HDCR hdcr, FaceData* fdata)
     74 {
     75     btk_Node leftEye, rightEye;
     76 
     77     btk_DCR_getNode(hdcr, 0, &leftEye);
     78     btk_DCR_getNode(hdcr, 1, &rightEye);
     79 
     80     fdata->eyedist = (float)(rightEye.x - leftEye.x) / (1 << 16);
     81     fdata->midpointx = (float)(rightEye.x + leftEye.x) / (1 << 17);
     82     fdata->midpointy = (float)(rightEye.y + leftEye.y) / (1 << 17);
     83     fdata->confidence = (float)btk_DCR_confidence(hdcr) / (1 << 24);
     84 }
     85 
     86 // ---------------------------------------------------------------------------
     87 
     88 static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
     89 {
     90     jclass npeClazz = env->FindClass(exc);
     91     env->ThrowNew(npeClazz, msg);
     92 }
     93 
     94 static void
     95 nativeClassInit
     96 (JNIEnv *_env, jclass _this)
     97 {
     98     gFaceDetectorOffsets.fd             = _env->GetFieldID(_this, "mFD", "I");
     99     gFaceDetectorOffsets.sdk            = _env->GetFieldID(_this, "mSDK", "I");
    100     gFaceDetectorOffsets.dcr            = _env->GetFieldID(_this, "mDCR", "I");
    101     gFaceDetectorOffsets.width          = _env->GetFieldID(_this, "mWidth", "I");
    102     gFaceDetectorOffsets.height         = _env->GetFieldID(_this, "mHeight", "I");
    103     gFaceDetectorOffsets.maxFaces       = _env->GetFieldID(_this, "mMaxFaces", "I");
    104     gFaceDetectorOffsets.bwbuffer       = _env->GetFieldID(_this, "mBWBuffer", "[B");
    105 
    106     jclass faceClass = _env->FindClass("android/media/FaceDetector$Face");
    107     gFaceOffsets.confidence  = _env->GetFieldID(faceClass, "mConfidence", "F");
    108     gFaceOffsets.midpointx   = _env->GetFieldID(faceClass, "mMidPointX", "F");
    109     gFaceOffsets.midpointy   = _env->GetFieldID(faceClass, "mMidPointY", "F");
    110     gFaceOffsets.eyedist     = _env->GetFieldID(faceClass, "mEyesDist", "F");
    111     gFaceOffsets.eulerx      = _env->GetFieldID(faceClass, "mPoseEulerX", "F");
    112     gFaceOffsets.eulery      = _env->GetFieldID(faceClass, "mPoseEulerY", "F");
    113     gFaceOffsets.eulerz      = _env->GetFieldID(faceClass, "mPoseEulerZ", "F");
    114 
    115     jclass bitmapClass = _env->FindClass("android/graphics/Bitmap");
    116     nativeBitmapID = _env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
    117 }
    118 
    119 // ---------------------------------------------------------------------------
    120 
    121 static jint
    122 initialize(JNIEnv *_env, jobject _this,
    123      jint w, jint h, jint maxFaces)
    124 {
    125     // load the configuration file
    126     const char* root = getenv("ANDROID_ROOT");
    127     String8 path(root);
    128     path.appendPath("usr/share/bmd/RFFstd_501.bmd");
    129     // path.appendPath("usr/share/bmd/RFFspeed_501.bmd");
    130 
    131     const int MAX_FILE_SIZE = 65536;
    132     void* initData = malloc( MAX_FILE_SIZE ); /* enough to fit entire file */
    133     int filedesc = open(path.string(), O_RDONLY);
    134     int initDataSize = read(filedesc, initData, MAX_FILE_SIZE);
    135     close(filedesc);
    136 
    137     // --------------------------------------------------------------------
    138     btk_HSDK sdk = NULL;
    139     btk_SDKCreateParam sdkParam = btk_SDK_defaultParam();
    140     sdkParam.fpMalloc = malloc;
    141     sdkParam.fpFree = free;
    142     sdkParam.maxImageWidth = w;
    143     sdkParam.maxImageHeight = h;
    144 
    145     btk_Status status = btk_SDK_create(&sdkParam, &sdk);
    146     // make sure everything went well
    147     if (status != btk_STATUS_OK) {
    148         // XXX: be more precise about what went wrong
    149         doThrow(_env, "java/lang/OutOfMemoryError", NULL);
    150         return 0;
    151     }
    152 
    153     btk_HDCR dcr = NULL;
    154     btk_DCRCreateParam dcrParam = btk_DCR_defaultParam();
    155     btk_DCR_create( sdk, &dcrParam, &dcr );
    156 
    157     btk_HFaceFinder fd = NULL;
    158     btk_FaceFinderCreateParam fdParam = btk_FaceFinder_defaultParam();
    159     fdParam.pModuleParam = initData;
    160     fdParam.moduleParamSize = initDataSize;
    161     fdParam.maxDetectableFaces = maxFaces;
    162     status = btk_FaceFinder_create( sdk, &fdParam, &fd );
    163     btk_FaceFinder_setRange(fd, 20, w/2); /* set eye distance range */
    164 
    165     // make sure everything went well
    166     if (status != btk_STATUS_OK) {
    167         // XXX: be more precise about what went wrong
    168         doThrow(_env, "java/lang/OutOfMemoryError", NULL);
    169         return 0;
    170     }
    171 
    172     // free the configuration file
    173     free(initData);
    174 
    175     // initialize the java object
    176     _env->SetIntField(_this, gFaceDetectorOffsets.fd,  (jint)fd);
    177     _env->SetIntField(_this, gFaceDetectorOffsets.sdk, (jint)sdk);
    178     _env->SetIntField(_this, gFaceDetectorOffsets.dcr, (jint)dcr);
    179 
    180     return 1;
    181 }
    182 
    183 static void
    184 destroy(JNIEnv *_env, jobject _this)
    185 {
    186     btk_HFaceFinder hfd =
    187         (btk_HFaceFinder)(_env->GetIntField(_this, gFaceDetectorOffsets.fd));
    188     btk_FaceFinder_close( hfd );
    189 
    190     btk_HDCR hdcr = (btk_HDCR)(_env->GetIntField(_this, gFaceDetectorOffsets.dcr));
    191     btk_DCR_close( hdcr );
    192 
    193     btk_HSDK hsdk = (btk_HSDK)(_env->GetIntField(_this, gFaceDetectorOffsets.sdk));
    194     btk_SDK_close( hsdk );
    195 }
    196 
    197 static jint
    198 detect(JNIEnv *_env, jobject _this,
    199      jobject bitmap)
    200 {
    201     // get the fields we need
    202     btk_HDCR hdcr = (btk_HDCR)(_env->GetIntField(_this, gFaceDetectorOffsets.dcr));
    203     btk_HFaceFinder hfd =
    204         (btk_HFaceFinder)(_env->GetIntField(_this, gFaceDetectorOffsets.fd));
    205     u32 maxFaces = _env->GetIntField(_this, gFaceDetectorOffsets.maxFaces);
    206     u32 width = _env->GetIntField(_this, gFaceDetectorOffsets.width);
    207     u32 height = _env->GetIntField(_this, gFaceDetectorOffsets.height);
    208 
    209     jbyteArray bwbufferObject = (jbyteArray)
    210             _env->GetObjectField(_this, gFaceDetectorOffsets.bwbuffer);
    211 
    212     // get to the native bitmap
    213     SkBitmap const * nativeBitmap =
    214             (SkBitmap const *)_env->GetIntField(bitmap, nativeBitmapID);
    215 
    216     // get to our BW temporary buffer
    217     jbyte* bwbuffer = _env->GetByteArrayElements(bwbufferObject, 0);
    218 
    219     // convert the image to B/W
    220     uint8_t* dst = (uint8_t*)bwbuffer;
    221 
    222     // manage the life-time of locking our pixels
    223     SkAutoLockPixels alp(*nativeBitmap);
    224 
    225     uint16_t const* src = (uint16_t const*)nativeBitmap->getPixels();
    226     int wpr = nativeBitmap->rowBytes() / 2;
    227     for (u32 y=0 ; y<height; y++) {
    228         for (u32 x=0 ; x<width ; x++) {
    229             uint16_t rgb = src[x];
    230             int r  = rgb >> 11;
    231             int g2 = (rgb >> 5) & 0x3F;
    232             int b  = rgb & 0x1F;
    233             // L coefficients 0.299 0.587 0.11
    234             int L = (r<<1) + (g2<<1) + (g2>>1) + b;
    235             *dst++ = L;
    236         }
    237         src += wpr;
    238     }
    239 
    240     // run detection
    241     btk_DCR_assignGrayByteImage(hdcr, bwbuffer, width, height);
    242 
    243     int numberOfFaces = 0;
    244     if (btk_FaceFinder_putDCR(hfd, hdcr) == btk_STATUS_OK) {
    245         numberOfFaces = btk_FaceFinder_faces(hfd);
    246     } else {
    247         ALOGE("ERROR: Return 0 faces because error exists in btk_FaceFinder_putDCR.\n");
    248     }
    249 
    250     // release the arrays we're using
    251     _env->ReleaseByteArrayElements(bwbufferObject, bwbuffer, 0);
    252     return numberOfFaces;
    253 }
    254 
    255 static void
    256 get_face(JNIEnv *_env, jobject _this,
    257      jobject face, jint index)
    258 {
    259     btk_HDCR hdcr = (btk_HDCR)(_env->GetIntField(_this, gFaceDetectorOffsets.dcr));
    260     btk_HFaceFinder hfd =
    261         (btk_HFaceFinder)(_env->GetIntField(_this, gFaceDetectorOffsets.fd));
    262 
    263     FaceData faceData;
    264     btk_FaceFinder_getDCR(hfd, hdcr);
    265     getFaceData(hdcr, &faceData);
    266 
    267     const float X2F = 1.0f / 65536.0f;
    268     _env->SetFloatField(face, gFaceOffsets.confidence,  faceData.confidence);
    269     _env->SetFloatField(face, gFaceOffsets.midpointx,   faceData.midpointx);
    270     _env->SetFloatField(face, gFaceOffsets.midpointy,   faceData.midpointy);
    271     _env->SetFloatField(face, gFaceOffsets.eyedist,     faceData.eyedist);
    272     _env->SetFloatField(face, gFaceOffsets.eulerx,      0);
    273     _env->SetFloatField(face, gFaceOffsets.eulery,      0);
    274     _env->SetFloatField(face, gFaceOffsets.eulerz,      0);
    275 }
    276 
    277 // ---------------------------------------------------------------------------
    278 
    279 static const char *classPathName = "android/media/FaceDetector";
    280 
    281 static JNINativeMethod methods[] = {
    282 {"nativeClassInit", "()V",                                  (void*)nativeClassInit },
    283 {"fft_initialize",  "(III)I",                               (void*)initialize },
    284 {"fft_detect",      "(Landroid/graphics/Bitmap;)I",         (void*)detect },
    285 {"fft_get_face",    "(Landroid/media/FaceDetector$Face;I)V",(void*)get_face },
    286 {"fft_destroy",     "()V",                                  (void*)destroy },
    287 };
    288 
    289 int register_android_media_FaceDetector(JNIEnv *_env)
    290 {
    291     return android::AndroidRuntime::registerNativeMethods(
    292             _env, classPathName, methods, NELEM(methods));
    293 }
    294 
    295 // ---------------------------------------------------------------------------
    296 
    297 jint JNI_OnLoad(JavaVM* vm, void* reserved)
    298 {
    299     JNIEnv* env = NULL;
    300     jint result = -1;
    301 
    302     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    303         ALOGE("ERROR: GetEnv failed\n");
    304         goto bail;
    305     }
    306     assert(env != NULL);
    307 
    308     if (register_android_media_FaceDetector(env) < 0) {
    309         ALOGE("ERROR: MediaPlayer native registration failed\n");
    310         goto bail;
    311     }
    312 
    313     /* success -- return valid version number */
    314     result = JNI_VERSION_1_4;
    315 
    316 bail:
    317     return result;
    318 }
    319