JAVA应用JNI调用OpenCV2.1实现人脸检测

1.JAVA JNI部分


a.调用JNI的基类(JNIBase.java)
package info.lveyo.opencv.facedetection;

public class JNIBase {

public JNIBase() {
}

public JNIBase(String libraryName) {
loadLibrary(libraryName);
}

private static void loadLibrary(String libraryName) {
System.loadLibrary(libraryName);
}

}

b.实现这个基类(JNIOpencv.java)
package info.lveyo.opencv.facedetection;

public class JNIOpencv extends JNIBase {

public JNIOpencv(String libraryName) {
super(libraryName);
}

public JNIOpencv() {
System.loadLibrary("JNIOpenCV21");
}

/*调用DLL的方法实现人脸检测,返回人脸的坐标(x, y, width, height)*/
public native int[] detectFace(int minFaceWidth, int minFaceHeight, String cascade, String filename, double scale);

}

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

生成的.h头文件是info_lveyo_opencv_facedetection_JNIOpencv.h,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class info_lveyo_opencv_facedetection_JNIOpencv */

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

#ifdef __cplusplus
}
#endif
#endif

需要用C来实现Java_info_lveyo_opencv_facedetection_JNIOpencv_detectFace这个函数。

2.C程序部分


a.编译环境采用VS2008,安装和配置OpenCV2.1开发环境,可以参考这篇文章:
http://www.opencv.org.cn/index.php/VC_2008_Express%E4%B8%8B%E5%AE%89%E8%A3%85OpenCV2.0/2.1
还需要将JDK目录下的include目录和include/win32目录都加入到VS2008的包含文件中,以便程序可以引用jni.h头文件。

b.新建项目,项目类型依次选择"Visual C++", "WIN32", "WIN32项目",项目名称输入"JNIOpenCV21",点确定按钮后进入应用程序向导,在应用程序类型中选中"DLL",点击完成按钮。

c.把刚才生成的info_lveyo_opencv_facedetection_JNIOpencv.h文件加入到项目,项目的文件结构如下:

d.编辑JNIOpenCV21.cpp文件:
// JNIOpenCV21.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"

#include "info_lveyo_opencv_facedetection_JNIOpencv.h"
#include <jni.h>

#include "highgui.h"
#include "cv.h"

using namespace std;
using namespace cv;

JNIEXPORT jintArray JNICALL Java_info_lveyo_opencv_facedetection_JNIOpencv_detectFace
(JNIEnv *env, jobject obj, jint widthInput, jint heightInput, jstring cascadeInput, jstring filenameInput, jdouble scaleInput)
{
//get the parameters from jni
const char *str_cascade, *str_filename;
str_cascade = env->GetStringUTFChars(cascadeInput, false);
str_filename = env->GetStringUTFChars(filenameInput, false);

//cascade
CascadeClassifier cascade;

//return value
jintArray faceArray;

Mat img = imread(str_filename);

if(!img.empty() && cascade.load( str_cascade ))
{
vector faces;
Mat gray, smallImg( cvRound (img.rows/scaleInput),
cvRound(img.cols/scaleInput), CV_8UC1 );

//img becomes to gray
cvtColor( img, gray, CV_BGR2GRAY );

if(scaleInput>0)
{
//resize the gray image
resize( gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR );

//detection
cascade.detectMultiScale( smallImg, faces,
1.1, 2, 0
//|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
|CV_HAAR_SCALE_IMAGE
,
Size(widthInput, heightInput) );

}
else
{
//detection
cascade.detectMultiScale( gray, faces,
1.1, 2, 0
//|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
|CV_HAAR_SCALE_IMAGE
,
Size(widthInput, heightInput) );

}

faceArray = env-> NewIntArray(4*faces.size());
jint faceBuf[4];

int i = 0;
for( vector::const_iterator r = faces.begin(); r != faces.end(); r++, i++)
{
faceBuf[0] = cvRound (r->x * scaleInput);
faceBuf[1] = cvRound (r->y * scaleInput);
faceBuf[2] = cvRound (r->width * scaleInput);
faceBuf[3] = cvRound (r->height * scaleInput);

env->SetIntArrayRegion(faceArray,i*4,4,faceBuf);
}

}

env->ReleaseStringUTFChars(cascadeInput, str_cascade);
env->ReleaseStringUTFChars(filenameInput, str_filename);

return faceArray;

}

编译之前别忘了为项目添加增加依赖的库,选择"解决方案管理器"里的"JNIOpenCV21"项目,点击鼠标右键,选择"属性",
为项目的Debug配置增加依赖的库:cxcore210d.lib cv210d.lib highgui210d.lib
为项目的Release配置增加依赖的库:cxcore210.lib cv210.lib highgui210.lib
然后就可以编译生成JNIOpenCV21.dll文件了。

e.将编译好的JNIOpenCV21.dll文件复制到JDK的bin目录下,在把OpenCV的bin目录下的所有dll文件都复制到classpath指定的目录中。在eclipse中,可以将这些dll文件复制到src目录下。


3.实现JAVA调用


编写一个简单的测试类:
package info.lveyo.opencv.test;

import info.lveyo.opencv.facedetection.JNIOpencv;

public class FaceDetectionTest {

public static void main(String[] args) {

// 初始化JNI调用类JNIOpencv
JNIOpencv open = new JNIOpencv("JNIOpenCV21");

// 要检测的图片文件名
String iamgeFilename = "d:/lena.jpg";

// //OpenCv提供的人脸的特征文件
String cascadeFile = "d:/haarcascade_frontalface_alt.xml";

// 要检测的最小的人脸宽度和高度,此数值越大检测的速度越快,同时检测率也越低
int minFaceWidth = 80;
int minFaceHeight = 80;

// 计时开始
long t = System.currentTimeMillis();

// 返回值为人脸在图中的坐标和宽高,{x, y, width, height}
int[] faces = open.detectFace(minFaceWidth, minFaceHeight, cascadeFile,
iamgeFilename, 1);
if (faces != null && faces.length != 0) {

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

// 分别输出每个人脸的坐标信息
int faceCount = 1;
for (int i = 0; i < faces.length; i = i + 4) {
System.out.printf(
"face %d : x %d, y %d, width %d, height %d\n",
faceCount, faces[i], faces[i + 1], faces[i + 2],
faces[i + 3]);
faceCount++;
}
}

//输出识别时间,ms
System.out.println("Detection time (ms): "
+ String.valueOf(System.currentTimeMillis() - t));
}

}


4.其他问题


a.在运行过程中可以出现这种提示:由于应用程序的配置不正确,应用程序未能启动,重新安装应用程序可能会纠正这个问题。而导致程序无法运行,原因就是系统没有安装VC9的运行库,下载并安装一个Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)就可以解决问题。

b.程序运行时报内错错误,错误信息大致为:
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x77dc2be9, pid=4244, tid=5644
#
# JRE version: 6.0_21-b06
# Java VM: Java HotSpot(TM) Client VM (17.0-b16 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [ntdll.dll+0x52be9]

原因就是在编译dll动态库的时候,运行时库选择错误,修改运行时库的方法为:右键选择项目点击"属性",选择"配置属性","C/C++","代码生成",右侧的"运行时库",Release版时应配置为"多线程DLL(/MD)",Debug版时应配置为"多线程调试DLL(/MDd)"


c.本程序只适用于32位版应用,64位版本需要调用64位版本的OpenCV。
[ad#Single page bottom]

评论

此博客中的热门博文

SSH代理使用说明

BuyVM黑掉了

PhotonVPS的Xen VPS性能