使用JNI进行Java与C/C++语言混合编程----在Java中调用C/C++生成的DLL动态链接库
JNI是Java Native Interface的英文缩写,
中文翻译为本地调用, 自从Java 1.1开始就成为了Java标准的一部分.
Java调用C/C++大概有这样几个步骤
编写带有native方法的Java类, 使用javac工具编译Java类
使用javah来生成与native方法对应的头文件
实现相应的头文件, 并编译为动态链接库(windows下是.dll, linux下是.so)
首先创建一个java工程,测试一下能不能调用openCV代码:
public class hello { //很多教程会叫我们声明成static,但是我用的时候报错了,所以我还是 //不声明成static了 public native void sayHello(String path); public static void main(String[] args) { // TODO Auto-generated method stub hello h=new hello(); //有时候会报错说cannot load xxx.dll //只需要将生成的xxx.dll放到这 //条语句输出的路径下即可 System.out.println(System.getProperty("java.library.path")); //这里调用的是java本地库,TestAgain不加.dll。 //怎样生成.dll后面会介绍 System.loadLibrary("TestAgain"); h.sayHello("D:/images/5.jpg"); }
}
由于我用的是myeclipse编写和编译的Java程序,
只需要配置一下就可以用myeclipse生成xxx.h文件,
在myclipse->run->External Tools->External Tools configurations
一般需要选中一个工程才能run,然后就可以生成xxx.h头文件了
working Directory:${project_loc}
arguments:-v -classpath "${project_loc}/src" -d "${project_loc}/jni"
${java_type_name}
制作.h文件还有一个更简单的办法,那就是直接cmd到当前工程的src目录下,然后javah xxx(包名).xxx(类名),然后就可以了,举例假设包名为com.action,类名为upLoad,而且src目录下
则应该是javah com.action.upLoad即可
这里我也有些地方没搞懂,因为我自己做这个项目的时候,用过jdk 1.6 1.7 1.8,最后使用的jdk1.6+tomcat6.0开发的,但是当我在location 中写入jdk1.6 的bin/javah.exe,编译我的javaweb工程时出现如下错误:
���������� com.servlet.HandleImg δ�ҵ� com.servlet.HandleImg �����ļ� javadoc: ���� - �Ҳ����� com.servlet.HandleImg�� [ Search Path: D:jdkjdkjrelib esources.jar;D:jdkjdkjrelib t.jar;D:jdkjdkjrelibsunrsasign.jar;D:jdkjdkjrelibjsse.jar;D:jdkjdkjrelibjce.jar;D:jdkjdkjrelibcharsets.jar;D:jdkjdkjrelibmodulesjdk.boot.jar;D:jdkjdkjreclassesD:MyEclipseProfessional2014workspaceHandleImage/src ] Error: δ����������ָ���κ��ࡣ�볢��ʹ�� -help��
因此我用的是我jdk1.7中bin/javah.exe生成的xxx.h头文件,实在不行就只能下载两个jdk了,知识水平有限,望有人能解决这个问题。
不管怎么说,最终头文件是生成了,xxx.h文件在当前工程jni文件夹下,如下图:
打开后
然后就可以开始写c++代码了
file->new ->project 选择visual C++ 和win32 Console Application然后ok
我的操作系统和myeclipse都是64位的,因此生成32位的dll,在myeclipse中调用,会出现错误,这里就需要配一下
然后就是配置opencv环境,我用的是opencv2.4.4,具体的配置过程,去百度opencv2.4.4配置教程,这次是在debug|x64下添加propertySheet了,
继续添加两个路径:C:Program FilesJavajdk1.7.0_80include和C:Program FilesJavajdk1.7.0_80includewin32,目的是为了引入三个头文件jni.h和jni_md.h还有jawt_md.h
再就是右键工程 ->open folder in file explore 将上面生成的xxx.h文件copy到该文件夹下:
然后HeaderFile里面也放一份
第二种引入改头文件的方法是直接新建一个头文件Test.h(名字可以随意取),然后将myEclipse中生成的头文件的内容全部复制到Test.h中,然后在引用的时候将#include "com_Test_hello.h"改为#include "Test.h"即可
然后在source Files中新建TestAgain.cpp,代码如下,
#include "com_Test_hello.h" #include#include #include #include #include using namespace std; using namespace cv; JNIEXPORT void JNICALL Java_com_Test_hello_sayHello (JNIEnv *env, jobject obj,jstring string){ const char* str = env->GetStringUTFChars(string, 0); char cap[128]; strcpy(cap, str); env->ReleaseStringUTFChars(string, 0); int len=strlen(cap); Mat imag, result; imag = imread(str,0); //将读入的彩色图像直接以灰度图像读入 //namedWindow("原图", 1); // imshow("原图", imag); result = imag.clone(); //进行二值化处理,选择30,200.0为阈值 threshold(imag, result, 30, 200.0, CV_THRESH_BINARY); //namedWindow("二值化图像"); // imshow("二值化图像", result); imwrite(str,result); printf("图像二值化成功!"); }
然后就可以build->build solution
如何使用Debug生成的解决方案的话,上面的那些环境就应该是在Debug下属性表里面添加,而且生成的dll也是在Debug文件夹目录下。
要是想用Release生成的话,同上步骤即可。
构建成功后可以在这里找到
将.dll文件复制到之前Java代码中System.out.println(System.getProperty("java.library.path"));这条语句输出的路径中任何一个文件夹下即可
最后回到myeclipse中运行Java程序,就可以了
完成了这个调用后,就只需要将这个嵌入到javaweb工程中即可
有些时候调用dll需要返回int数组,或者字符串,我再分享这两个方法
返回一个int数组
生成的头文件用javah生成.h头文件
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class Test_floatArray */ #ifndef _Included_Test_floatArray #define _Included_Test_floatArray #ifdef __cplusplus extern "C" { #endif /* * Class: Test_floatArray * Method: haha * Signature: ()[I */ JNIEXPORT jintArray JNICALL Java_Test_floatArray_haha (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
#include "Test.h" #includeJNIEXPORT jintArray JNICALL Java_Test_floatArray_haha (JNIEnv *env, jobject obj){ //1.新建长度len数组 jintArray jarr = env->NewIntArray(3); //2.获取数组指针 jint *arr = env->GetIntArrayElements(jarr, NULL); //3.赋值 int i = 0; for (; i < 3; i++){ arr[i] = i; } //4.释放资源 env->ReleaseIntArrayElements(jarr, arr, 0); //5.返回数组 return jarr; }
返回一个字符串型数组,首先需要用一个stojstring()函数,将c++的string类型转换为const char 类型,再从const char 转化为jstring类型,即可直接返回一个jstring类型
#include "test.h" #include#include using namespace std; jstring stoJstring(JNIEnv* env, const char* pat) { jclass strClass = env->FindClass("Ljava/lang/String;"); jmethodID ctorID = env->GetMethodID(strClass, " ", "([BLjava/lang/String;)V"); jbyteArray bytes = env->NewByteArray(strlen(pat)); env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat); jstring encoding = env->NewStringUTF("utf-8"); return (jstring)env->NewObject(strClass, ctorID, bytes, encoding); } JNIEXPORT jstring JNICALL Java_com_action_upLoadAction_shibie (JNIEnv *env, jobject obj, jstring path1, jstring path2, jstring path3){ const char* str1 = env->GetStringUTFChars(path1, 0); const char* str2 = env->GetStringUTFChars(path2, 0); const char* str3 = env->GetStringUTFChars(path3, 0); printf("%s ", str1); printf("%s ", str2); printf("%s ", str3); printf("你好 "); string str = "好#"; const char* chardata = str.c_str(); jstring jstr = stoJstring(env, chardata); return jstr; }
test.h文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class com_action_upLoadAction */ #ifndef _Included_com_action_upLoadAction #define _Included_com_action_upLoadAction #ifdef __cplusplus extern "C" { #endif /* * Class: com_action_upLoadAction * Method: Contrast * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT void JNICALL Java_com_action_upLoadAction_Contrast (JNIEnv , jobject , jstring , jstring, jstring); JNIEXPORT jstring JNICALL Java_com_action_upLoadAction_shibie (JNIEnv *, jobject, jstring, jstring, jstring); #ifdef __cplusplus } #endif #endif
关于java web,推荐去看我的另一篇文章:https://segmentfault.com/a/11...
然后关于有些方法需要返回某个参数如String,boolean等,可以参考这篇文章:
http://www.cnblogs.com/icejoy...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70389.html
摘要:可惜收费的,今天要介绍的完美验证码识别系统是类似的免费产品。调用函数相当简单的,对比复杂的参数,这个识别是相当的快捷。 此文已由作者徐迪授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 讲到验证码识别,大家第一个可能想到tesseract。诚然,对于OCR而言,tesseract确实很强大,自带的字模能识别绝大多数规整的中英文。但是验证码毕竟不是OCR。对于现在...
摘要:这是坐标百度,好像没啥好研究的了,不过出于好奇还是想知道使用是如何做到把文字区域进行框选的,所以接下来我们就看看如何在上使用实现图片中的文字框选。一些探索 最近下了几个OCR的App(比如白描),发现可以选中图片中的文字行逐行转成文字,觉得很有意思(当然想用要花钱啦),想着自己研究一下实现原理,google之后,发现了两个库,一个是OpenCV,在机器视觉方面应用广泛,图像分析必备利器。另一...
阅读 785·2023-04-25 21:21
阅读 3184·2021-11-24 09:39
阅读 3043·2021-09-02 15:41
阅读 1966·2021-08-26 14:13
阅读 1776·2019-08-30 11:18
阅读 2737·2019-08-29 16:25
阅读 492·2019-08-28 18:27
阅读 1543·2019-08-28 18:17