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