作者 钟来

模块整理

1 package com.zhonglai.luhui.smart.feeder.opencv; 1 package com.zhonglai.luhui.smart.feeder.opencv;
2 2
3 import com.ruoyi.common.utils.DESUtil; 3 import com.ruoyi.common.utils.DESUtil;
  4 +import com.ruoyi.common.utils.StringUtils;
4 import com.zhonglai.luhui.smart.feeder.config.OpenCVConfig; 5 import com.zhonglai.luhui.smart.feeder.config.OpenCVConfig;
5 import com.zhonglai.luhui.smart.feeder.dto.VeiwDto; 6 import com.zhonglai.luhui.smart.feeder.dto.VeiwDto;
6 import com.zhonglai.luhui.smart.feeder.service.CameraService; 7 import com.zhonglai.luhui.smart.feeder.service.CameraService;
  8 +import com.zhonglai.luhui.smart.feeder.service.FFmCameraService;
7 import com.zhonglai.luhui.smart.feeder.service.impl.HtmllVeiwServiceImpl; 9 import com.zhonglai.luhui.smart.feeder.service.impl.HtmllVeiwServiceImpl;
8 import com.zhonglai.luhui.smart.feeder.service.impl.JFrameVeiwServiceImpl; 10 import com.zhonglai.luhui.smart.feeder.service.impl.JFrameVeiwServiceImpl;
9 import org.bytedeco.javacv.FrameGrabber; 11 import org.bytedeco.javacv.FrameGrabber;
@@ -22,12 +24,43 @@ import java.util.List; @@ -22,12 +24,43 @@ import java.util.List;
22 24
23 25
24 public class OpenCVUtil { 26 public class OpenCVUtil {
  27 +
  28 + private static String MAC = "78-a6-a0-d2-bd-e1";
25 private static final Logger logger = LoggerFactory.getLogger(OpenCVUtil.class); 29 private static final Logger logger = LoggerFactory.getLogger(OpenCVUtil.class);
26 30
27 public static void main(String[] args) { 31 public static void main(String[] args) {
28 System.out.println(DESUtil.decode("5F06AAC657B2E2B287289D25D950A829", "EXU5RUhI1"));; 32 System.out.println(DESUtil.decode("5F06AAC657B2E2B287289D25D950A829", "EXU5RUhI1"));;
29 } 33 }
30 34
  35 + public static VideoCapture readVideoCaptureForRtsp()
  36 + {
  37 + FFmCameraService fFmCameraService = new FFmCameraService();
  38 + String ip = fFmCameraService.findCameraIp();
  39 + if(StringUtils.isEmpty(ip))
  40 + {
  41 + logger.info("未检测到摄像头{},尝试打开本地视频",MP4_FILE_PATH);
  42 + //如果找不到摄像头就找本地视频文件
  43 + File file = new File(MP4_FILE_PATH);
  44 + if(file.exists() && file.isFile())
  45 + {
  46 + VideoCapture videoCapture = OpenCVUtil.readVideoCaptureForVideo(MP4_FILE_PATH);
  47 + return videoCapture;
  48 + }
  49 + logger.info("未检测到摄像头!!!");
  50 + }
  51 + String rtspUrl = "rtsp://admin:Luhui586@"+ip+":554/h264/ch1/main/av_stream";
  52 + VideoCapture videoCapture = new VideoCapture(rtspUrl);
  53 + while (!videoCapture.isOpened())
  54 + {
  55 + try {
  56 + Thread.sleep(1000);
  57 + } catch (InterruptedException e) {
  58 + throw new RuntimeException(e);
  59 + }
  60 + }
  61 + return videoCapture;
  62 + }
  63 +
31 public static VideoCapture readVideoCaptureForVideo(int i) 64 public static VideoCapture readVideoCaptureForVideo(int i)
32 { 65 {
33 logger.info("初始化摄像头"); 66 logger.info("初始化摄像头");
@@ -92,7 +125,7 @@ public class OpenCVUtil { @@ -92,7 +125,7 @@ public class OpenCVUtil {
92 Mat extractedRegion = Mat.zeros(frame.size(), frame.type()); 125 Mat extractedRegion = Mat.zeros(frame.size(), frame.type());
93 126
94 // 将指定的轮廓绘制到新的Mat上 127 // 将指定的轮廓绘制到新的Mat上
95 - Imgproc.drawContours(extractedRegion, Collections.singletonList(largestContour), 0, new Scalar(255, 255, 255), -1); 128 + Imgproc.drawContours(extractedRegion,Collections.singletonList(largestContour) , 0, new Scalar(255, 255, 255), -1);
96 129
97 // 使用按位与操作提取对应的图像区域 130 // 使用按位与操作提取对应的图像区域
98 Mat extractedImage = new Mat(); 131 Mat extractedImage = new Mat();
@@ -119,6 +152,9 @@ public class OpenCVUtil { @@ -119,6 +152,9 @@ public class OpenCVUtil {
119 Mat hierarchy = new Mat(); 152 Mat hierarchy = new Mat();
120 Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); 153 Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
121 154
  155 + gray.release();
  156 + binary.release();
  157 + hierarchy.release();
122 return contours; 158 return contours;
123 159
124 } 160 }
@@ -40,7 +40,7 @@ public class CameraService { @@ -40,7 +40,7 @@ public class CameraService {
40 */ 40 */
41 private void openCapture() 41 private void openCapture()
42 { 42 {
43 - videoCapture = OpenCVUtil.openCapture(); 43 + videoCapture = OpenCVUtil.readVideoCaptureForRtsp();
44 if(null == videoCapture) 44 if(null == videoCapture)
45 { 45 {
46 return; 46 return;
@@ -70,7 +70,7 @@ public class CameraService { @@ -70,7 +70,7 @@ public class CameraService {
70 { 70 {
71 if(null == scheduledFuture || scheduledFuture.isDone()) 71 if(null == scheduledFuture || scheduledFuture.isDone())
72 { 72 {
73 - scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(() -> { 73 + scheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
74 if(!videoIsOpen) 74 if(!videoIsOpen)
75 { 75 {
76 openCapture(); 76 openCapture();
1 package com.zhonglai.luhui.smart.feeder.service; 1 package com.zhonglai.luhui.smart.feeder.service;
2 2
3 -import org.bytedeco.javacv.CanvasFrame;  
4 -import org.bytedeco.javacv.FFmpegFrameGrabber; 3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.hutool.core.util.ReUtil;
  5 +import cn.hutool.core.util.RuntimeUtil;
  6 +import com.ruoyi.common.utils.StringUtils;
  7 +import org.bytedeco.ffmpeg.global.avutil;
  8 +import org.bytedeco.javacv.*;
5 import org.bytedeco.javacv.Frame; 9 import org.bytedeco.javacv.Frame;
6 -import org.bytedeco.javacv.FrameGrabber; 10 +import org.springframework.stereotype.Service;
7 11
  12 +import java.awt.*;
  13 +import java.awt.image.BufferedImage;
  14 +import java.time.LocalDateTime;
  15 +import java.time.format.DateTimeFormatter;
  16 +import java.util.ArrayList;
  17 +import java.util.List;
  18 +
  19 +@Service
8 public class FFmCameraService { 20 public class FFmCameraService {
9 - public static void main(String[] args) {  
10 -// int maxCameraIndex = 10; // 最大尝试的摄像头索引数量  
11 -// for (int cameraIndex = 0; cameraIndex < maxCameraIndex; ++cameraIndex) {  
12 -// try {  
13 -// FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(cameraIndex);  
14 -// grabber.start(); // 尝试开始,如果失败将抛出异常  
15 -// try {  
16 -// // 创建一个窗口用于显示摄像头的视频流  
17 -// CanvasFrame frame = new CanvasFrame("Webcam");  
18 -//  
19 -// // 判断窗口是否关闭  
20 -// while (frame.isVisible()) {  
21 -// // 抓取一帧视频并将其放在窗口上显示,该操作会阻塞程序,直到下一帧视频可用  
22 -// Frame grabbedFrame = grabber.grab();  
23 -// frame.showImage(grabbedFrame);  
24 -// }  
25 -//  
26 -// // 关闭窗口  
27 -// frame.dispose();  
28 -// } finally {  
29 -// // 停止抓取  
30 -// grabber.stop();  
31 -// }  
32 -// break; // 如果找到了摄像头并成功打开,就结束循环  
33 -// } catch (FrameGrabber.Exception e) {  
34 -// // 如果失败,就继续尝试下一个摄像头  
35 -// System.out.println("Failed to start the camera with device index: " + cameraIndex);  
36 -// }  
37 -// } 21 +
  22 + private FFmpegFrameGrabber grabber;
  23 +
  24 + private static String MAC = "78-a6-a0-d2-bd-e1";
  25 + /**
  26 + * 查找摄像头
  27 + * @return
  28 + */
  29 + public String findCameraIp()
  30 + {
  31 + List<String> list = RuntimeUtil.execForLines("arp", "-a");
  32 + for (String str:list)
  33 + {
  34 + if(str.indexOf(MAC)>=0)
  35 + {
  36 + // 使用Hutool提取字符串中的IP地址
  37 + List<String> ips = ReUtil.findAll("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", str, 0, new ArrayList<>());
  38 + if(null != ips && ips.size()!=0)
  39 + {
  40 + return ips.get(0);
  41 + }
  42 + }
  43 + }
  44 +
  45 + return null;
  46 + }
  47 +
  48 + /**
  49 + * 加载摄像头
  50 + */
  51 + public void loadCamera()
  52 + {
  53 + String ip = findCameraIp();
  54 + if(StringUtils.isEmpty(ip))
  55 + {
  56 + System.out.println("没有找到摄像头:"+MAC);
  57 + return;
  58 + }
  59 + String rtspUrl = "rtsp://admin:Luhui586@"+ip+":554/h264/ch1/main/av_stream";
  60 +
  61 + try {
  62 + FFmpegFrameGrabber.tryLoad();
  63 + } catch (Exception e) {
  64 + throw new RuntimeException("Failed to load FFmpeg", e);
  65 + }
  66 +
  67 + grabber = new FFmpegFrameGrabber(rtspUrl);
  68 + grabber.setVideoOption("fflags", "nobuffer"); // 禁用缓冲
  69 + grabber.setVideoOption("rtsp_transport", "tcp"); // 使用TCP传输
  70 + avutil.av_log_set_level(avutil.AV_LOG_ERROR); // 设置日志级别
  71 +
  72 + try {
  73 + grabber.start();
  74 + } catch (Exception e) {
  75 + close();
  76 + e.printStackTrace();
  77 + }
  78 + }
  79 +
  80 + public Boolean isOk()
  81 + {
  82 + try {
  83 + return null!=grabber && grabber.grab() != null;
  84 + } catch (FrameGrabber.Exception e) {
  85 + return false;
  86 + }
  87 + }
  88 +
  89 + public void close()
  90 + {
  91 + if(null != grabber)
  92 + {
  93 + try {
  94 + grabber.stop();
  95 + grabber.close();
  96 + } catch (Exception e) {
  97 + e.printStackTrace();
  98 + }
  99 + }
  100 +
  101 + }
  102 +
  103 + private void display(Frame frame) throws FrameGrabber.Exception {
  104 + CanvasFrame canvasFrame = new CanvasFrame("Key Frame Capture", CanvasFrame.getDefaultGamma() / grabber.getGamma());
  105 + canvasFrame.dispose();
  106 + while (true) {
  107 + canvasFrame.showImage(dream(getFrame()));
  108 + LocalDateTime now = LocalDateTime.now();
  109 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
  110 + String formattedDateTime = now.format(formatter);
  111 +
  112 + System.out.println("当前时间: " + formattedDateTime);
  113 + }
  114 + }
  115 +
  116 + public Frame getFrame() {
  117 + Frame frame = null;
  118 + try {
  119 + frame = grabber.grabImage();
  120 + if (frame == null) {
  121 + //TODO:连续n次为null,进行重连
  122 + System.out.println("frame is null");
  123 + return null;
  124 + }
  125 + else{
  126 + return frame;
  127 + }
  128 + } catch (FrameGrabber.Exception e) {
  129 + return null;
  130 + }
  131 +
  132 + }
  133 +
  134 +
  135 + private int maxX = 200;
  136 + private int POINT_RADIUS = 3;
  137 + private int quHeight = 200;
  138 + private java.util.List<Double> points = new ArrayList<>();
  139 + private BufferedImage dream(Frame frame)
  140 + {
  141 + BufferedImage image = Java2DFrameUtils.toBufferedImage(frame);
  142 +
  143 + double point = Math.random();
  144 + points.add(point);
  145 + if (points.size() > maxX) {
  146 + points.remove(0);
  147 + }
  148 +
  149 + BufferedImage curveImage = new BufferedImage(image.getWidth(), image.getHeight()+quHeight,image.getType());
  150 + Graphics2D g2d = curveImage.createGraphics();
  151 +
  152 + // 在图像上绘制曲线
  153 + g2d.setColor(Color.RED);
  154 + g2d.setStroke(new BasicStroke(POINT_RADIUS));
  155 +
  156 + Double maxY = ArrayUtil.max(points.toArray(new Double[0]));
  157 +
  158 + int vx = image.getWidth()/maxX;
  159 + Double vy = quHeight/maxY;
  160 + // 绘制动态曲线
  161 + for(int i=0;i<points.size()-1;i++)
  162 + {
  163 + int x = i*vx;
  164 + int y = new Double(points.get(i)*vy).intValue();
  165 + g2d.drawLine(x, y+image.getHeight(), x+vx, new Double(points.get(i+1)*vy).intValue()+image.getHeight());
  166 + }
  167 +
  168 + // 将第一个帧绘制到合并图像的上方
  169 + curveImage.createGraphics().drawImage(image, 0, 0, null);
  170 +
  171 + g2d.dispose();
  172 + return curveImage;
38 } 173 }
39 } 174 }
@@ -3,11 +3,12 @@ package com.zhonglai.luhui.smart.feeder.service; @@ -3,11 +3,12 @@ package com.zhonglai.luhui.smart.feeder.service;
3 import com.zhonglai.luhui.smart.feeder.config.WebSocketClien; 3 import com.zhonglai.luhui.smart.feeder.config.WebSocketClien;
4 import com.zhonglai.luhui.smart.feeder.dto.ConfigurationParameter; 4 import com.zhonglai.luhui.smart.feeder.dto.ConfigurationParameter;
5 import com.zhonglai.luhui.smart.feeder.dto.VeiwDto; 5 import com.zhonglai.luhui.smart.feeder.dto.VeiwDto;
6 -import com.zhonglai.luhui.smart.feeder.dto.VeiwType;  
7 import com.zhonglai.luhui.smart.feeder.opencv.OpenCVUtil; 6 import com.zhonglai.luhui.smart.feeder.opencv.OpenCVUtil;
8 import com.zhonglai.luhui.smart.feeder.service.impl.HtmllVeiwServiceImpl; 7 import com.zhonglai.luhui.smart.feeder.service.impl.HtmllVeiwServiceImpl;
9 import org.bytedeco.javacv.Frame; 8 import org.bytedeco.javacv.Frame;
  9 +import org.bytedeco.javacv.Java2DFrameUtils;
10 import org.bytedeco.javacv.OpenCVFrameConverter; 10 import org.bytedeco.javacv.OpenCVFrameConverter;
  11 +import org.bytedeco.opencv.opencv_core.IplImage;
11 import org.opencv.core.Mat; 12 import org.opencv.core.Mat;
12 import org.opencv.core.MatOfPoint; 13 import org.opencv.core.MatOfPoint;
13 import org.opencv.core.Scalar; 14 import org.opencv.core.Scalar;
@@ -65,7 +66,7 @@ public class FishGroupImageRecognitionService { @@ -65,7 +66,7 @@ public class FishGroupImageRecognitionService {
65 },1,1,TimeUnit.SECONDS); 66 },1,1,TimeUnit.SECONDS);
66 67
67 } 68 }
68 - 69 + // 创建FrameConverter对象
69 public void start() 70 public void start()
70 { 71 {
71 if(cameraService.getVideoIsOpen()) //摄像头打开才能识别 72 if(cameraService.getVideoIsOpen()) //摄像头打开才能识别
@@ -105,13 +106,15 @@ public class FishGroupImageRecognitionService { @@ -105,13 +106,15 @@ public class FishGroupImageRecognitionService {
105 if (area > maxArea) { 106 if (area > maxArea) {
106 maxArea = area; 107 maxArea = area;
107 maxAreaIndex = i; 108 maxAreaIndex = i;
108 - }else{  
109 - matOfPoint.release();  
110 } 109 }
111 } 110 }
112 // 获取最大区域的轮廓 111 // 获取最大区域的轮廓
113 MatOfPoint largestContour = contours.get(maxAreaIndex); 112 MatOfPoint largestContour = contours.get(maxAreaIndex);
114 - 113 + contours.remove(maxAreaIndex);
  114 + for(MatOfPoint matOfPoint:contours)
  115 + {
  116 + matOfPoint.release();
  117 + }
115 firstBinaryImage.release(); 118 firstBinaryImage.release();
116 hierarchy.release(); 119 hierarchy.release();
117 120
@@ -158,8 +161,8 @@ public class FishGroupImageRecognitionService { @@ -158,8 +161,8 @@ public class FishGroupImageRecognitionService {
158 logger.info("重新初始化"); 161 logger.info("重新初始化");
159 cameraService.clean(); 162 cameraService.clean();
160 } 163 }
  164 + frame.release();
161 return; 165 return;
162 -  
163 } 166 }
164 if (fishGroupImageRecognition && isread) { 167 if (fishGroupImageRecognition && isread) {
165 identify(frame); 168 identify(frame);