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 <nativehelper/JNIHelp.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 // --------------------------------------------------------------------------- 70 71 static 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 86 static 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 92 static void 93 nativeClassInit 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 116 static jint 117 initialize(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 178 static void 179 destroy(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 192 static jint 193 detect(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 width = _env->GetIntField(_this, gFaceDetectorOffsets.width); 201 u32 height = _env->GetIntField(_this, gFaceDetectorOffsets.height); 202 203 jbyteArray bwbufferObject = (jbyteArray) 204 _env->GetObjectField(_this, gFaceDetectorOffsets.bwbuffer); 205 206 // get to our BW temporary buffer 207 jbyte* bwbuffer = _env->GetByteArrayElements(bwbufferObject, 0); 208 209 // convert the image to B/W 210 uint8_t* dst = (uint8_t*)bwbuffer; 211 212 uint16_t const* src; 213 AndroidBitmapInfo bitmapInfo; 214 AndroidBitmap_getInfo(_env, bitmap, &bitmapInfo); 215 AndroidBitmap_lockPixels(_env, bitmap, (void**) &src); 216 217 int wpr = bitmapInfo.stride / 2; 218 for (u32 y=0 ; y<height; y++) { 219 for (u32 x=0 ; x<width ; x++) { 220 uint16_t rgb = src[x]; 221 int r = rgb >> 11; 222 int g2 = (rgb >> 5) & 0x3F; 223 int b = rgb & 0x1F; 224 // L coefficients 0.299 0.587 0.11 225 int L = (r<<1) + (g2<<1) + (g2>>1) + b; 226 *dst++ = L; 227 } 228 src += wpr; 229 } 230 231 // run detection 232 btk_DCR_assignGrayByteImage(hdcr, bwbuffer, width, height); 233 234 int numberOfFaces = 0; 235 if (btk_FaceFinder_putDCR(hfd, hdcr) == btk_STATUS_OK) { 236 numberOfFaces = btk_FaceFinder_faces(hfd); 237 } else { 238 ALOGE("ERROR: Return 0 faces because error exists in btk_FaceFinder_putDCR.\n"); 239 } 240 241 // release the arrays we're using 242 AndroidBitmap_unlockPixels(_env, bitmap); 243 _env->ReleaseByteArrayElements(bwbufferObject, bwbuffer, 0); 244 return numberOfFaces; 245 } 246 247 static void 248 get_face(JNIEnv *_env, jobject _this, 249 jobject face, jint) 250 { 251 btk_HDCR hdcr = (btk_HDCR)(_env->GetLongField(_this, gFaceDetectorOffsets.dcr)); 252 btk_HFaceFinder hfd = 253 (btk_HFaceFinder)(_env->GetLongField(_this, gFaceDetectorOffsets.fd)); 254 255 FaceData faceData; 256 btk_FaceFinder_getDCR(hfd, hdcr); 257 getFaceData(hdcr, &faceData); 258 259 _env->SetFloatField(face, gFaceOffsets.confidence, faceData.confidence); 260 _env->SetFloatField(face, gFaceOffsets.midpointx, faceData.midpointx); 261 _env->SetFloatField(face, gFaceOffsets.midpointy, faceData.midpointy); 262 _env->SetFloatField(face, gFaceOffsets.eyedist, faceData.eyedist); 263 _env->SetFloatField(face, gFaceOffsets.eulerx, 0); 264 _env->SetFloatField(face, gFaceOffsets.eulery, 0); 265 _env->SetFloatField(face, gFaceOffsets.eulerz, 0); 266 } 267 268 // --------------------------------------------------------------------------- 269 270 static const char *classPathName = "android/media/FaceDetector"; 271 272 static JNINativeMethod methods[] = { 273 {"nativeClassInit", "()V", (void*)nativeClassInit }, 274 {"fft_initialize", "(III)I", (void*)initialize }, 275 {"fft_detect", "(Landroid/graphics/Bitmap;)I", (void*)detect }, 276 {"fft_get_face", "(Landroid/media/FaceDetector$Face;I)V",(void*)get_face }, 277 {"fft_destroy", "()V", (void*)destroy }, 278 }; 279 280 int register_android_media_FaceDetector(JNIEnv *_env) 281 { 282 return jniRegisterNativeMethods(_env, classPathName, methods, NELEM(methods)); 283 } 284 285 // --------------------------------------------------------------------------- 286 287 jint JNI_OnLoad(JavaVM* vm, void*) 288 { 289 JNIEnv* env = NULL; 290 jint result = -1; 291 292 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 293 ALOGE("ERROR: GetEnv failed\n"); 294 goto bail; 295 } 296 assert(env != NULL); 297 298 if (register_android_media_FaceDetector(env) < 0) { 299 ALOGE("ERROR: MediaPlayer native registration failed\n"); 300 goto bail; 301 } 302 303 /* success -- return valid version number */ 304 result = JNI_VERSION_1_4; 305 306 bail: 307 return result; 308 } 309