diff --git a/README.md b/README.md index 7241f3e..f9818c6 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,3 @@ -

- logo -

-

rtsp视频分析 v1.0.0

-

基于SpringBoot+Vue前后端分离的rtsp视频分析系统

- -## 平台简介 - -这是一个基于ruoyi-vue框架改进而来的RTSP视频分析平台,继承ruoyi框架的基本功能。项目集成了虹软SDK,实现了人脸识别、活体检测、3D角度分析、年龄及性别识别等功能;同时,利用JavaCV进行高效的视频处理,将rtsp视频转成http-flv和ws-flv。后端采用SpringBoot框架,前端则运用了Vue3框架,确保系统的稳定与用户体验的流畅。 - -* 前端采用Vue3、Element Plus、XgpLayer。 -* 后端采用Spring Boot、Spring Security、Redis 、 javaCv & Jwt。 - -加微信联系: chenbai0511 - -## 内置功能 - -1. 若依全功能。 -2. rtsp视频转http-flv,ws-flv在线播放。 -3. rtsp视频人脸识别,活体检测,3D角度分析,年龄及性别识别。 - -## 演示图 - - - - - - - - - - - -
- -## 联系我吧 - - - -qwq \ No newline at end of file +java 17 +node16 .20 +opencv推测4.10 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index daa28a0..1507567 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -7,7 +7,7 @@ ruoyi: # 版权年份 copyrightYear: 2025 # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) - profile: /home/wangyu/uploadPath + profile: D:\temp # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数字计算 char 字符验证 diff --git a/ruoyi-admin/src/main/resources/libs/models/garbage/classes.txt b/ruoyi-admin/src/main/resources/libs/models/garbage/classes.txt new file mode 100644 index 0000000..c5c3c75 --- /dev/null +++ b/ruoyi-admin/src/main/resources/libs/models/garbage/classes.txt @@ -0,0 +1 @@ +trash \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/libs/models/garbage/garbage.onnx b/ruoyi-admin/src/main/resources/libs/models/garbage/garbage.onnx new file mode 100644 index 0000000..d646c08 Binary files /dev/null and b/ruoyi-admin/src/main/resources/libs/models/garbage/garbage.onnx differ diff --git a/ruoyi-admin/src/main/resources/libs/models/models.json b/ruoyi-admin/src/main/resources/libs/models/models.json new file mode 100644 index 0000000..4d85293 --- /dev/null +++ b/ruoyi-admin/src/main/resources/libs/models/models.json @@ -0,0 +1,5 @@ +[ + {"name":"garbage","path":"libs/models/garbage","size":[640,640],"backend":"OpenCV"}, + {"name":"smoke","path":"libs/models/smoke","size":[640,640],"backend":"OpenCV"} + +] diff --git a/ruoyi-video/src/main/resources/libs/models/smoke/smoke.onnx b/ruoyi-admin/src/main/resources/libs/models/smoke/smoke.onnx similarity index 100% rename from ruoyi-video/src/main/resources/libs/models/smoke/smoke.onnx rename to ruoyi-admin/src/main/resources/libs/models/smoke/smoke.onnx diff --git a/ruoyi-video/src/main/java/com/ruoyi/video/thread/MediaTransferFlvByJavacv.java b/ruoyi-video/src/main/java/com/ruoyi/video/thread/MediaTransferFlvByJavacv.java index 53c580c..92e04fc 100644 --- a/ruoyi-video/src/main/java/com/ruoyi/video/thread/MediaTransferFlvByJavacv.java +++ b/ruoyi-video/src/main/java/com/ruoyi/video/thread/MediaTransferFlvByJavacv.java @@ -201,16 +201,15 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable if (!enableDetection) return; modelManager = new ModelManager(); - URL json = getClass().getResource("/models/models.json"); + URL json = getClass().getResource("/libs/models/models.json"); modelManager.load(json); - // 你可按需切换单模型或多模型并行 // detector = modelManager.get("person-helmet"); detector = new CompositeDetector( "all-models", java.util.List.of( - modelManager.get("person-helmet"), - modelManager.get("vehicle-plate") + modelManager.get("garbage"), + modelManager.get("smoke") ), 2 // 并行度 ); @@ -345,6 +344,7 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable if (enableDetection) initDetectors(); } catch (Exception e) { log.error("初始化检测模型失败:{}", e.getMessage(), e); + // 模型失败不影响推流,但不禁用检测 } if (!createGrabber()) return; @@ -367,7 +367,7 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable final long DETECTION_INTERVAL_MS = 3000; // 每3秒检测一次 long lastDetectionTime = 0; List currentDetections = Collections.emptyList(); // 当前显示的检测结果 - + for (; running && grabberStatus && recorderStatus; ) { try { if (transferFlag) { @@ -395,48 +395,48 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable closeMedia(); break; } - + if (frame != null && enableDetection) { // 将Frame转换为Mat以进行处理 Mat mat = toMat.convert(frame); - + if (mat != null && !mat.empty()) { long currentTime = System.currentTimeMillis(); - + // 每隔DETECTION_INTERVAL_MS执行一次检测 if (currentTime - lastDetectionTime >= DETECTION_INTERVAL_MS) { try { - log.debug("执行新一轮检测,上次检测时间: {}ms前", + log.debug("执行新一轮检测,上次检测时间: {}ms前", currentTime - lastDetectionTime); - + // 创建副本进行检测 Mat detectionMat = new Mat(); mat.copyTo(detectionMat); - + // 执行检测 currentDetections = detector.detect(detectionMat); lastDetectionTime = currentTime; latestDetections.set(currentDetections); - + // 释放检测Mat detectionMat.release(); - + // 窗口巡检回调 - if (windowMode && detectionListener != null && + if (windowMode && detectionListener != null && currentJobId != null && currentDeviceId != null) { - detectionListener.onDetections(currentJobId, - currentDeviceId, - currentDetections, + detectionListener.onDetections(currentJobId, + currentDeviceId, + currentDetections, currentTime); } - - log.debug("检测完成,发现 {} 个目标,框将保持3秒", + + log.debug("检测完成,发现 {} 个目标,框将保持3秒", currentDetections == null ? 0 : currentDetections.size()); } catch (Exception e) { log.debug("检测异常: {}", e.getMessage()); } } - + // 每一帧都使用最新的检测结果绘制框 // 这样框会保持在原位置,直到下一次检测更新 if (currentDetections != null && !currentDetections.isEmpty()) { @@ -447,24 +447,24 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable log.debug("绘制检测框异常: {}", e.getMessage()); } } - + // 更新"最近叠好框的帧"用于存证 updateLatestAnnotated(mat); - + // 统计(仅窗口巡检时) if (windowMode) updateStats(currentDetections); - + // 窗口结束判定 if (windowMode && System.currentTimeMillis() >= windowEndMs) { finishWindow(); } - + // 将处理后的Mat转换回Frame try { // 创建新的转换器 OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat(); Frame processedFrame = converter.convert(mat); - + if (processedFrame != null) { // 使用处理后的帧替换原始帧 frame = processedFrame; @@ -473,7 +473,7 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable log.debug("Mat转Frame异常: {}", e.getMessage()); // 如果转换失败,继续使用原始帧 } - + // 释放Mat mat.release(); } diff --git a/ruoyi-video/src/main/java/com/ruoyi/video/thread/detector/OpenVinoYoloDetector.java b/ruoyi-video/src/main/java/com/ruoyi/video/thread/detector/OpenVinoYoloDetector.java index 855e61b..c7ea03e 100644 --- a/ruoyi-video/src/main/java/com/ruoyi/video/thread/detector/OpenVinoYoloDetector.java +++ b/ruoyi-video/src/main/java/com/ruoyi/video/thread/detector/OpenVinoYoloDetector.java @@ -25,15 +25,9 @@ public final class OpenVinoYoloDetector implements YoloDetector { this.input = new Size(inW, inH); this.colorBGR = colorBGR; - // 自动查找模型文件 - String xml = findModelFile(dir, ".xml"); - String bin = findModelFile(dir, ".bin"); + String xml = dir.resolve("model.xml").toString(); + String bin = dir.resolve("model.bin").toString(); - if (xml == null || bin == null) { - throw new Exception("找不到模型文件,请确保目录中存在 .xml 和 .bin 文件: " + dir); - } - - // 读取类别文件 Path clsPath = dir.resolve("classes.txt"); if (Files.exists(clsPath)) { this.classes = Files.readAllLines(clsPath).stream().map(String::trim) @@ -42,34 +36,19 @@ public final class OpenVinoYoloDetector implements YoloDetector { this.classes = new String[0]; } - try { - // 加载模型,但强制使用OpenCV后端 - this.net = readNetFromModelOptimizer(xml, bin); - - // 强制使用OpenCV后端,避免OpenVINO依赖 + this.net = readNetFromModelOptimizer(xml, bin); + + boolean set = false; + if ("openvino".equalsIgnoreCase(backend)) { + try { + net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE); + net.setPreferableTarget(DNN_TARGET_CPU); + set = true; + } catch (Throwable ignore) { /* 回退 */ } + } + if (!set) { net.setPreferableBackend(DNN_BACKEND_OPENCV); net.setPreferableTarget(DNN_TARGET_CPU); - - System.out.println("模型加载成功: " + name + " (使用OpenCV后端)"); - - } catch (Exception e) { - throw new Exception("模型加载失败: " + e.getMessage() + - "\n请确保模型文件完整且格式正确", e); - } - } - - /** - * 在目录中查找指定扩展名的模型文件 - */ - private String findModelFile(Path dir, String extension) { - try { - return Files.list(dir) - .filter(path -> path.toString().toLowerCase().endsWith(extension.toLowerCase())) - .map(Path::toString) - .findFirst() - .orElse(null); - } catch (Exception e) { - return null; } } @@ -248,4 +227,4 @@ public final class OpenVinoYoloDetector implements YoloDetector { } @Override public void close(){ net.close(); } -} \ No newline at end of file +}