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 两个模型为例子,实现模型离线部署和调用。
MDPI公司福利 431人发布