项目背景:某项目中需要Java调用底层C++写的算法,底层算法用的是opencv,算法那边提供的接口是传递一个opencv封装的Mat对象,返回的也是处理后的Mat对象,还有一些基本的配置参数,准备直接传图片路径,C++去读取图片转化为Mat,但是考虑到读取IO效率问题,最后决定Java传字节数组,C++接口做预处理转化为Mat对象,解析算法得到处理后的Mat对象,最后转化为base64返回给java后台,最后通过websocket把处理后的图片实时显示到前端页面*
java调用C++实例程序可以参考我上一篇博客
https://blog.csdn.net/kuaidi2883/article/details/107161039
1.Java类
package com.ainnovation.ai.business.jni;
import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Decoder;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
public class Surface {
//初始参数
private int x0;
private int y0;
private int x1;
private int y1;
private int ignore_x;
private int ignore_y;
private String result;
public native String excute(byte[] image);
static {
System.load("D:\\C#Projects\\JniDemo\\JniDemo\\x64\\Debug\\JniDemo.dll");
}
static BASE64Decoder decoder = new sun.misc.BASE64Decoder();
public static void main(String[] args) throws IOException {
Surface surface = new Surface();
surface.x0 = 0;
surface.y0 = 300;
surface.x1 = 1000;
surface.y1 = 900;
surface.ignore_x = 150;
surface.ignore_y = 800;
//1.获取图片字节流为从相机获取 这里方便演示直接读取本地图片
File file = new File("C:\\Users\\Admin\\Desktop\\test.png");
InputStream in = new FileInputStream(file);
byte[] image = IOUtils.toByteArray(in);
//2.运行dll动态链接库
String base64Img = surface.excute(image);
//3.获取图片base64直接通过websocket推送给前端实时显示处理结果,这里为演示保存图片
byte[] bytes1 = decoder.decodeBuffer(base64Img);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
BufferedImage bi1 = ImageIO.read(bais);
File f1 = new File("d://out1.jpg");
ImageIO.write(bi1, "jpg", f1);
}
}
C++实现代码:
#include "com_ainnovation_ai_business_jni_Surface.h"
#include <iostream>
#include "cv.h"
#include "highgui.h"
#include "surface.h"
using namespace std;
//base64编码
std::string base64Encode(const unsigned char* Data, int DataByte) {
//编码表
const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//返回值
std::string strEncode;
unsigned char Tmp[4] = { 0 };
int LineLength = 0;
for (int i = 0; i < (int)(DataByte / 3); i++) {
Tmp[1] = *Data++;
Tmp[2] = *Data++;
Tmp[3] = *Data++;
strEncode += EncodeTable[Tmp[1] >> 2];
strEncode += EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
strEncode += EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
strEncode += EncodeTable[Tmp[3] & 0x3F];
if (LineLength += 4, LineLength == 76) { strEncode += "\r\n"; LineLength = 0; }
}
//对剩余数据进行编码
int Mod = DataByte % 3;
if (Mod == 1) {
Tmp[1] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4)];
strEncode += "==";
}
else if (Mod == 2) {
Tmp[1] = *Data++;
Tmp[2] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
strEncode += EncodeTable[((Tmp[2] & 0x0F) << 2)];
strEncode += "=";
}
return strEncode;
}
//imgType 包括png bmp jpg jpeg等opencv能够进行编码解码的文件
std::string Mat2Base64(const cv::Mat& img, std::string imgType) {
//Mat转base64
std::string img_data;
std::vector<uchar> vecImg;
std::vector<int> vecCompression_params;
vecCompression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
vecCompression_params.push_back(90);
imgType = "." + imgType;
cv::imencode(imgType, img, vecImg, vecCompression_params);
img_data = base64Encode1(vecImg.data(), vecImg.size());
return img_data;
}
JNIEXPORT jstring JNICALL Java_com_ainnovation_ai_business_jni_Surface_excute
(JNIEnv *env, jobject obj, jbyteArray jbyteArra)
{
try
{
//1.获取java端传来的jbyteArray转化为opencv Mat
//复制java数组到C++
jsize len = env->GetArrayLength(jbyteArra);
signed char* pData = new signed char[len];
env->GetByteArrayRegion(jbyteArra, 0, len, pData);
//! 解码内存数据,变成cv::Mat数据
cv::Mat img_decode;
vector<uchar> data;
for (int i = 0; i < len; ++i) {
data.push_back(pData[i]);
}
img_decode = cv::imdecode(data, IMREAD_COLOR);
//2.获取java端的成员变量数据x0,x1,y0,y1 传入算法获取运行结果
jclass jclazz = env->GetObjectClass(obj); //注释(1)
jfieldID x0_id = env->GetFieldID(jclazz, "x0", "I");//注释(2)
jint x0 = env->GetIntField(obj, x0_id);
jfieldID y0_id = env->GetFieldID(jclazz, "y0", "I");//注释(2)
jint y0 = env->GetIntField(obj, y0_id);
jfieldID x1_id = env->GetFieldID(jclazz, "x1", "I");//注释(2)
jint x1 = env->GetIntField(obj, x1_id);
jfieldID y1_id = env->GetFieldID(jclazz, "y1", "I");//注释(2)
jint y1 = env->GetIntField(obj, y1_id);
jfieldID ignore_x_id = env->GetFieldID(jclazz, "ignore_x", "I");//注释(2)
jint ignore_x = env->GetIntField(obj, ignore_x_id);
jfieldID ignore_y_id = env->GetFieldID(jclazz, "ignore_y", "I");//注释(2)
jint ignore_y = env->GetIntField(obj, ignore_y_id);
vector<int> cut_box = { x0,y0,x1,y1}; // {x0, y0, x1, y1}
vector<int> ignore_area = { ignore_x,ignore_y};
//3.调用底层算法将返回的mat类型转化为base64并返回给java服务器端
Mat image = surface(img_decode, cut_box, ignore_area);
std::string str = Mat2Base641(image, "jpg");
char* data1;
int len1 = str.length();
data1 = (char*)malloc((len1 + 1) * sizeof(char));
str.copy(data1, len1, 0);
jstring rtstr = env->NewStringUTF(data1);
//4.获取java端成员变量 并赋值最后运行结果
jfieldID result_id = env->GetFieldID(jclazz, "result", "Ljava/lang/String;");
env->SetObjectField(obj, result_id, env->NewStringUTF("ng"));
return rtstr;
}
catch (exception e) {
cerr << "发生错误:" << e.what();
}
}
运行结果
D盘下面生成了处理后的图片