修改yoloSDK的库加载逻辑
This commit is contained in:
parent
84125e62ec
commit
73f9459f37
|
|
@ -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.
Binary file not shown.
Binary file not shown.
BIN
.vs/slnx.sqlite
BIN
.vs/slnx.sqlite
Binary file not shown.
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue