修改yoloSDK的库加载逻辑

This commit is contained in:
guanyuankai 2025-11-17 17:20:16 +08:00
parent 84125e62ec
commit 73f9459f37
7 changed files with 106 additions and 46 deletions

2
.gitignore vendored
View File

@ -60,6 +60,8 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid* hs_err_pid*
replay_pid* replay_pid*
build/
.vs/
!/prebuilt_libs/win-x64/*.dll !/prebuilt_libs/win-x64/*.dll
!/prebuilt_libs/linux-x86_64/*.so !/prebuilt_libs/linux-x86_64/*.so

Binary file not shown.

Binary file not shown.

View File

@ -1,98 +1,135 @@
package com.bonus.sdk; package com.bonus.sdk;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte; import java.awt.image.DataBufferByte;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class YoloSdk implements AutoCloseable { public class YoloSdk implements AutoCloseable {
private long nativeHandle; // private long nativeHandle; // 指向 C++ YoloDetector 实例的指针
private static final Set<String> loadedLibraries = new HashSet<>(); private static final Set<String> loadedLibraries = new HashSet<>();
private static Path tempDir; // 用于存放所有本地库的唯一临时目录
/** /**
* */ * 静态初始化块用于加载本地库
*/
static { static {
try { try {
loadSdkLibrary(); // 1. 创建一个唯一的临时目录用于存放所有DLL/SO文件
tempDir = Files.createTempDirectory("yolo_sdk_native_libs_");
// 2. 确保在JVM退出时删除此目录
tempDir.toFile().deleteOnExit();
// 3. 将所有库解压并加载到此目录
loadSdkLibrary(tempDir);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("CRITICAL: Failed to load native YOLO SDK libraries", e); throw new RuntimeException("CRITICAL: Failed to create temp directory for native libraries", e);
} }
} }
private static void loadSdkLibrary() throws IOException { /**
* 负责解压和加载所有本地库
* @param tempDir
*/
private static void loadSdkLibrary(Path tempDir) throws IOException {
String osName = System.getProperty("os.name").toLowerCase(); String osName = System.getProperty("os.name").toLowerCase();
String osArch = System.getProperty("os.arch").toLowerCase(); String osArch = System.getProperty("os.arch").toLowerCase();
String libPathInJar; String libPathInJar;
String sdkLibName; String sdkLibName;
String[] dependencyLibs = {}; String[] dependencyLibs = {};
if (osName.contains("win") && osArch.contains("64")) { if (osName.contains("win") && osArch.contains("64")) {
libPathInJar = "/lib/win-x64/"; libPathInJar = "/lib/win-x64/";
// 依赖项必须按加载顺序列出
dependencyLibs = new String[]{ dependencyLibs = new String[]{
"onnxruntime.dll", "onnxruntime.dll",
"abseil_dll.dll", "abseil_dll.dll",
"libprotobuf.dll", "libprotobuf.dll",
"zlib1.dll", "zlib1.dll",
"opencv_core4.dll", "opencv_core4.dll",
"opencv_imgproc4.dll", "opencv_imgproc4.dll",
"opencv_dnn4.dll" "opencv_dnn4.dll"
}; };
sdkLibName = "my_yolo_sdk.dll"; sdkLibName = "my_yolo_sdk.dll";
} else if ((osName.contains("nix") || osName.contains("nux")) && osArch.contains("64")) { } else if ((osName.contains("nix") || osName.contains("nux")) && osArch.contains("64")) {
libPathInJar = "/lib/linux-x86_64/"; libPathInJar = "/lib/linux-x86_64/";
// 确保这些是您Linux构建所需的 .so 依赖项
dependencyLibs = new String[]{ dependencyLibs = new String[]{
"libonnxruntime.so.1.23.2", "libonnxruntime.so.1.23.2", // 示例请使用您确切的文件名
"libopencv_core.so.4.6.0", "libopencv_core.so.4.6.0",
"libopencv_imgproc.so.4.6.0", "libopencv_imgproc.so.4.6.0",
"libopencv_dnn.so.4.6.0" "libopencv_dnn.so.4.6.0"
}; };
sdkLibName = "libmy_yolo_sdk.so"; sdkLibName = "libmy_yolo_sdk.so";
} else { } else {
throw new UnsupportedOperationException("Unsupported OS/Arch: " + osName + "/" + osArch); throw new UnsupportedOperationException("Unsupported OS/Arch: " + osName + "/" + osArch);
} }
// 1. // --- 关键的修复逻辑 ---
// 步骤 1: 首先将所有库文件从JAR解压到同一个临时目录保持原始文件名
for (String lib : dependencyLibs) { for (String lib : dependencyLibs) {
loadLibraryFromJar(libPathInJar + lib); extractLibraryFromJar(libPathInJar + lib, tempDir);
}
// 解压主SDK库
extractLibraryFromJar(libPathInJar + sdkLibName, tempDir);
// 步骤 2: 然后按顺序从该临时目录加载库
// 这样Windows/Linux加载器可以在同一目录中找到所有依赖项
try {
for (String lib : dependencyLibs) {
if (!loadedLibraries.contains(lib)) {
System.load(tempDir.resolve(lib).toAbsolutePath().toString());
loadedLibraries.add(lib);
}
}
if (!loadedLibraries.contains(sdkLibName)) {
System.load(tempDir.resolve(sdkLibName).toAbsolutePath().toString());
loadedLibraries.add(sdkLibName);
}
} catch (UnsatisfiedLinkError e) {
// 提供更详细的错误信息
System.err.println("--- NATIVE LIBRARY LOAD FAILED ---");
System.err.println("Failed to load native library. This often means a dependency is missing on the host system.");
System.err.println("For Windows: Ensure 'Visual C++ Redistributable for Visual Studio (x64)' is installed.");
System.err.println("Libraries were extracted to: " + tempDir.toAbsolutePath());
System.err.println("-----------------------------------");
throw e; // 重新抛出原始错误
} }
// 2.
loadLibraryFromJar(libPathInJar + sdkLibName);
} }
/** /**
* */ * * @param pathInJar
private static void loadLibraryFromJar(String path) throws IOException { * @param tempDir
String libName = new File(path).getName(); */
if (loadedLibraries.contains(libName)) { private static void extractLibraryFromJar(String pathInJar, Path tempDir) throws IOException {
return; // String libName = new File(pathInJar).getName();
}
try (InputStream in = YoloSdk.class.getResourceAsStream(pathInJar)) {
try (InputStream in = YoloSdk.class.getResourceAsStream(path)) {
if (in == null) { if (in == null) {
throw new FileNotFoundException("Library " + path + " not found in JAR."); throw new FileNotFoundException("Library " + pathInJar + " not found in JAR.");
} }
File tempFile = File.createTempFile(libName, ".tmp"); // 目标文件路径保持原始文件名
tempFile.deleteOnExit(); // Path targetFile = tempDir.resolve(libName);
Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); // 将文件从JAR复制到临时目录
System.load(tempFile.getAbsolutePath()); Files.copy(in, targetFile, StandardCopyOption.REPLACE_EXISTING);
loadedLibraries.add(libName);
// 为解压的目录/文件设置退出时删除双重保险
targetFile.toFile().deleteOnExit();
} }
} }
// --- // --- Native JNI 方法 ---
private native long nativeInit(String modelPath, int inputWidth, int inputHeight); private native long nativeInit(String modelPath, int inputWidth, int inputHeight);
private native void nativeRelease(long handle); private native void nativeRelease(long handle);
private native Detection[] nativePredict( private native Detection[] nativePredict(
@ -100,14 +137,27 @@ public class YoloSdk implements AutoCloseable {
float confThreshold, float iouThreshold float confThreshold, float iouThreshold
); );
// --- // --- 公共 Java API ---
/**
* 初始化YOLO SDK
* @param modelPath
* @param inputWidth
* @param inputHeight
*/
public YoloSdk(String modelPath, int inputWidth, int inputHeight) { public YoloSdk(String modelPath, int inputWidth, int inputHeight) {
this.nativeHandle = nativeInit(modelPath, inputWidth, inputHeight); this.nativeHandle = nativeInit(modelPath, inputWidth, inputHeight);
if (this.nativeHandle == 0) { if (this.nativeHandle == 0) {
throw new RuntimeException("Failed to initialize native YOLO SDK. Check logs."); throw new RuntimeException("Failed to initialize native YOLO SDK. Check logs for details.");
} }
} }
/**
* @param image
* @param confThreshold
* @param iouThreshold
* @return
*/
public Detection[] predict(BufferedImage image, float confThreshold, float iouThreshold) { public Detection[] predict(BufferedImage image, float confThreshold, float iouThreshold) {
if (this.nativeHandle == 0) { if (this.nativeHandle == 0) {
throw new IllegalStateException("SDK already closed or failed to initialize."); throw new IllegalStateException("SDK already closed or failed to initialize.");
@ -119,6 +169,9 @@ public class YoloSdk implements AutoCloseable {
); );
} }
/**
* 释放本地资源
*/
@Override @Override
public void close() { public void close() {
if (this.nativeHandle != 0) { if (this.nativeHandle != 0) {
@ -128,11 +181,16 @@ public class YoloSdk implements AutoCloseable {
} }
/** /**
* */ * * @param image
* @return
*/
private byte[] getBgrBytes(BufferedImage image) { private byte[] getBgrBytes(BufferedImage image) {
// 如果图像已经是 3-byte BGR直接返回
if (image.getType() == BufferedImage.TYPE_3BYTE_BGR) { if (image.getType() == BufferedImage.TYPE_3BYTE_BGR) {
return ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); return ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
} }
// 否则创建一个 BGR 副本
BufferedImage bgrImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR); BufferedImage bgrImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
bgrImage.getGraphics().drawImage(image, 0, 0, null); bgrImage.getGraphics().drawImage(image, 0, 0, null);
return ((DataBufferByte) bgrImage.getRaster().getDataBuffer()).getData(); return ((DataBufferByte) bgrImage.getRaster().getDataBuffer()).getData();