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和依赖包全部放入文件夹中。

突然自闭了,后面再更

以部署 U2Net 和 U2Net_portrait 两个模型为例子,实现模型离线部署和调用。

全部评论

相关推荐

刘湘_passion:出国旅游?那就小心你的腰子咯
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务