正在显示
4 个修改的文件
包含
215 行增加
和
41 行删除
| 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); |
-
请 注册 或 登录 后发表评论