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