diff --git a/README.md b/README.md
index 7241f3e..f9818c6 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,3 @@
-
-
-
-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/pom.xml b/pom.xml
index c20c466..11c4a70 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,7 +42,7 @@
org.bytedeco
javacv-platform
- 1.5.10
+ 1.5.12
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index ada44c2..136c8a7 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/pom.xml b/ruoyi-video/pom.xml
index f6dd5be..885f9d2 100644
--- a/ruoyi-video/pom.xml
+++ b/ruoyi-video/pom.xml
@@ -35,7 +35,12 @@
org.bytedeco
javacv-platform
- 1.5.11
+ 1.5.12
+
+
+ org.bytedeco
+ opencv-platform
+ 4.11.0-1.5.12
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/OnnxYoloDetector.java b/ruoyi-video/src/main/java/com/ruoyi/video/thread/detector/OnnxYoloDetector.java
index 44156ed..9ece6c7 100644
--- a/ruoyi-video/src/main/java/com/ruoyi/video/thread/detector/OnnxYoloDetector.java
+++ b/ruoyi-video/src/main/java/com/ruoyi/video/thread/detector/OnnxYoloDetector.java
@@ -39,7 +39,7 @@ public final class OnnxYoloDetector implements YoloDetector {
} else {
this.classes = new String[0];
}
-
+ System.out.println("CV_VERSION = " + CV_VERSION);
try {
// 加载ONNX模型
this.net = readNetFromONNX(onnx);
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
+}