diff --git a/pom.xml b/pom.xml index f4de52a..7affc08 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ 3.5.5 + org.springframework.boot @@ -83,6 +84,14 @@ ${project.basedir}/src/main/resources/libs/MvCameraControlWrapper.jar + + opencv + opencv + 1.0 + system + ${project.basedir}/src/main/resources/libs/opencv-453.jar + + org.springframework.boot @@ -109,7 +118,6 @@ org.springframework.boot spring-boot-starter-aop - com.google.code.gson diff --git a/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java b/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java index fb9d57c..016c3ae 100644 --- a/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java +++ b/src/main/java/com/example/lxcameraapi/service/s7/S7MultiPlcService.java @@ -14,6 +14,14 @@ import com.sourceforge.snap7.moka7.S7Client; import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint2f; +import org.opencv.core.Point; +import org.opencv.core.Size; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +import java.io.File; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; @@ -50,6 +58,37 @@ import java.util.concurrent.*; @Slf4j public class S7MultiPlcService { + // 静态加载OpenCV DLL + static { + String[] dllPaths = { + System.getProperty("user.dir") + "/libs/opencv/opencv_java453.dll", + System.getProperty("user.dir") + "/out/opencv_java453.dll" + }; + boolean loaded = false; + for (String dllPath : dllPaths) { + File dllFile = new File(dllPath); + if (dllFile.exists()) { + try { + System.load(dllFile.getAbsolutePath()); + log.info("OpenCV DLL 加载成功: {}", dllFile.getAbsolutePath()); + loaded = true; + break; + } catch (UnsatisfiedLinkError e) { + log.warn("加载失败 {}: {}", dllPath, e.getMessage()); + } + } + } + if (!loaded) { + try { + System.loadLibrary("opencv_java453"); + log.info("OpenCV DLL 从系统路径加载成功"); + loaded = true; + } catch (UnsatisfiedLinkError e) { + log.error("OpenCV DLL 加载失败,所有路径都不存在或加载失败"); + } + } + } + // PLC连接池映射: plcNumber -> 连接池 private final Map> plcConnectionPools = new ConcurrentHashMap<>(); @@ -436,96 +475,90 @@ public class S7MultiPlcService { return null; } + public static void main(String[] args) { + AppConfig.Camera camera = new AppConfig.Camera(); + camera.setCrop(new ArrayList<>()); + List crop = new ArrayList<>(); + crop.add(640); + crop.add(640); + camera.getCrop().add(List.of(800, 1800)); + camera.getCrop().add( List.of(800, 550)); + camera.getCrop().add(List.of(2100, 550)); + camera.getCrop().add(List.of(2100, 1800)); + camera.setCropSize(crop); + S7MultiPlcService service = new S7MultiPlcService(); + service.cropAndCompressImage("D:\\data\\media\\2026-05-15\\1\\2026-05-15-01-16-18-640.png.jpg",camera); + } + /** * 透视变换裁剪图片并压缩 * @param imagePath 原图路径 */ - private void cropAndCompressImage(String imagePath,AppConfig.Camera config) { + private void cropAndCompressImage(String imagePath, AppConfig.Camera config) { try { + File imageFile = new File(imagePath); if (!imageFile.exists()) { log.warn("图片文件不存在: {}", imagePath); return; } - BufferedImage originalImage = ImageIO.read(imageFile); - if (originalImage == null) { - log.warn("无法读取图片: {}", imagePath); - return; - } List> crop = config.getCrop(); List cropSize = config.getCropSize(); - int targetWidth = cropSize.get(0); int targetHeight = cropSize.get(1); - // 源四边形四个顶点 (顺时针: 左上, 右上, 右下, 左下) - double x0 = crop.get(0).get(0), y0 = crop.get(0).get(1); - double x1 = crop.get(1).get(0), y1 = crop.get(1).get(1); - double x2 = crop.get(2).get(0), y2 = crop.get(2).get(1); - double x3 = crop.get(3).get(0), y3 = crop.get(3).get(1); - - // 目标矩形四个顶点 (左上, 右上, 右下, 左下) - double u0 = 0, v0 = 0; - double u1 = targetWidth, v1 = 0; - double u2 = targetWidth, v2 = targetHeight; - double u3 = 0, v3 = targetHeight; - - // 创建目标图像 - BufferedImage croppedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB); - int[] srcPixels = originalImage.getRGB(0, 0, originalImage.getWidth(), originalImage.getHeight(), null, 0, originalImage.getWidth()); - int srcWidth = originalImage.getWidth(); - int srcHeight = originalImage.getHeight(); - - // 计算透视变换的逆变换系数 (从目标坐标映射到源坐标) - // 使用双线性插值的透视变换 - for (int y = 0; y < targetHeight; y++) { - for (int x = 0; x < targetWidth; x++) { - // 计算相对于目标矩形的归一化坐标 - double mu = (double) x / targetWidth; - double mv = (double) y / targetHeight; - - // 计算透视变换后的源坐标 (使用双线性插值) - double srcX = bilinearInterpolate( - bilinearInterpolate(x0, x1, mu), - bilinearInterpolate(x3, x2, mu), - mv - ); - double srcY = bilinearInterpolate( - bilinearInterpolate(y0, y1, mu), - bilinearInterpolate(y3, y2, mu), - mv - ); - - // 从源图像采样像素 - int srcXInt = (int) Math.round(srcX); - int srcYInt = (int) Math.round(srcY); - - if (srcXInt >= 0 && srcXInt < srcWidth && srcYInt >= 0 && srcYInt < srcHeight) { - int pixel = srcPixels[srcYInt * srcWidth + srcXInt]; - croppedImage.setRGB(x, y, pixel); - } else { - croppedImage.setRGB(x, y, 0xFFFFFF); // 白色填充 - } - } + // crop.get(0) -> 目标左上, crop.get(1) -> 目标右上 + // crop.get(2) -> 目标右下, crop.get(3) -> 目标左下 + // 源图像中的四个点 + Point[] srcArray = new Point[] { + new Point(crop.get(0).get(0), crop.get(0).get(1)), + new Point(crop.get(1).get(0), crop.get(1).get(1)), + new Point(crop.get(2).get(0), crop.get(2).get(1)), + new Point(crop.get(3).get(0), crop.get(3).get(1)) + }; + MatOfPoint2f srcPoints2f = new MatOfPoint2f(srcArray); + + // 目标矩形的四个顶点 + Point[] dstArray = new Point[] { + new Point(0, 0), + new Point(targetWidth, 0), + new Point(targetWidth, targetHeight), + new Point(0, targetHeight) + }; + MatOfPoint2f dstPoints = new MatOfPoint2f(dstArray); + + // 计算透视变换矩阵 + Mat transformMatrix = Imgproc.getPerspectiveTransform(srcPoints2f, dstPoints); + + // 读取源图像 + Mat srcImage = Imgcodecs.imread(imagePath); + if (srcImage.empty()) { + log.warn("无法读取图片: {}", imagePath); + return; } - // 压缩并保存 - compressAndSaveImage(croppedImage, imageFile, config.getCompress()); + // 创建目标图像并进行透视变换 + Mat dstImage = new Mat(); + Imgproc.warpPerspective(srcImage, dstImage, transformMatrix, new Size(targetWidth, targetHeight)); + + // 保存结果 + Imgcodecs.imwrite(imagePath, dstImage); + + // 释放资源 + srcImage.release(); + dstImage.release(); + transformMatrix.release(); + srcPoints2f.release(); + dstPoints.release(); log.info("图片透视裁剪完成: {}, 尺寸: {}x{}", imagePath, targetWidth, targetHeight); - } catch (IOException e) { + } catch (Exception e) { log.error("图片透视裁剪失败: {}", imagePath, e); } } - /** - * 双线性插值 - */ - private double bilinearInterpolate(double v0, double v1, double t) { - return v0 + (v1 - v0) * t; - } /** * 保存为JPEG并覆盖原图 diff --git a/src/main/resources/libs/opencv-453.jar b/src/main/resources/libs/opencv-453.jar new file mode 100644 index 0000000..78ba233 Binary files /dev/null and b/src/main/resources/libs/opencv-453.jar differ