Java 调用模型(JNI)
前面我们已经叙述了C++调用模型的板块,现在我们将学习如何使用Java进行调用C++代码。
JNI 开发
JNI 全称是 Java Native Interface(Java 本地接口)单词首字母的缩写,本地接口就是指用 C 和 C++ 开发的接口。由于 JNI 是 JVM 规范中的一部份,因此可以将我们写的 JNI 程序在任何实现了 JNI 规范的 Java 虚拟机中运行。同时,这个特性使我们可以复用以前用 C/C++ 写的大量代码。
JNI使用大致流程
- 编写声明了 native 方法的 Java 类
- 将 Java 源代码编译成 class 字节码文件
- 用 javah -jni 命令生成.h头文件(javah 是 jdk 自带的一个命令,-jni 参数表示将 class 中用native 声明的函数生成 JNI 规则的函数)
- 用本地代码实现.h头文件中的函数
- 将本地代码编译成动态库(Windows:*.dll,linux/unix:*.so,mac os x:*.jnilib)
- 拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序
详细演示过程
推荐一个简单的demo操作教程博客
https://blog.csdn.net/luzaijiaoxia0618/article/details/99685747
编写声明了 native 方法的 Java 类
package java_dll; import java.io.File; public class javaClass { /** * 图像主体分割 * @param inputDir 输入图片文件夹路径 * @param outputDir 输入图片文件夹路径 * @param path C++ 的运行环境路径 */ public static native void SOD_func(String inputDir ,String outputDir,String path ); }
用 javah -jni 命令生成.h头文件
这里我们用bat 的方式进行生成。
在java_dll同级文件夹创建一个h.bat。
点击bat运行可以得到.h 文件
用本地代码实现.h头文件中的函数 ( VS2017 )
配置环境
选择 Release x64 运行环境 常规----> 配置类型(动态库.dll) MFC的使用(在静态库中使用MFC) 字符集 (使用Unicode字符集) vc++目录--->包含目录 ( D:\baiDuYunPan\bushu\opencv\build\include D:\baiDuYunPan\bushu\opencv\build\include\opencv2 ) 库目录 ( D:\baiDuYunPan\bushu\opencv\build\x64\vc15\lib ) C/C++ ---> 附加包含目录 ( C:\Program Files\Java\jdk1.8.0_60\include C:\Program Files\Java\jdk1.8.0_60\include\win32 ) 链接器 ----> 输入-->附加依赖项( D:\baiDuYunPan\bushu\opencv\build\x64\vc15\lib\opencv_world3413.lib ) 命令行 ( /FORCE:MULTIPLE ) (可能会出现一个或多个重定义的报错需要设置) 配置说明: 常规配置的是指定项目生成 .dll 动态库文件,使用MFC的一些库函数。 VC++目录 配置好 opencv 的.h 头文件信息和.lib 库信息 C/C++ 附加包含目录 配置好jni的jni.h。 链接器的输入配置opencv 的库文件。
编写封装好头文件deploy.h
#pragma once #include <opencv2/dnn.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <fstream> #include <SDKDDKVer.h> #include <iostream> #include <cstdlib> #include <cstdio> #include<string> #include<direct.h> #include<afxcoll.h> #include<string> using namespace cv; using namespace std; //float b[307200]; float b[786432]; vector<string> split(string str, string pattern) { string::size_type pos; vector<string> result; str += pattern; int size = str.size(); for (int i = 0; i < size; i++) { pos = str.find(pattern, i); if (pos < size) { std::string s = str.substr(i, pos - i); result.push_back(s); i = pos + pattern.size() - 1; } } return result; } void SOD_func( string s, string outputSrc ) { //cout << "Please input the image directory(e.g. D:/image): "; vector<String> files; glob(s, files, true); cout << "正在执行SOD..." << endl; String modelFile = "u2netp.onnx"; dnn::Net net = cv::dnn::readNetFromONNX(modelFile); cout << "载入模型成功..." << endl; for (int i = 0; i < files.size(); i++) { string imageFile = files[i]; Mat image = imread(files[i]); cv::cvtColor(image, image, cv::COLOR_BGR2RGB); int imrows = image.rows, imcols = image.cols; Mat img; image.convertTo(img, CV_32F, 1.0 / 255.0); resize(img, image, cv::Size(320, 320)); float maxx = -1; for (int i = 0; i < 320; i++) { Vec3f* p = image.ptr<Vec3f>(i); for (int j = 0; j < 320; j++) { Vec3f& pix = *p++; maxx = max(maxx, pix[0]); maxx = max(maxx, pix[1]); maxx = max(maxx, pix[2]); } } for (int i = 0; i < 320; i++) { Vec3f* p = image.ptr<Vec3f>(i); for (int j = 0; j < 320; j++) { Vec3f& pix = *p++; pix[0] /= maxx; pix[1] /= maxx; pix[2] /= maxx; } } for (int i = 0; i < 320; i++) { Vec3f* p = image.ptr<Vec3f>(i); for (int j = 0; j < 320; j++) { Vec3f& pix = *p++; pix[0] = (pix[0] - 0.485) / 0.229; pix[1] = (pix[1] - 0.456) / 0.224; pix[2] = (pix[2] - 0.406) / 0.225; } } int tot = 0; for (int i = 0; i < 320; i++) { Vec3f* p = image.ptr<Vec3f>(i); for (int j = 0; j < 320; j++) { Vec3f& pix = *p++; b[tot++] = pix[0]; } } for (int i = 0; i < 320; i++) { Vec3f* p = image.ptr<Vec3f>(i); for (int j = 0; j < 320; j++) { Vec3f& pix = *p++; b[tot++] = pix[1]; } } for (int i = 0; i < 320; i++) { Vec3f* p = image.ptr<Vec3f>(i); for (int j = 0; j < 320; j++) { Vec3f& pix = *p++; b[tot++] = pix[2]; } } tot = 0; for (int i = 0; i < 320; i++) { Vec3f* p = image.ptr<Vec3f>(i); for (int j = 0; j < 320; j++) { Vec3f& pix = *p++; pix[0] = b[tot++]; pix[1] = b[tot++]; pix[2] = b[tot++]; } } int sizes[] = { 1, 3, 320, 320 }; Mat img_input = Mat(4, sizes, CV_32F, image.data); cout << "输入图片处理成功..." << endl; net.setInput(img_input); Mat result = net.forward(); cv::Mat a(cv::Size(320, 320), CV_32F); for (int x = 0; x < 1; x++) { for (int y = 0; y < 1; y++) { for (int i = 0; i < 320; i++) { float* iptr = a.ptr<float>(i); for (int j = 0; j < 320; j++) { int id = result.step[0] * x + result.step[1] * y + result.step[2] * i + j * result.step[3]; float* p = (float*)(result.data + id); iptr[j] = (float)(*p); } } } } maxx = -10000; float minx = 10000; for (int i = 0; i < 320; i++) { for (int j = 0; j < 320; j++) { float xx = a.at<float>(i, j); //a.at<float>(i, j) = 1 - xx; maxx = max(maxx, xx); minx = min(minx, xx); } } cv::Mat res_img(cv::Size(320, 320), CV_8U); for (int i = 0; i < 320; i++) { for (int j = 0; j < 320; j++) { a.at<float>(i, j) = (a.at<float>(i, j) - minx) / (maxx - minx); res_img.at<uchar>(i, j) = (uchar)(a.at<float>(i, j) * 255); } } cout << "输出图片处理成功..." << endl; //获取图片名 for (int j = 0; j < imageFile.size(); j++) { if (imageFile[j] == '/') imageFile[j] = '\\'; } string tmpstring = "\\"; vector<string> vs = split(imageFile, tmpstring); string filename = vs[vs.size() - 1]; vector<string> name = split(filename, "."); filename = name[0]; cv::Mat src, dst, img_rgb; //cv::imread("1-mask.png", CV_LOAD_IMAGE_GRAYSCALE); resize(res_img, dst, cv::Size(imcols, imrows)); //threshold(dst, dst, 230, 255, CV_THRESH_BINARY); //imwrite(outputSrc + "/" + filename + ".png", dst); bitwise_not(dst, img); cvtColor(img, img_rgb, COLOR_GRAY2RGB); Mat ori_img_rgb = imread(imageFile); add(ori_img_rgb, img_rgb, dst); //imwrite("result/" + filename + ".png", dst); imwrite(outputSrc + "/" + filename + ".png", dst); cout << imageFile << endl; } //system("pause"); }
项目结构
javaDll.cpp
#include"java_dll_javaClass.h" #include"deploy.h" /* setting working path easy to reference dynamic library */ void setEnvPath(string s) { CString strDirPath = s.c_str(); SetCurrentDirectory(strDirPath); } /* function : jstring convert string */ std::string jstring2str(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); std::string stemp(rtn); free(rtn); return stemp; } JNIEXPORT void JNICALL Java_java_1dll_javaClass_SOD_1func (JNIEnv *env, jclass, jstring var1, jstring var2, jstring path) { std::string s1 = jstring2str(env, var1); std::string s2 = jstring2str(env, var2); std::string s3 = jstring2str(env, path); setEnvPath(s3); SOD_func(s1, s2); }
点击重新生成解决方案
下图表示生成成功
然后我们找到所在文件夹,拷贝dll 和 所有依赖的动态库和依赖文件到Java项目的根目录。
在javaClass类中引入dll并测试。
为了将这么多库文件进行规划存储,我们建立一个sources文件夹作为dll的工作空间路径,将dll和依赖包全部放入文件夹中。
突然自闭了,后面再更
网络模型工程化专题( VC++ 2017 ) 文章被收录于专栏
以部署 U2Net 和 U2Net_portrait 两个模型为例子,实现模型离线部署和调用。