JAVA应用JNI调用OpenCV实现人脸检测

1.JAVA的JNI部分

调用JNI的基类

package lveyo.bcndyl.jni.opencv;
public class JNIBase {
	
	public JNIBase(){}
	
	public JNIBase(String libraryName){
		loadLibrary(libraryName);
	}
	
	private static void loadLibrary(String libraryName){
		System.loadLibrary(libraryName);
	}

}

实现这个基类

package lveyo.bcndyl.jni.opencv;

public class JNIOpencv extends JNIBase{
	
	public JNIOpencv (String libraryName){
		super(libraryName);
	}
	
	public JNIOpencv(){
		System.loadLibrary("jniOpenCV");
	}

    public native int[] detectFace(int minFaceWidth, int minFaceHeight,
                                   String cascade, String filename);

}

类中定义了一个detectFace方法,是要用C来实现的。

编译好这个类后,要用在命令行用javah命令生成需要的.h的头文件:

引用

javahlveyo.bcndyl.jni.opencv.JNIOpencv

会生成一个名为lveyo_bcndyl_jni_opencv_JNIOpencv.h的头文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class lveyo_bcndyl_jni_opencv_JNIOpencv */

#ifndef _Included_lveyo_bcndyl_jni_opencv_JNIOpencv
#define _Included_lveyo_bcndyl_jni_opencv_JNIOpencv
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     lveyo_bcndyl_jni_opencv_JNIOpencv
 * Method:    detectFace
 * Signature: (IILjava/lang/String;Ljava/lang/String;)[I
 */
JNIEXPORT jintArray JNICALL Java_lveyo_bcndyl_jni_opencv_JNIOpencv_detectFace
  (JNIEnv *, jobject, jint, jint, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

2.C程序部分

按照http://www.opencv.org.cn/index.php/Template:Install安装OpenCV并配置相应的开发环境,我这里用的是VS2005,同时要将JDK中的include目录和include/win32目录都加入到VS2005的IncludeFiles中。

新建一个win32MFCDLL项目jniOpenCV,把刚才生成的lveyo_bcndyl_jni_opencv_JNIOpencv.h头文件加入到项目,并且修改jniOpenCV.cpp文件:

#include "stdafx.h"
#include <jni.h>
#include "lveyo_bcndyl_jni_opencv_JNIOpencv.h"
#include "cv.h"
#include "highgui.h"

JNIEXPORT jintArray JNICALL Java_lveyo_bcndyl_jni_opencv_JNIOpencv_detectFace
  (JNIEnv *env, jobject obj, jint width, jint height, jstring cascade, jstring filename)
{
	const char *str_cascade, *str_filename;
	str_cascade = env->GetStringUTFChars(cascade, false);
	str_filename = env->GetStringUTFChars(filename, false);

	jintArray faceArray;
	CvHaarClassifierCascade *cv_cascade = (CvHaarClassifierCascade*)cvLoad( str_cascade );
	IplImage *image = cvLoadImage( str_filename, 1 );
	
	if(image!=0){

		CvMemStorage* storage = cvCreateMemStorage(0);
		CvSeq* faces;

		//double t = (double)cvGetTickCount();
		/* use the fastest variant */
		faces = cvHaarDetectObjects( image, cv_cascade, storage, 1.2, 2,
                                   CV_HAAR_DO_CANNY_PRUNING, cvSize(width, height) );
		//t = (double)cvGetTickCount() - t;
        //printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
		

		const int total = faces->total;

		faceArray = env-> NewIntArray(4*total);
		jint faceBuf[4];
		
		for( int i = 0; i < total; i++ )
		{
			CvRect face_rect = *(CvRect*)cvGetSeqElem( faces, i );
			int pointX = face_rect.x;
			int pointY = face_rect.y;
			int faceWidth = face_rect.width;
			int faceHeight = face_rect.height;

			//printf("i %d, x %d, y %d, width %d, height %d\n",
                        //        i,pointX,pointY,faceWidth,faceHeight);


			faceBuf[0] = pointX;
			faceBuf[1] = pointY;
			faceBuf[2] = faceWidth;
			faceBuf[3] = faceHeight;


			env->SetIntArrayRegion(faceArray,i*4,4,faceBuf);
			
		}
		
		cvReleaseMemStorage( &storage );
		cvReleaseImage( &image );
	}
	cvReleaseHaarClassifierCascade( &cv_cascade );
    

	env->ReleaseStringUTFChars(cascade, str_cascade);
	env->ReleaseStringUTFChars(filename, str_filename);
	return faceArray;
}

编译生成jniOpenCV.dll。此处的检测代码是根据OpenCV的文档和示例程序修改,由于本人对c++程序不是很熟练,所以对此段代码是否会有潜在危险和内存泄露不是很肯定,还请熟悉c使用的朋友帮忙检查一下。

别忘记将生成的jniOpenCV.dll文件复制到%JAVA_HOME%/bin中,如果在没安装OpenCV的机器上运行,还需要将OpenCV安装目录中bin目录下所有dll文件一起复制到%JAVA_HOME%/bin中。

3.实现JAVA的调用

package lveyo.bcndyl.jni.opencv;

public class Test {

	public static void main(String[] args) {
		
		//初始化JNI调用类JNIOpencv
		JNIOpencv open = new JNIOpencv("jniOpenCV");

		//要检测的图片文件
		String filename = "d:/80010.jpg";

		//OpenCv提供的人间的特征文件
		String cascade = "d:/haarcascade_frontalface_alt2.xml";

		//人脸检测,前两个参数为可检测的最小人脸的宽度和高度
		//返回值为人脸在图中的坐标和宽高,{x, y, width, height}
		int[] faces = open.detectFace(40, 40, cascade, filename);
		if(faces != null && faces.length!=0){

			//返回的人脸总数
			System.out.println( "faces " + faces.length/4 );

			//分别输出每个人脸的坐标信息
			for (int temp : faces) {
				System.out.println(temp);
			}
		}
	}
}

相关推荐