diff --git a/.gitignore b/.gitignore index ef26ea0..55a0b4a 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* +build/ +.vs/ !/prebuilt_libs/win-x64/*.dll !/prebuilt_libs/linux-x86_64/*.so diff --git a/.vs/sishu-yolo-sdk/FileContentIndex/bd13bfa6-05ac-4f3d-b094-6c09a09bf4f5.vsidx b/.vs/sishu-yolo-sdk/FileContentIndex/bd13bfa6-05ac-4f3d-b094-6c09a09bf4f5.vsidx deleted file mode 100644 index e920317..0000000 Binary files a/.vs/sishu-yolo-sdk/FileContentIndex/bd13bfa6-05ac-4f3d-b094-6c09a09bf4f5.vsidx and /dev/null differ diff --git a/.vs/sishu-yolo-sdk/FileContentIndex/c122d212-269f-40fd-adb8-6b07769fb320.vsidx b/.vs/sishu-yolo-sdk/FileContentIndex/c122d212-269f-40fd-adb8-6b07769fb320.vsidx deleted file mode 100644 index 992d29e..0000000 Binary files a/.vs/sishu-yolo-sdk/FileContentIndex/c122d212-269f-40fd-adb8-6b07769fb320.vsidx and /dev/null differ diff --git a/.vs/sishu-yolo-sdk/v17/Browse.VC.db b/.vs/sishu-yolo-sdk/v17/Browse.VC.db index d310d76..0b70e3d 100644 Binary files a/.vs/sishu-yolo-sdk/v17/Browse.VC.db and b/.vs/sishu-yolo-sdk/v17/Browse.VC.db differ diff --git a/.vs/sishu-yolo-sdk/v17/ipch/AutoPCH/316448d41650dc7c/YOLOSDK_JNI.ipch b/.vs/sishu-yolo-sdk/v17/ipch/AutoPCH/316448d41650dc7c/YOLOSDK_JNI.ipch index b1b758c..bd54f53 100644 Binary files a/.vs/sishu-yolo-sdk/v17/ipch/AutoPCH/316448d41650dc7c/YOLOSDK_JNI.ipch and b/.vs/sishu-yolo-sdk/v17/ipch/AutoPCH/316448d41650dc7c/YOLOSDK_JNI.ipch differ diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite index 5af36f9..f5dc46b 100644 Binary files a/.vs/slnx.sqlite and b/.vs/slnx.sqlite differ diff --git a/src/main/java/com/bonus/sdk/YoloSdk.java b/src/main/java/com/bonus/sdk/YoloSdk.java index b97bef4..aa07b66 100644 --- a/src/main/java/com/bonus/sdk/YoloSdk.java +++ b/src/main/java/com/bonus/sdk/YoloSdk.java @@ -1,98 +1,135 @@ -package com.bonus.sdk; +package com.bonus.sdk; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.*; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.HashSet; import java.util.Set; public class YoloSdk implements AutoCloseable { - private long nativeHandle; // + private long nativeHandle; // 指向 C++ YoloDetector 实例的指针 private static final Set loadedLibraries = new HashSet<>(); + private static Path tempDir; // 用于存放所有本地库的唯一临时目录 /** - * */ + * 静态初始化块,用于加载本地库。 + */ static { try { - loadSdkLibrary(); + // 1. 创建一个唯一的临时目录,用于存放所有DLL/SO文件 + tempDir = Files.createTempDirectory("yolo_sdk_native_libs_"); + // 2. 确保在JVM退出时删除此目录 + tempDir.toFile().deleteOnExit(); + + // 3. 将所有库解压并加载到此目录 + loadSdkLibrary(tempDir); + } 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 osArch = System.getProperty("os.arch").toLowerCase(); String libPathInJar; String sdkLibName; - String[] dependencyLibs = {}; + String[] dependencyLibs = {}; if (osName.contains("win") && osArch.contains("64")) { - libPathInJar = "/lib/win-x64/"; - + // 依赖项必须按加载顺序列出 dependencyLibs = new String[]{ - "onnxruntime.dll", - "abseil_dll.dll", - "libprotobuf.dll", - "zlib1.dll", - "opencv_core4.dll", - "opencv_imgproc4.dll", - "opencv_dnn4.dll" + "onnxruntime.dll", + "abseil_dll.dll", + "libprotobuf.dll", + "zlib1.dll", + "opencv_core4.dll", + "opencv_imgproc4.dll", + "opencv_dnn4.dll" }; - sdkLibName = "my_yolo_sdk.dll"; } else if ((osName.contains("nix") || osName.contains("nux")) && osArch.contains("64")) { libPathInJar = "/lib/linux-x86_64/"; - + // 确保这些是您Linux构建所需的 .so 依赖项 dependencyLibs = new String[]{ - "libonnxruntime.so.1.23.2", - "libopencv_core.so.4.6.0", - "libopencv_imgproc.so.4.6.0", - "libopencv_dnn.so.4.6.0" + "libonnxruntime.so.1.23.2", // 示例,请使用您确切的文件名 + "libopencv_core.so.4.6.0", + "libopencv_imgproc.so.4.6.0", + "libopencv_dnn.so.4.6.0" }; - sdkLibName = "libmy_yolo_sdk.so"; } else { throw new UnsupportedOperationException("Unsupported OS/Arch: " + osName + "/" + osArch); } - // 1. + // --- 关键的修复逻辑 --- + + // 步骤 1: 首先,将所有库文件从JAR解压到同一个临时目录,保持原始文件名 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); } /** - * */ - private static void loadLibraryFromJar(String path) throws IOException { - String libName = new File(path).getName(); - if (loadedLibraries.contains(libName)) { - return; // - } - - try (InputStream in = YoloSdk.class.getResourceAsStream(path)) { + * * @param pathInJar + * @param tempDir + */ + private static void extractLibraryFromJar(String pathInJar, Path tempDir) throws IOException { + String libName = new File(pathInJar).getName(); + + try (InputStream in = YoloSdk.class.getResourceAsStream(pathInJar)) { 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(); // - - Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - System.load(tempFile.getAbsolutePath()); - loadedLibraries.add(libName); + // 目标文件路径,保持原始文件名 + Path targetFile = tempDir.resolve(libName); + + // 将文件从JAR复制到临时目录 + Files.copy(in, targetFile, StandardCopyOption.REPLACE_EXISTING); + + // 为解压的目录/文件设置退出时删除(双重保险) + targetFile.toFile().deleteOnExit(); } } - // --- + // --- Native JNI 方法 --- private native long nativeInit(String modelPath, int inputWidth, int inputHeight); private native void nativeRelease(long handle); private native Detection[] nativePredict( @@ -100,14 +137,27 @@ public class YoloSdk implements AutoCloseable { float confThreshold, float iouThreshold ); - // --- + // --- 公共 Java API --- + + /** + * 初始化YOLO SDK。 + * @param modelPath + * @param inputWidth + * @param inputHeight + */ public YoloSdk(String modelPath, int inputWidth, int inputHeight) { this.nativeHandle = nativeInit(modelPath, inputWidth, inputHeight); 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) { if (this.nativeHandle == 0) { throw new IllegalStateException("SDK already closed or failed to initialize."); @@ -119,6 +169,9 @@ public class YoloSdk implements AutoCloseable { ); } + /** + * 释放本地资源。 + */ @Override public void close() { if (this.nativeHandle != 0) { @@ -128,11 +181,16 @@ public class YoloSdk implements AutoCloseable { } /** - * */ + * * @param image + * @return + */ private byte[] getBgrBytes(BufferedImage image) { + // 如果图像已经是 3-byte BGR,直接返回 if (image.getType() == BufferedImage.TYPE_3BYTE_BGR) { return ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); } + + // 否则,创建一个 BGR 副本 BufferedImage bgrImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR); bgrImage.getGraphics().drawImage(image, 0, 0, null); return ((DataBufferByte) bgrImage.getRaster().getDataBuffer()).getData();