blob: 14731a9462dde1c53b44584048848fec070bc7a9 [file] [log] [blame]
Marius Renn6acb9a72011-05-25 13:40:38 -07001/*
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ƒŒ[ƒXƒP[ƒ‹‰æ‘œ—pIplImage
220 IplImage *binaryImage = cvCreateImage( cvGetSize(m_sourceImage), IPL_DEPTH_8U, 1 ); // 2’l‰æ‘œ—pIplImage
221 IplImage *contourImage = cvCreateImage( cvGetSize(m_sourceImage), IPL_DEPTH_8U, 3 ); // —ÖŠs‰æ‘œ—pIplImage
222
223 // BGR‚©‚çƒOƒŒ[ƒXƒP[ƒ‹‚É•ÏŠ·‚·‚é
224 cvCvtColor( m_sourceImage, grayImage, CV_BGR2GRAY );
225
226 // ƒOƒŒ[ƒXƒP[ƒ‹‚©‚ç2’l‚É•ÏŠ·‚·‚é
227 cvThreshold( grayImage, binaryImage, THRESHOLD, THRESHOLD_MAX_VALUE, CV_THRESH_BINARY );
228
229 // —ÖŠs’Šo—p‚̃ƒ‚ƒŠ‚ðŠm•Û‚·‚é
230 CvMemStorage* storage = cvCreateMemStorage( 0 ); // ’Šo‚³‚ꂽ—ÖŠs‚ð•Û‘¶‚·‚é—̈æ
231 CvSeq* find_contour = 0; // —ÖŠs‚ւ̃|ƒCƒ“ƒ^
232
233 // 2’l‰æ‘œ’†‚Ì—ÖŠs‚ðŒ©‚Â‚¯A‚»‚̐”‚ð•Ô‚·
234 int find_contour_num = cvFindContours(
235 binaryImage, // “ü—͉摜(‚WƒrƒbƒgƒVƒ“ƒOƒ‹ƒ`ƒƒƒ“ƒlƒ‹j
236 storage, // ’Šo‚³‚ꂽ—ÖŠs‚ð•Û‘¶‚·‚é—̈æ
237 &find_contour, // ˆê”ÔŠO‘¤‚Ì—ÖŠs‚ւ̃|ƒCƒ“ƒ^‚ւ̃|ƒCƒ“ƒ^
238 sizeof( CvContour ), // ƒV[ƒPƒ“ƒXƒwƒbƒ_‚̃TƒCƒY
239 CV_RETR_LIST, // ’Šoƒ‚[ƒh
240 CV_CHAIN_APPROX_NONE, // „’èŽè–@
241 cvPoint( 0, 0 ) // ƒIƒtƒZƒbƒg
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üiŒŠj‚̐F
251 CONTOUR_MAX_LEVEL, // •`‰æ‚³‚ê‚é—ÖŠs‚̍ő僌ƒxƒ‹
252 LINE_THICKNESS, // •`‰æ‚³‚ê‚é—ÖŠsü‚Ì‘¾‚³
253 LINE_TYPE, // ü‚ÌŽí—Þ
254 cvPoint( 0, 0 ) // ƒIƒtƒZƒbƒg
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