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