Java调用C++动态库DLL实现图像处理
作者:颇有几分姿色
前言
相关:Java 调用 C++ 动态库(DLL)完整实践:无图像无实体处理场景
这里主要介绍如何通过 JNI(Java Native Interface)在 Java 中调用一个用 C++ 编写的校色算法库。不涉及图像和 java 实体处理。环境如下:
- 系统:Windows 10
- JDK:1.8
- IDE:Visual Studio 2022
- Java 构建工具:Maven
- 目标平台:x64
一、整体目标
Java 端通过 JNI 调用算法提供的动态库,实现图像缺色偏色检测功能。为了方便,我把所有的依赖库都放在了 jdk 的 bin 目录,这样我在调用的时候只需要导入我的自己生成的 jni 库就可以,如果需要频繁切换 jdk,那建议自己配置 java.library.path
或将依赖库放到独立目录中 ,处理好动态库的依赖关系即可。
二、准备工作
1. C++ 头文件(API 定义)
这是算法的头文件,jni 层需要根据他的方法调用即可。建议和 java 的方法保持一致,不保持一致也可以,自己在 jni 层处理好就可以。
头文件方法定义:
// 版本号 COLORCAST_API int ColorCastGetVersion(char* pOutBuf, int nOutBufSize); COLORCAST_API ColorCastResults analyzeImageColorCast( unsigned char* pData, int pw, int ph, int pchannels, double skin_ratio_threshold = 0.1, double C_threshold = 20.0, double Red_threshold = 18.0, double Green_threshold = 5.0, double Yellow_threshold = 30.0, double Blue_threshold = 5.0, double face_threshold = 0.5); COLORCAST_API ContrastColorCastResults contrastImageColorCast( unsigned char* tData, int tw, int th, int tchannels, unsigned char* pData, int pw, int ph, int pchannels, int YCrCb_cbmax = 125, int ROIwidthDiv = 6, int ROIheightDiv = 100, double USwidthRatio = 0.5, double USheightRatio = 0.01 );
结构头文件:
// 偏色对比结果(双图比对) struct ContrastColorCastResults { // 红色比对色偏指数 double Red_Contrast = 999; // 绿色比对色偏指数 double Green_Contrast = 999; // 黄色比对色偏指数 double Yellow_Contrast = 999; // 蓝色比对色偏指数 double Blue_Contrast = 999; // ROI偏色占比 double ColorCast_ratio = 999; };
2. Java 接口类定义
这里示例图像的三种传输方式:
- Java 层将图像转换为 BGR 格式的 byte[],直接传给 JNI;
- Java 层传原始图像数据,JNI 使用 OpenCV 解码;
- Java 层传递文件路径,JNI 自行读取文件内容。
package com.emp.empxmrz.util; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /*** * @title * @author shijiangyong * @date 2025/8/27 11:23 **/ public class ColorCastDetExampleJni { static { System.loadLibrary("ColorCastDetExampleJni"); } // 获取版本号 public static native int getVersion(byte[] buffer, int bufSize); // 对比图偏色检测 // 传图片数据,处理成BGR格式 public static native ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCast( byte[] tData, int tw, int th, int tchannels, byte[] photoData, int pw, int ph, int pchannels, int yCrCbCbmax, int roiWidthDiv, int roiHeightDiv, double usWidthRatio, double usHeightRatio ); // 传原图片数据,不转格式 public static native ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastOri( byte[] tData, byte[] photoData, int yCrCbCbmax, int roiWidthDiv, int roiHeightDiv, double usWidthRatio, double usHeightRatio ); // 传图片本地路径 public static native ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastByPath( String tData, String photoData, int yCrCbCbmax, int roiWidthDiv, int roiHeightDiv, double usWidthRatio, double usHeightRatio ); @Data public static class ContrastColorCastResults { @Schema(description = "红色比对偏色指数") public double redContrast; @Schema(description = "绿色比对偏色指数") public double greenContrast; @Schema(description = "黄色比对偏色指数") public double yellowContrast; @Schema(description = "蓝色比对偏色指数") public double blueContrast; @Schema(description = "ROI区域偏色占比") public double colorCastRatio; } }
三、生成 JNI 头文件
javah -classpath target/classes -d src/main/jni com.emp.empxmrz.util.ColorCastDetExampleJni
这会生成 com_emp_empxmrz_util_ColorCastDetExampleJni.h 和 com_emp_empxmrz_util_ColorCastDetExampleJni_ContrastColorCastResults.h
,它定义了 JNI 接口供 C++ 实现。生成之后不能随便移动类的位置或修改包名、类名等,如果必须调整的话,需要重新生成。
生成的头文件大概如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_emp_empxmrz_util_ColorCastDetExampleJni */ #ifndef _Included_com_emp_empxmrz_util_ColorCastDetExampleJni #define _Included_com_emp_empxmrz_util_ColorCastDetExampleJni #ifdef __cplusplus extern "C" { #endif /* * Class: com_emp_empxmrz_util_ColorCastDetExampleJni * Method: getVersion * Signature: ([BI)I */ JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_getVersion (JNIEnv *, jclass, jbyteArray, jint); /* * Class: com_emp_empxmrz_util_ColorCastDetExampleJni * Method: contrastImageColorCast * Signature: ([BIII[BIIIIIIDD)Lcom/emp/empxmrz/util/ColorCastDetExampleJni/ContrastColorCastResults; */ JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCast (JNIEnv *, jclass, jbyteArray, jint, jint, jint, jbyteArray, jint, jint, jint, jint, jint, jint, jdouble, jdouble); /* * Class: com_emp_empxmrz_util_ColorCastDetExampleJni * Method: contrastImageColorCastOri * Signature: ([B[BIIIDD)Lcom/emp/empxmrz/util/ColorCastDetExampleJni/ContrastColorCastResults; */ JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastOri (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint, jint, jdouble, jdouble); /* * Class: com_emp_empxmrz_util_ColorCastDetExampleJni * Method: contrastImageColorCastByPath * Signature: (Ljava/lang/String;Ljava/lang/String;IIIDD)Lcom/emp/empxmrz/util/ColorCastDetExampleJni/ContrastColorCastResults; */ JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastByPath (JNIEnv *, jclass, jstring, jstring, jint, jint, jint, jdouble, jdouble); #ifdef __cplusplus } #endif #endif
四、JNI 实现(C++)
这是 jni 的头文件实现,因为我是要调用多个算法,所以使用 vs 创建了一个解决方案,里面创建了多个项目,每个项目都是一种算法的 jni 层,根据自己的实际情况操作就可以,实现类中需要包含算法头文件和 jni 头文件,java实体的头文件不用添加。项目结构大致如下:
cpp 完整代码如下:
#include "ColorCast.h" #include "com_emp_empxmrz_util_ColorCastDetExampleJni.h" #include <iostream> #include <stdexcept> #include <string> #include <opencv2/core.hpp> #include <opencv2/imgcodecs.hpp> void native_log(const std::string& message) { std::cerr << "[NativeLog] " << message << std::endl; } void throwJavaException(JNIEnv* env, const char* message) { jclass exceptionCls = env->FindClass("java/lang/RuntimeException"); if (exceptionCls != nullptr) { env->ThrowNew(exceptionCls, message); } } void throwIllegalArgument(JNIEnv* env, const char* message) { jclass exClass = env->FindClass("java/lang/IllegalArgumentException"); if (exClass != nullptr) { env->ThrowNew(exClass, message); } } JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_getVersion(JNIEnv* env, jobject obj, jbyteArray buffer, jint bufSize) { try { jboolean isCopy = JNI_FALSE; jbyte* bufPtr = env->GetByteArrayElements(buffer, &isCopy); if (bufPtr == nullptr) { throwJavaException(env, "Failed to get buffer pointer"); return 0; } // 写入版本号 int len = ColorCastGetVersion(reinterpret_cast<char*>(bufPtr), static_cast<int>(bufSize)); // 写成功,手动同步数据回 Java env->ReleaseByteArrayElements(buffer, bufPtr, 0); return len; } catch (const std::exception& e) { native_log(e.what()); throwJavaException(env, e.what()); return 0; } } // 构造 ContrastColorCastResults Java 对象 jobject createContrastColorCastResults(JNIEnv* env, const ContrastColorCastResults& result) { jclass cls = env->FindClass("com/emp/empxmrz/util/ColorCastDetJni$ContrastColorCastResults"); if (cls == nullptr) { throwJavaException(env, "Failed to find ContrastColorCastResults class"); return nullptr; } jmethodID constructor = env->GetMethodID(cls, "<init>", "()V"); if (constructor == nullptr) { throwJavaException(env, "Failed to get constructor for ContrastColorCastResults"); return nullptr; } jobject obj = env->NewObject(cls, constructor); env->SetDoubleField(obj, env->GetFieldID(cls, "redContrast", "D"), result.Red_Contrast); env->SetDoubleField(obj, env->GetFieldID(cls, "greenContrast", "D"), result.Green_Contrast); env->SetDoubleField(obj, env->GetFieldID(cls, "yellowContrast", "D"), result.Yellow_Contrast); env->SetDoubleField(obj, env->GetFieldID(cls, "blueContrast", "D"), result.Blue_Contrast); env->SetDoubleField(obj, env->GetFieldID(cls, "colorCastRatio", "D"), result.ColorCast_ratio); return obj; } JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCast (JNIEnv* env, jobject obj, jbyteArray templateData, jint tw, jint th, jint tchannels, jbyteArray photoData, jint pw, jint ph, jint pchannels, jint cbmax, jint roiWidthDiv, jint roiHeightDiv, jdouble usWidthRatio, jdouble usHeightRatio) { try { jbyte* tData = env->GetByteArrayElements(templateData, nullptr); jbyte* pData = env->GetByteArrayElements(photoData, nullptr); ContrastColorCastResults result = contrastImageColorCast( reinterpret_cast<unsigned char*>(tData), tw, th, tchannels, reinterpret_cast<unsigned char*>(pData), pw, ph, pchannels, cbmax, roiWidthDiv, roiHeightDiv, usWidthRatio, usHeightRatio ); env->ReleaseByteArrayElements(templateData, tData, 0); env->ReleaseByteArrayElements(photoData, pData, 0); // 将 ContrastColorCastResults 映射到 Java 对象 return createContrastColorCastResults(env, result); } catch (const std::exception& e) { native_log(e.what()); throwJavaException(env, e.what()); return nullptr; } } JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastOri (JNIEnv* env, jobject obj, jbyteArray templateData, jbyteArray photoData, jint cbmax, jint roiWidthDiv, jint roiHeightDiv, jdouble usWidthRatio, jdouble usHeightRatio) { try { if (templateData == nullptr) { throwIllegalArgument(env,"templateData is null"); return nullptr; } if (photoData == nullptr) { throwIllegalArgument(env, "photoData is null"); return nullptr; } jsize tempLen = env->GetArrayLength(templateData); if (tempLen <= 0) { throwIllegalArgument(env, "templateData is null"); return nullptr; } jsize photoLen = env->GetArrayLength(photoData); if (photoLen <= 0) { throwIllegalArgument(env, "photoData is null"); return nullptr; } // Zero-copy: 获取 JVM 中的数据指针(映射) jbyte* tData = env->GetByteArrayElements(templateData, nullptr); if (tData == nullptr) { throwJavaException(env, "templateData Failed to get byte array elements"); return nullptr; } // Zero-copy: 获取 JVM 中的数据指针(映射) jbyte* pData = env->GetByteArrayElements(photoData, nullptr); if (pData == nullptr) { throwJavaException(env, "photoData Failed to get byte array elements"); return nullptr; } // 原图 // 构造一个 OpenCV Mat 来包装 byte[] 数据(不复制) cv::Mat tBuf(1, tempLen, CV_8UC1, reinterpret_cast<uchar*>(tData)); // 解码为图像 cv::Mat tImage = cv::imdecode(tBuf, cv::IMREAD_UNCHANGED); if (tImage.empty()) { env->ReleaseByteArrayElements(photoData, pData, JNI_ABORT); throwJavaException(env, "templateData Failed to decode image"); return nullptr; } // 目标图 cv::Mat pBuf(1, photoLen, CV_8UC1, reinterpret_cast<uchar*>(pData)); cv::Mat pImage = cv::imdecode(pBuf, cv::IMREAD_UNCHANGED); if (pImage.empty()) { env->ReleaseByteArrayElements(templateData, tData, JNI_ABORT); throwJavaException(env, "photoData Failed to decode image"); return nullptr; } ContrastColorCastResults result = contrastImageColorCast( reinterpret_cast<unsigned char*>(tImage.data), tImage.cols, tImage.rows, tImage.channels(), reinterpret_cast<unsigned char*>(pImage.data), pImage.cols, pImage.rows, pImage.channels(), cbmax, roiWidthDiv, roiHeightDiv, usWidthRatio, usHeightRatio ); // 释放映射,不拷贝回 Java(JNI_ABORT) env->ReleaseByteArrayElements(templateData, tData, JNI_ABORT); env->ReleaseByteArrayElements(photoData, pData, JNI_ABORT); // 将 ContrastColorCastResults 映射到 Java 对象 return createContrastColorCastResults(env, result); } catch (const std::exception& e) { native_log(e.what()); throwJavaException(env, e.what()); return nullptr; } } JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastByPath (JNIEnv* env, jobject obj, jstring templateData, jstring photoData, jint cbmax, jint roiWidthDiv, jint roiHeightDiv, jdouble usWidthRatio, jdouble usHeightRatio) { try { const char* tImagePath = env->GetStringUTFChars(templateData, nullptr); const char* pImagePath = env->GetStringUTFChars(photoData, nullptr); // 直接用 OpenCV 读取图像 cv::Mat tImage = cv::imread(tImagePath); if (tImage.empty()) { env->ReleaseStringUTFChars(templateData, tImagePath); // 释放资源 env->ReleaseStringUTFChars(photoData, pImagePath); throwJavaException(env, "Failed to load templateData"); return nullptr; } cv::Mat pImage = cv::imread(pImagePath); if (pImage.empty()) { env->ReleaseStringUTFChars(templateData, tImagePath); // 释放资源 env->ReleaseStringUTFChars(photoData, pImagePath); throwJavaException(env, "Failed to load photoData"); return nullptr; } ContrastColorCastResults result = contrastImageColorCast( reinterpret_cast<unsigned char*>(tImage.data), tImage.cols, tImage.rows, tImage.channels(), reinterpret_cast<unsigned char*>(pImage.data), pImage.cols, pImage.rows, pImage.channels(), cbmax, roiWidthDiv, roiHeightDiv, usWidthRatio, usHeightRatio ); env->ReleaseStringUTFChars(templateData, tImagePath); env->ReleaseStringUTFChars(photoData, pImagePath); // 将 ContrastColorCastResults 映射到 Java 对象 return createContrastColorCastResults(env, result); } catch (const std::exception& e) { native_log(e.what()); throwJavaException(env, e.what()); return nullptr; } }
建议添加异常处理,避免JVM 崩溃,注意资源的释放。
五、Visual Studio 配置
1. 添加包含目录
打开【项目属性】 > C/C++
> 常规
> 附加包含目录
:
防止编译阶段报错。
2. 添加库目录
打开【链接器】 > 常规
> 附加库目录
:
3. 添加依赖库
打开【链接器】 > 输入
> 附加依赖项
:
告诉编译器如何调用 .dll
中的函数; 出现 JNI 方法未导出错误,可添加 .def
文件显式指定导出符号 ,内容如下,其实就是 jni 头文件方法:
LIBRARY ColorCastDetExampleJni EXPORTS Java_com_emp_empxmrz_util_ColorCastDetExampleJni_getVersion Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCast Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastOri Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastByPath
4.生成 DLL
编译后会生成 ColorCastDetExampleJni.dll
和 ColorCastDetExampleJni.lib
,将他们复制到 jdk 的 bin 目录或者你自己定义的目录下, java 就可以直接调用 ColorCastDetExampleJni
了。
六、Java 调用测试
工具类 CustomImgUtils
和 ImageProcessor
在这篇文章里,Java 图像处理传 JNI 到 C++(OpenCV):两种高效实现方式对比
java 测试结果是否正确,也可以自己封装成接口。
service 层:
package com.emp.empxmrz.service; import com.emp.empxmrz.controller.vo.ColorCastDetExampleReq; import com.emp.empxmrz.util.ColorCastDetExampleJni; import com.emp.empxmrz.util.CustomImgUtils; import com.emp.empxmrz.util.ImageProcessor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; /*** * @title * @author shijiangyong * @date 2025/9/15 10:17 **/ @Slf4j @Service public class ColorCastDetExampleJniServiceImpl implements ColorCastDetExampleJniService{ @Override public String getVersion() { byte[] versionBuffer = new byte[12]; int result = ColorCastDetExampleJni.getVersion(versionBuffer, versionBuffer.length); String version = "获取版本号失败"; if (result == 0) { version = new String(versionBuffer, StandardCharsets.UTF_8).trim(); log.info("Algorithm model version number: {}", version); } else { log.warn("Failed to get version number: {} ", result); } return version; } @Override public ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCast(ColorCastDetExampleReq req) { MultipartFile sourceFile = req.getSourceFile(); if (sourceFile == null || sourceFile.isEmpty()) { throw new IllegalArgumentException("Source file is null or empty"); } MultipartFile targetFile = req.getTargetFile(); if (targetFile == null || targetFile.isEmpty()) { throw new IllegalArgumentException("Target file is null or empty"); } try { byte[] sourceBytes = ImageProcessor.inputStreamToByteArray(sourceFile.getInputStream()); BufferedImage sourceImage = ImageIO.read(new ByteArrayInputStream(sourceBytes)); if (sourceImage == null) { throw new RuntimeException("图像读取失败"); } byte[] targetBytes = ImageProcessor.inputStreamToByteArray(targetFile.getInputStream()); BufferedImage targetImage = ImageIO.read(new ByteArrayInputStream(targetBytes)); if (targetImage == null) { throw new RuntimeException("图像读取失败"); } byte[] sourceMatrixBGR = CustomImgUtils.getMatrixBGR(sourceImage); byte[] targetMatrixBGR = CustomImgUtils.getMatrixBGR(targetImage); return ColorCastDetExampleJni.contrastImageColorCast(sourceMatrixBGR, sourceImage.getWidth(), sourceImage.getHeight(), sourceImage.getColorModel().getNumColorComponents(), targetMatrixBGR, targetImage.getWidth(), targetImage.getHeight(), targetImage.getColorModel().getNumColorComponents(), req.getYCrCbCbmax(), req.getRoiWidthDiv(), req.getRoiHeightDiv(), req.getUsWidthRatio(), req.getUsHeightRatio()); } catch (IOException e) { throw new RuntimeException(e); } } @Override public ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastOri(ColorCastDetExampleReq req) { String sourceFileUrl = req.getSourceFileUrl(); if (sourceFileUrl == null || sourceFileUrl.isEmpty()) { throw new IllegalArgumentException("Source file url is null or empty"); } String targetFileUrl = req.getTargetFileUrl(); if (targetFileUrl == null || targetFileUrl.isEmpty()) { throw new IllegalArgumentException("Target file url is null or empty"); } try { byte[] sourceMatrixBGR = this.getImageBytes(sourceFileUrl); byte[] targetMatrixBGR = this.getImageBytes(targetFileUrl); return ColorCastDetExampleJni.contrastImageColorCastOri(sourceMatrixBGR, targetMatrixBGR, req.getYCrCbCbmax(), req.getRoiWidthDiv(), req.getRoiHeightDiv(), req.getUsWidthRatio(), req.getUsHeightRatio()); } catch (IOException e) { throw new RuntimeException(e); } } @Override public ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastByPath(ColorCastDetExampleReq req) { String sourceFileUrl = "E:\\test\\CertifHaed.jpg"; String targetFileUrl = "E:\\test\\face_big_homepage.jpg"; return ColorCastDetExampleJni.contrastImageColorCastByPath(sourceFileUrl,targetFileUrl,req.getYCrCbCbmax(), req.getRoiWidthDiv(), req.getRoiHeightDiv(), req.getUsWidthRatio(), req.getUsHeightRatio()); } /** * http流转BufferedImage * @param req * @return * @throws IOException */ private BufferedImage getBufferedImage(String req) throws IOException { URL url = new URL(req); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(10000); return ImageIO.read(conn.getInputStream()); } /** * http流转Byte[] * @param req * @return * @throws IOException */ private byte[] getImageBytes(String req) throws IOException { URL url = new URL(req); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(10000); try (InputStream inputStream = conn.getInputStream()) { return ImageProcessor.inputStreamToByteArray(inputStream); } } }
controller 层:
package com.emp.empxmrz.controller; import com.emp.empxmrz.controller.vo.ColorCastDetExampleReq; import com.emp.empxmrz.service.ColorCastDetExampleJniService; import com.emp.empxmrz.util.ColorCastDetExampleJni; import com.emp.empxmrz.util.R; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /*** * @title * @author shijiangyong * @date 2025/9/15 10:47 **/ @Slf4j @RestController @AllArgsConstructor @Tag(name = "demo示例") @RequestMapping("/example") public class ColorCastDetExampleJniController { private final ColorCastDetExampleJniService colorCastDetExampleJniService; @PostMapping("/getVersion") @Operation(summary = "管理后台-获取算法版本") public R<String> getVersion() { return R.ok(colorCastDetExampleJniService.getVersion()); } @PostMapping("/contrast/color/castDet") @Operation(summary = "管理后台-偏色检测算法") public R<ColorCastDetExampleJni.ContrastColorCastResults> contrastImageColorCast(ColorCastDetExampleReq req) { return R.ok(colorCastDetExampleJniService.contrastImageColorCast(req)); } @PostMapping("/contrast/color/castDetOri") @Operation(summary = "管理后台-偏色检测算法") public R<ColorCastDetExampleJni.ContrastColorCastResults> contrastImageColorCastOri(ColorCastDetExampleReq req) { return R.ok(colorCastDetExampleJniService.contrastImageColorCastOri(req)); } @PostMapping("/contrast/color/castDetByPath") @Operation(summary = "管理后台-偏色检测算法") public R<ColorCastDetExampleJni.ContrastColorCastResults> contrastImageColorCastByPath(ColorCastDetExampleReq req) { return R.ok(colorCastDetExampleJniService.contrastImageColorCastByPath(req)); } }
调用示例:
路径去掉 Ori 就是测试上面两个入参,加上 Ori 就是测试下面两个入参,反正到了 java 层,就自己随便玩了。实体类就不贴了。
调试建议
如果报错找不到 ColorCastDetExampleJni.dll
,请将该 DLL 放入:
- 项目运行目录;
- 或者
jdk/bin
目录; - 或者设置
-Djava.library.path
。
如果 JNI 函数名对应不上,请确保:
- 包名、类名、方法名匹配;
- DLL 导出的函数使用
extern "C"
。 - JNI 方法未正确导出,可通过
.def
文件显式指定导出符号。
七、总结
- 编写 Java native 接口;
- 使用
javah
生成 JNI 头文件; - C++ 实现 JNI 方法;
- 配置 Visual Studio 编译动态库;
- Java 调用测试。
如果你不想污染自己的JDK bin,可以将 .dll
和 .lib
放到一个统一的目录下,这样的话切换 jdk 比较方便,因为我只使用 jdk8,所以贪图方便放在了 bin 目录,但这是不规范滴 。
到此这篇关于Java调用C++动态库DLL实现图像处理的文章就介绍到这了,更多相关Java调用DLL实现图像处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!