1/* 2OpenCV for Android NDK 3Copyright (c) 2006-2009 SIProp Project http://www.siprop.org/ 4 5This software is provided 'as-is', without any express or implied warranty. 6In no event will the authors be held liable for any damages arising from the use of this software. 7Permission is granted to anyone to use this software for any purpose, 8including commercial applications, and to alter it and redistribute it freely, 9subject to the following restrictions: 10 111. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 122. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 133. This notice may not be removed or altered from any source distribution. 14*/ 15#include "cvjni.h" 16#include <time.h> 17 18 19#define THRESHOLD 10 20#define THRESHOLD_MAX_VALUE 255 21 22#define CONTOUR_MAX_LEVEL 1 23#define LINE_THICKNESS 2 24#define LINE_TYPE 8 25 26#define HAAR_SCALE (1.4) 27#define IMAGE_SCALE (2) 28#define MIN_NEIGHBORS (2) 29#define HAAR_FLAGS_SINGLE_FACE (0 | CV_HAAR_FIND_BIGGEST_OBJECT | CV_HAAR_DO_ROUGH_SEARCH) 30#define HAAR_FLAGS_ALL_FACES (0) 31// Other options we dropped out: 32// CV_HAAR_DO_CANNY_PRUNING | CV_HAAR_SCALE_IMAGE 33#define MIN_SIZE_WIDTH (20) 34#define MIN_SIZE_HEIGHT (20) 35#define PAD_FACE_SIZE (10) 36#define PAD_FACE_AREA (40) 37#define PAD_FACE_AREA_2 (PAD_FACE_AREA * 2) 38 39// Initialize a socket capture to grab images from a socket connection. 40JNIEXPORT 41jboolean 42JNICALL 43Java_org_siprop_opencv_OpenCV_createSocketCapture(JNIEnv* env, 44 jobject thiz, 45 jstring address_str, 46 jstring port_str, 47 jint width, 48 jint height) { 49 const char *address_chars = env->GetStringUTFChars(address_str, 0); 50 if (address_chars == 0) { 51 LOGV("Error loading socket address."); 52 return false; 53 } 54 55 const char *port_chars = env->GetStringUTFChars(port_str, 0); 56 if (port_chars == 0) { 57 env->ReleaseStringUTFChars(address_str, address_chars); 58 LOGV("Error loading socket port."); 59 return false; 60 } 61 62 m_capture = cvCreateSocketCapture(address_chars, port_chars, width, height); 63 env->ReleaseStringUTFChars(address_str, address_chars); 64 env->ReleaseStringUTFChars(port_str, port_chars); 65 if (m_capture == 0) 66 { 67 LOGV("Error creating socket capture."); 68 return false; 69 } 70 71 return true; 72} 73 74JNIEXPORT 75void 76JNICALL 77Java_org_siprop_opencv_OpenCV_releaseSocketCapture(JNIEnv* env, 78 jobject thiz) { 79 if (m_capture) { 80 cvReleaseCapture(&m_capture); 81 m_capture = 0; 82 } 83} 84 85JNIEXPORT 86jboolean 87JNICALL 88Java_org_siprop_opencv_OpenCV_grabSourceImageFromCapture(JNIEnv* env, 89 jobject thiz) { 90 if (m_capture == 0) 91 { 92 LOGE("Capture was never initialized."); 93 return false; 94 } 95 96 if (cvGrabFrame(m_capture) == 0) 97 { 98 LOGE("Failed to grab frame from the capture."); 99 return false; 100 } 101 102 IplImage *frame = cvRetrieveFrame(m_capture); 103 if (frame == 0) 104 { 105 LOGE("Failed to retrieve frame from the capture."); 106 return false; 107 } 108 109 if (m_sourceImage) { 110 cvReleaseImage(&m_sourceImage); 111 m_sourceImage = 0; 112 } 113 114 m_sourceImage = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 115 frame->nChannels); 116 117 // Check the origin of image. If top left, copy the image frame to frame_copy. 118 // Else flip and copy the image. 119 if (frame->origin == IPL_ORIGIN_TL) { 120 cvCopy(frame, m_sourceImage, 0); 121 } 122 else { 123 cvFlip(frame, m_sourceImage, 0); 124 } 125 126 return true; 127} 128 129// Generate and return a boolean array from the source image. 130// Return 0 if a failure occurs or if the source image is undefined. 131JNIEXPORT 132jbooleanArray 133JNICALL 134Java_org_siprop_opencv_OpenCV_getSourceImage(JNIEnv* env, 135 jobject thiz) 136{ 137 if (m_sourceImage == 0) { 138 LOGE("Error source image was not set."); 139 return 0; 140 } 141 142 CvMat stub; 143 CvMat *mat_image = cvGetMat(m_sourceImage, &stub); 144 int channels = CV_MAT_CN( mat_image->type ); 145 int ipl_depth = cvCvToIplDepth(mat_image->type); 146 147 WLNonFileByteStream *strm = new WLNonFileByteStream(); 148 loadImageBytes(mat_image->data.ptr, mat_image->step, mat_image->width, 149 mat_image->height, ipl_depth, channels, strm); 150 151 int imageSize = strm->GetSize(); 152 jbooleanArray res_array = env->NewBooleanArray(imageSize); 153 if (res_array == 0) { 154 LOGE("Unable to allocate a new boolean array for the source image."); 155 return 0; 156 } 157 env->SetBooleanArrayRegion(res_array, 0, imageSize, (jboolean*)strm->GetByte()); 158 159 strm->Close(); 160 SAFE_DELETE(strm); 161 162 return res_array; 163} 164 165// Given an integer array of image data, load an IplImage. 166// It is the responsibility of the caller to release the IplImage. 167IplImage* getIplImageFromIntArray(JNIEnv* env, jintArray array_data, 168 jint width, jint height) { 169 // Load Image 170 int *pixels = env->GetIntArrayElements(array_data, 0); 171 if (pixels == 0) { 172 LOGE("Error getting int array of pixels."); 173 return 0; 174 } 175 176 IplImage *image = loadPixels(pixels, width, height); 177 env->ReleaseIntArrayElements(array_data, pixels, 0); 178 if(image == 0) { 179 LOGE("Error loading pixel array."); 180 return 0; 181 } 182 183 return image; 184} 185 186// Set the source image and return true if successful or false otherwise. 187JNIEXPORT 188jboolean 189JNICALL 190Java_org_siprop_opencv_OpenCV_setSourceImage(JNIEnv* env, 191 jobject thiz, 192 jintArray photo_data, 193 jint width, 194 jint height) 195{ 196 // Release the image if it hasn't already been released. 197 if (m_sourceImage) { 198 cvReleaseImage(&m_sourceImage); 199 m_sourceImage = 0; 200 } 201 m_facesFound = 0; 202 203 m_sourceImage = getIplImageFromIntArray(env, photo_data, width, height); 204 if (m_sourceImage == 0) { 205 LOGE("Error source image could not be created."); 206 return false; 207 } 208 209 return true; 210} 211 212JNIEXPORT 213jbooleanArray 214JNICALL 215Java_org_siprop_opencv_OpenCV_findContours(JNIEnv* env, 216 jobject thiz, 217 jint width, 218 jint height) { 219 IplImage *grayImage = cvCreateImage( cvGetSize(m_sourceImage), IPL_DEPTH_8U, 1 ); // O[XP[æpIplImage 220 IplImage *binaryImage = cvCreateImage( cvGetSize(m_sourceImage), IPL_DEPTH_8U, 1 ); // 2læpIplImage 221 IplImage *contourImage = cvCreateImage( cvGetSize(m_sourceImage), IPL_DEPTH_8U, 3 ); // ÖsæpIplImage 222 223 // BGR©çO[XP[ÉÏ··é 224 cvCvtColor( m_sourceImage, grayImage, CV_BGR2GRAY ); 225 226 // O[XP[©ç2lÉÏ··é 227 cvThreshold( grayImage, binaryImage, THRESHOLD, THRESHOLD_MAX_VALUE, CV_THRESH_BINARY ); 228 229 // ÖsopÌðmÛ·é 230 CvMemStorage* storage = cvCreateMemStorage( 0 ); // o³ê½ÖsðÛ¶·éÌæ 231 CvSeq* find_contour = 0; // ÖsÖÌ|C^ 232 233 // 2læÌÖsð©Â¯A»ÌðÔ· 234 int find_contour_num = cvFindContours( 235 binaryImage, // üÍæ(WrbgVO`lj 236 storage, // o³ê½ÖsðÛ¶·éÌæ 237 &find_contour, // êÔO¤ÌÖsÖÌ|C^ÖÌ|C^ 238 sizeof( CvContour ), // V[PXwb_ÌTCY 239 CV_RETR_LIST, // o[h 240 CV_CHAIN_APPROX_NONE, // èè@ 241 cvPoint( 0, 0 ) // ItZbg 242 ); 243 244 // ¨ÌÌÖsðÔFÅ`æ·é 245 CvScalar red = CV_RGB( 255, 0, 0 ); 246 cvDrawContours( 247 m_sourceImage, // Ösð`æ·éæ 248 find_contour, // ÅÌÖsÖÌ|C^ 249 red, // O¤ÖsüÌF 250 red, // à¤ÖsüijÌF 251 CONTOUR_MAX_LEVEL, // `æ³êéÖsÌÅåx 252 LINE_THICKNESS, // `æ³êéÖsü̾³ 253 LINE_TYPE, // üÌíÞ 254 cvPoint( 0, 0 ) // ItZbg 255 ); 256 257 int imageSize; 258 CvMat stub, *mat_image; 259 int channels, ipl_depth; 260 mat_image = cvGetMat( m_sourceImage, &stub ); 261 channels = CV_MAT_CN( mat_image->type ); 262 263 ipl_depth = cvCvToIplDepth(mat_image->type); 264 265 LOGV("Load loadImageBytes."); 266 WLNonFileByteStream* strm = new WLNonFileByteStream(); 267 loadImageBytes(mat_image->data.ptr, mat_image->step, mat_image->width, 268 mat_image->height, ipl_depth, channels, strm); 269 270 imageSize = strm->GetSize(); 271 jbooleanArray res_array = env->NewBooleanArray(imageSize); 272 LOGV("Load NewBooleanArray."); 273 if (res_array == 0) { 274 return 0; 275 } 276 env->SetBooleanArrayRegion(res_array, 0, imageSize, (jboolean*)strm->GetByte()); 277 LOGV("Load SetBooleanArrayRegion."); 278 279 LOGV("Release sourceImage"); 280 if (m_sourceImage) { 281 cvReleaseImage(&m_sourceImage); 282 m_sourceImage = 0; 283 } 284 LOGV("Release binaryImage"); 285 cvReleaseImage( &binaryImage ); 286 LOGV("Release grayImage"); 287 cvReleaseImage( &grayImage ); 288 LOGV("Release contourImage"); 289 cvReleaseImage( &contourImage ); 290 LOGV("Release storage"); 291 cvReleaseMemStorage( &storage ); 292 LOGV("Delete strm"); 293 strm->Close(); 294 SAFE_DELETE(strm); 295 296 return res_array; 297} 298 299JNIEXPORT 300jboolean 301JNICALL 302Java_org_siprop_opencv_OpenCV_initFaceDetection(JNIEnv* env, 303 jobject thiz, 304 jstring cascade_path_str) { 305 306 // First call release to ensure the memory is empty. 307 Java_org_siprop_opencv_OpenCV_releaseFaceDetection(env, thiz); 308 309 char buffer[100]; 310 clock_t total_time_start = clock(); 311 312 m_smallestFaceSize.width = MIN_SIZE_WIDTH; 313 m_smallestFaceSize.height = MIN_SIZE_HEIGHT; 314 315 const char *cascade_path_chars = env->GetStringUTFChars(cascade_path_str, 0); 316 if (cascade_path_chars == 0) { 317 LOGE("Error getting cascade string."); 318 return false; 319 } 320 321 m_cascade = (CvHaarClassifierCascade*)cvLoad(cascade_path_chars); 322 env->ReleaseStringUTFChars(cascade_path_str, cascade_path_chars); 323 if (m_cascade == 0) { 324 LOGE("Error loading cascade."); 325 return false; 326 } 327 328 m_storage = cvCreateMemStorage(0); 329 330 clock_t total_time_finish = clock() - total_time_start; 331 sprintf(buffer, "Total Time to init: %f", (double)total_time_finish / (double)CLOCKS_PER_SEC); 332 LOGV(buffer); 333 334 return true; 335} 336 337// Release all of the memory used by face tracking. 338JNIEXPORT 339void 340JNICALL 341Java_org_siprop_opencv_OpenCV_releaseFaceDetection(JNIEnv* env, 342 jobject thiz) { 343 344 m_facesFound = 0; 345 m_faceCropArea.width = m_faceCropArea.height = 0; 346 347 if (m_cascade) { 348 cvReleaseHaarClassifierCascade(&m_cascade); 349 m_cascade = 0; 350 } 351 352 if (m_sourceImage) { 353 cvReleaseImage(&m_sourceImage); 354 m_sourceImage = 0; 355 } 356 357 if (m_grayImage) { 358 cvReleaseImage(&m_grayImage); 359 m_grayImage = 0; 360 } 361 362 if (m_smallImage) { 363 cvReleaseImage(&m_smallImage); 364 m_smallImage = 0; 365 } 366 367 if (m_storage) { 368 cvReleaseMemStorage(&m_storage); 369 m_storage = 0; 370 } 371} 372 373// Initalize the small image and the gray image using the input source image. 374// If a previous face was specified, we will limit the ROI to that face. 375void initFaceDetectionImages(IplImage *sourceImage, double scale = 1.0) { 376 if (m_grayImage == 0) { 377 m_grayImage = cvCreateImage(cvGetSize(sourceImage), IPL_DEPTH_8U, 1); 378 } 379 380 if (m_smallImage == 0) { 381 m_smallImage = cvCreateImage(cvSize(cvRound(sourceImage->width / scale), 382 cvRound(sourceImage->height / scale)), IPL_DEPTH_8U, 1); 383 } 384 385 if(m_faceCropArea.width > 0 && m_faceCropArea.height > 0) { 386 cvSetImageROI(m_smallImage, m_faceCropArea); 387 388 CvRect tPrev = cvRect(m_faceCropArea.x * scale, m_faceCropArea.y * scale, 389 m_faceCropArea.width * scale, m_faceCropArea.height * scale); 390 cvSetImageROI(sourceImage, tPrev); 391 cvSetImageROI(m_grayImage, tPrev); 392 } else { 393 cvResetImageROI(m_smallImage); 394 cvResetImageROI(m_grayImage); 395 } 396 397 cvCvtColor(sourceImage, m_grayImage, CV_BGR2GRAY); 398 cvResize(m_grayImage, m_smallImage, CV_INTER_LINEAR); 399 cvEqualizeHist(m_smallImage, m_smallImage); 400 cvClearMemStorage(m_storage); 401 402 cvResetImageROI(sourceImage); 403} 404 405// Given a sequence of rectangles, return an array of Android Rect objects 406// or null if any errors occur. 407jobjectArray seqRectsToAndroidRects(JNIEnv* env, CvSeq *rects) { 408 if (rects == 0 || rects->total <= 0) { 409 LOGE("No rectangles were specified!"); 410 return 0; 411 } 412 413 jclass jcls = env->FindClass("android/graphics/Rect"); 414 if (jcls == 0) { 415 LOGE("Unable to find class android.graphics.Rect"); 416 return 0; 417 } 418 419 jmethodID jconstruct = env->GetMethodID(jcls, "<init>", "(IIII)V"); 420 if (jconstruct == 0) { 421 LOGE("Unable to find constructor Rect(int, int, int, int)"); 422 return 0; 423 } 424 425 jobjectArray ary = env->NewObjectArray(rects->total, jcls, 0); 426 if (ary == 0) { 427 LOGE("Unable to create Rect array"); 428 return 0; 429 } 430 431 for (int i = 0; i < rects->total; i++) { 432 char buffer[100]; 433 CvRect *rect = (CvRect*)cvGetSeqElem(rects, i); 434 if (rect == 0) { 435 sprintf(buffer, "Invalid Rectangle #%d", i); 436 LOGE(buffer); 437 return 0; 438 } 439 440 jobject jrect = env->NewObject(jcls, jconstruct, rect->x, rect->y, 441 rect->width, rect->height); 442 if (jrect == 0) { 443 sprintf(buffer, "Unable to create Android Rect object for rectangle #%d", i); 444 LOGE(buffer); 445 return 0; 446 } 447 448 env->SetObjectArrayElement(ary, i, jrect); 449 env->DeleteLocalRef(jrect); 450 } 451 452 return ary; 453} 454 455// Identify all of the faces in the source image and return an array 456// of Android Rect objects with the face coordinates. If any errors 457// occur, a 0 array will be returned. 458JNIEXPORT 459jobjectArray 460JNICALL 461Java_org_siprop_opencv_OpenCV_findAllFaces(JNIEnv* env, 462 jobject thiz) { 463 char buffer[100]; 464 clock_t total_time_start = clock(); 465 466 if (m_cascade == 0 || m_storage == 0) { 467 LOGE("Error find faces was not initialized."); 468 return 0; 469 } 470 471 if (m_sourceImage == 0) { 472 LOGE("Error source image was not set."); 473 return 0; 474 } 475 476 initFaceDetectionImages(m_sourceImage, IMAGE_SCALE); 477 478 clock_t haar_detect_time_start = clock(); 479 m_facesFound = mycvHaarDetectObjects(m_smallImage, m_cascade, m_storage, HAAR_SCALE, 480 MIN_NEIGHBORS, HAAR_FLAGS_ALL_FACES, cvSize(MIN_SIZE_WIDTH, MIN_SIZE_HEIGHT)); 481 482 clock_t haar_detect_time_finish = clock() - haar_detect_time_start; 483 sprintf(buffer, "Total Time to cvHaarDetectObjects in findAllFaces: %f", (double)haar_detect_time_finish / (double)CLOCKS_PER_SEC); 484 LOGV(buffer); 485 486 jobjectArray faceRects = 0; 487 if (m_facesFound == 0 || m_facesFound->total <= 0) { 488 LOGV("FACES_DETECTED 0"); 489 } else { 490 sprintf(buffer, "FACES_DETECTED %d", m_facesFound->total); 491 LOGV(buffer); 492 m_faceCropArea.width = m_faceCropArea.height = 0; 493 faceRects = seqRectsToAndroidRects(env, m_facesFound); 494 } 495 496 clock_t total_time_finish = clock() - total_time_start; 497 sprintf(buffer, "Total Time to findAllFaces: %f", (double)total_time_finish / (double)CLOCKS_PER_SEC); 498 LOGV(buffer); 499 500 return faceRects; 501} 502 503// Store the previous face found in the scene. 504void storePreviousFace(CvRect* face) { 505 char buffer[100]; 506 if (m_faceCropArea.width > 0 && m_faceCropArea.height > 0) { 507 face->x += m_faceCropArea.x; 508 face->y += m_faceCropArea.y; 509 sprintf(buffer, "Face rect + m_faceCropArea: (%d, %d) to (%d, %d)", face->x, face->y, 510 face->x + face->width, face->y + face->height); 511 LOGV(buffer); 512 } 513 514 int startX = MAX(face->x - PAD_FACE_AREA, 0); 515 int startY = MAX(face->y - PAD_FACE_AREA, 0); 516 int w = m_smallImage->width - startX - face->width - PAD_FACE_AREA_2; 517 int h = m_smallImage->height - startY - face->height - PAD_FACE_AREA_2; 518 int sw = face->x - PAD_FACE_AREA, sh = face->y - PAD_FACE_AREA; 519 m_faceCropArea = cvRect(startX, startY, 520 face->width + PAD_FACE_AREA_2 + ((w < 0) ? w : 0) + ((sw < 0) ? sw : 0), 521 face->height + PAD_FACE_AREA_2 + ((h < 0) ? h : 0) + ((sh < 0) ? sh : 0)); 522 sprintf(buffer, "m_faceCropArea: (%d, %d) to (%d, %d)", m_faceCropArea.x, m_faceCropArea.y, 523 m_faceCropArea.x + m_faceCropArea.width, m_faceCropArea.y + m_faceCropArea.height); 524 LOGV(buffer); 525} 526 527// Given a rectangle, return an Android Rect object or null if any 528// errors occur. 529jobject rectToAndroidRect(JNIEnv* env, CvRect *rect) { 530 if (rect == 0) { 531 LOGE("No rectangle was specified!"); 532 return 0; 533 } 534 535 jclass jcls = env->FindClass("android/graphics/Rect"); 536 if (jcls == 0) { 537 LOGE("Unable to find class android.graphics.Rect"); 538 return 0; 539 } 540 541 jmethodID jconstruct = env->GetMethodID(jcls, "<init>", "(IIII)V"); 542 if (jconstruct == 0) { 543 LOGE("Unable to find constructor Rect(int, int, int, int)"); 544 return 0; 545 } 546 547 return env->NewObject(jcls, jconstruct, rect->x, rect->y, 548 rect->width, rect->height); 549} 550 551// Identify a single face in the source image and return an Android 552// Android Rect object with the face coordinates. This method is 553// optimized by focusing on a single face and cropping the detection 554// region to the area where the face is located plus some additional 555// padding to account for slight head movements. If any errors occur, 556// a 0 array will be returned. 557JNIEXPORT 558jobject 559JNICALL 560Java_org_siprop_opencv_OpenCV_findSingleFace(JNIEnv* env, 561 jobject thiz) { 562 char buffer[100]; 563 clock_t total_time_start = clock(); 564 565 if (m_cascade == 0 || m_storage == 0) { 566 LOGE("Error find faces was not initialized."); 567 return 0; 568 } 569 570 if (m_sourceImage == 0) { 571 LOGE("Error source image was not set."); 572 return 0; 573 } 574 575 initFaceDetectionImages(m_sourceImage, IMAGE_SCALE); 576 577 clock_t haar_detect_time_start = clock(); 578 m_facesFound = mycvHaarDetectObjects(m_smallImage, m_cascade, m_storage, HAAR_SCALE, 579 MIN_NEIGHBORS, HAAR_FLAGS_SINGLE_FACE, m_smallestFaceSize); 580 581 clock_t haar_detect_time_finish = clock() - haar_detect_time_start; 582 sprintf(buffer, "Total Time to cvHaarDetectObjects in findSingleFace: %f", (double)haar_detect_time_finish / (double)CLOCKS_PER_SEC); 583 LOGV(buffer); 584 585 jobject faceRect = 0; 586 if (m_facesFound == 0 || m_facesFound->total <= 0) { 587 LOGV("FACES_DETECTED 0"); 588 m_faceCropArea.width = m_faceCropArea.height = 0; 589 m_smallestFaceSize.width = MIN_SIZE_WIDTH; 590 m_smallestFaceSize.height = MIN_SIZE_HEIGHT; 591 } else { 592 LOGV("FACES_DETECTED 1"); 593 CvRect *face = (CvRect*)cvGetSeqElem(m_facesFound, 0); 594 if (face == 0) { 595 LOGE("Invalid rectangle detected"); 596 return 0; 597 } 598 m_smallestFaceSize.width = MAX(face->width - PAD_FACE_SIZE, MIN_SIZE_WIDTH); 599 m_smallestFaceSize.height = MAX(face->height - PAD_FACE_SIZE, MIN_SIZE_HEIGHT); 600 faceRect = rectToAndroidRect(env, face); 601 storePreviousFace(face); 602 } 603 604 clock_t total_time_finish = clock() - total_time_start; 605 sprintf(buffer, "Total Time to findSingleFace: %f", (double)total_time_finish / (double)CLOCKS_PER_SEC); 606 LOGV(buffer); 607 608 return faceRect; 609} 610 611// Draw a rectangle on the source image around the specified face rectangle. 612// Scale the face area to the draw area based on the specified scale. 613void highlightFace(IplImage *sourceImage, CvRect *face, double scale = 1.0) { 614 char buffer[100]; 615 sprintf(buffer, "Face Rectangle: (x: %d, y: %d) to (w: %d, h: %d)", 616 face->x, face->y, face->width, face->height); 617 LOGV(buffer); 618 CvPoint pt1 = cvPoint(int(face->x * scale), int(face->y * scale)); 619 CvPoint pt2 = cvPoint(int((face->x + face->width) * scale), 620 int((face->y + face->height) * scale)); 621 sprintf(buffer, "Draw Rectangle: (%d, %d) to (%d, %d)", pt1.x, pt1.y, pt2.x, pt2.y); 622 LOGV(buffer); 623 cvRectangle(sourceImage, pt1, pt2, CV_RGB(255, 0, 0), 3, 8, 0); 624} 625 626// Draw rectangles on the source image around each face that was found. 627// Scale the face area to the draw area based on the specified scale. 628// Return true if at least one face was highlighted and false otherwise. 629bool highlightFaces(IplImage *sourceImage, CvSeq *faces, double scale = 1.0) { 630 if (faces == 0 || faces->total <= 0) { 631 LOGV("No faces were highlighted!"); 632 return false; 633 } else { 634 LOGV("Drawing rectangles on each face"); 635 int count; 636 CvRect* face; 637 for (int i = 0; i < faces->total; i++) { 638 face = (CvRect*)cvGetSeqElem(faces, i); 639 highlightFace(sourceImage, face, scale); 640 } 641 } 642 643 return true; 644} 645 646// Highlight the faces that were detected in the source image. 647// Return true if one or more faces is highlighted or false otherwise. 648JNIEXPORT 649jboolean 650JNICALL 651Java_org_siprop_opencv_OpenCV_highlightFaces(JNIEnv* env, 652 jobject thiz) { 653 if (m_facesFound == 0 || m_facesFound->total <= 0) { 654 LOGV("No faces found to highlight!"); 655 return false; 656 } else { 657 highlightFaces(m_sourceImage, m_facesFound, IMAGE_SCALE); 658 } 659 660 return true; 661} 662 663#if 0 664 665JNIEXPORT 666jbooleanArray 667JNICALL 668Java_org_siprop_opencv_OpenCV_faceDetect(JNIEnv* env, 669 jobject thiz, 670 jintArray photo_data1, 671 jintArray photo_data2, 672 jint width, 673 jint height) { 674 LOGV("Load desp."); 675 676 int i, x, y; 677 int* pixels; 678 IplImage *frameImage; 679 680 IplImage *backgroundImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 681 IplImage *grayImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 682 IplImage *differenceImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 683 684 IplImage *hsvImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 3 ); 685 IplImage *hueImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 686 IplImage *saturationImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 687 IplImage *valueImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 688 IplImage *thresholdImage1 = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 689 IplImage *thresholdImage2 = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 690 IplImage *thresholdImage3 = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 691 IplImage *faceImage = cvCreateImage( cvSize(width, height), IPL_DEPTH_8U, 1 ); 692 693 CvMoments moment; 694 double m_00; 695 double m_10; 696 double m_01; 697 int gravityX; 698 int gravityY; 699 700 jbooleanArray res_array; 701 int imageSize; 702 703 704 705 // Load Image 706 pixels = env->GetIntArrayElements(photo_data1, 0); 707 frameImage = loadPixels(pixels, width, height); 708 if(frameImage == 0) { 709 LOGV("Error loadPixels."); 710 return 0; 711 } 712 713 714 cvCvtColor( frameImage, backgroundImage, CV_BGR2GRAY ); 715 716 717 pixels = env->GetIntArrayElements(photo_data2, 0); 718 frameImage = loadPixels(pixels, width, height); 719 if(frameImage == 0) { 720 LOGV("Error loadPixels."); 721 return 0; 722 } 723 cvCvtColor( frameImage, grayImage, CV_BGR2GRAY ); 724 cvAbsDiff( grayImage, backgroundImage, differenceImage ); 725 726 cvCvtColor( frameImage, hsvImage, CV_BGR2HSV ); 727 LOGV("Load cvCvtColor."); 728 cvSplit( hsvImage, hueImage, saturationImage, valueImage, 0 ); 729 LOGV("Load cvSplit."); 730 cvThreshold( hueImage, thresholdImage1, THRESH_BOTTOM, THRESHOLD_MAX_VALUE, CV_THRESH_BINARY ); 731 cvThreshold( hueImage, thresholdImage2, THRESH_TOP, THRESHOLD_MAX_VALUE, CV_THRESH_BINARY_INV ); 732 cvAnd( thresholdImage1, thresholdImage2, thresholdImage3, 0 ); 733 LOGV("Load cvAnd."); 734 735 cvAnd( differenceImage, thresholdImage3, faceImage, 0 ); 736 737 cvMoments( faceImage, &moment, 0 ); 738 m_00 = cvGetSpatialMoment( &moment, 0, 0 ); 739 m_10 = cvGetSpatialMoment( &moment, 1, 0 ); 740 m_01 = cvGetSpatialMoment( &moment, 0, 1 ); 741 gravityX = m_10 / m_00; 742 gravityY = m_01 / m_00; 743 LOGV("Load cvMoments."); 744 745 746 cvCircle( frameImage, cvPoint( gravityX, gravityY ), CIRCLE_RADIUS, 747 CV_RGB( 255, 0, 0 ), LINE_THICKNESS, LINE_TYPE, 0 ); 748 749 750 751 752 CvMat stub, *mat_image; 753 int channels, ipl_depth; 754 mat_image = cvGetMat( frameImage, &stub ); 755 channels = CV_MAT_CN( mat_image->type ); 756 757 ipl_depth = cvCvToIplDepth(mat_image->type); 758 759 WLNonFileByteStream* m_strm = new WLNonFileByteStream(); 760 loadImageBytes(mat_image->data.ptr, mat_image->step, mat_image->width, 761 mat_image->height, ipl_depth, channels, m_strm); 762 LOGV("Load loadImageBytes."); 763 764 765 imageSize = m_strm->GetSize(); 766 res_array = env->NewBooleanArray(imageSize); 767 LOGV("Load NewByteArray."); 768 if (res_array == 0) { 769 return 0; 770 } 771 env->SetBooleanArrayRegion(res_array, 0, imageSize, (jboolean*)m_strm->GetByte()); 772 LOGV("Load SetBooleanArrayRegion."); 773 774 775 776 777 cvReleaseImage( &backgroundImage ); 778 cvReleaseImage( &grayImage ); 779 cvReleaseImage( &differenceImage ); 780 cvReleaseImage( &hsvImage ); 781 cvReleaseImage( &hueImage ); 782 cvReleaseImage( &saturationImage ); 783 cvReleaseImage( &valueImage ); 784 cvReleaseImage( &thresholdImage1 ); 785 cvReleaseImage( &thresholdImage2 ); 786 cvReleaseImage( &thresholdImage3 ); 787 cvReleaseImage( &faceImage ); 788 cvReleaseImage( &frameImage ); 789 m_strm->Close(); 790 SAFE_DELETE(m_strm); 791 792 return res_array; 793 794} 795#endif 796 797