资讯专栏INFORMATION COLUMN

JavaWeb开发利用JNI调用C++代码生成的dll进行图像二值化处理并保存

cheukyin / 3384人阅读

使用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"
#include 
JNIEXPORT 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

相关文章

  • windows下简单验证码识别——完美验证码识别系统

    摘要:可惜收费的,今天要介绍的完美验证码识别系统是类似的免费产品。调用函数相当简单的,对比复杂的参数,这个识别是相当的快捷。 此文已由作者徐迪授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 讲到验证码识别,大家第一个可能想到tesseract。诚然,对于OCR而言,tesseract确实很强大,自带的字模能识别绝大多数规整的中英文。但是验证码毕竟不是OCR。对于现在...

    shleyZ 评论0 收藏0
  • iOS利用OpenCV 实现文字行区域提取尝试

    摘要:这是坐标百度,好像没啥好研究的了,不过出于好奇还是想知道使用是如何做到把文字区域进行框选的,所以接下来我们就看看如何在上使用实现图片中的文字框选。一些探索 最近下了几个OCR的App(比如白描),发现可以选中图片中的文字行逐行转成文字,觉得很有意思(当然想用要花钱啦),想着自己研究一下实现原理,google之后,发现了两个库,一个是OpenCV,在机器视觉方面应用广泛,图像分析必备利器。另一...

    番茄西红柿 评论0 收藏0

发表评论

0条评论

cheukyin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<