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