diff --git a/python-inference-service/app/main.py b/python-inference-service/app/main.py
index 3af65bc..f9e1948 100644
--- a/python-inference-service/app/main.py
+++ b/python-inference-service/app/main.py
@@ -37,7 +37,7 @@ async def startup_event():
model_manager = ModelManager()
# Look for models.json configuration file
- models_json_path = os.getenv("MODELS_JSON", os.path.join(os.path.dirname(__file__), "..", "models", "models.json"))
+ models_json_path = os.getenv("MODELS_JSON", os.path.join(os.path.dirname(__file__), "..", "models.json"))
if os.path.exists(models_json_path):
try:
diff --git a/python-inference-service/models.json b/python-inference-service/models.json
new file mode 100644
index 0000000..b81c848
--- /dev/null
+++ b/python-inference-service/models.json
@@ -0,0 +1,14 @@
+[
+ {
+ "name": "smoke",
+ "path": "models/smoke_model.py",
+ "size": [640, 640],
+ "comment": "烟雾检测模型"
+ },
+ {
+ "name": "garbage",
+ "path": "models/garbage_model.py",
+ "size": [640, 640],
+ "comment": "垃圾检测模型"
+ }
+]
\ No newline at end of file
diff --git a/python-inference-service/models/models.json b/python-inference-service/models/models.json
deleted file mode 100644
index a3eaf3b..0000000
--- a/python-inference-service/models/models.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {
- "name": "yolov8_detector",
- "path": "models/yolov8_model.py",
- "size": [640, 640],
- "comment": "YOLOv8检测模型,确保将训练好的best.pt文件放在models目录下"
- }
-]
\ No newline at end of file
diff --git a/python-inference-service/models/smoke.pt b/python-inference-service/models/smoke.pt
new file mode 100644
index 0000000..ce69aea
Binary files /dev/null and b/python-inference-service/models/smoke.pt differ
diff --git a/python-inference-service/models/smoke_model.py b/python-inference-service/models/smoke_model.py
new file mode 100644
index 0000000..9ab6639
--- /dev/null
+++ b/python-inference-service/models/smoke_model.py
@@ -0,0 +1,207 @@
+import os
+import numpy as np
+import cv2
+from typing import List, Dict, Any
+import torch
+
+class Model:
+ """
+ 垃圾识别模型 - 直接加载 PyTorch 模型文件
+ """
+
+ def __init__(self):
+ """初始化模型"""
+ # 获取当前文件所在目录路径
+ model_dir = os.path.dirname(os.path.abspath(__file__))
+ # 模型文件路径
+ model_path = os.path.join(model_dir, "smoke.pt")
+
+ print(f"正在加载垃圾识别模型: {model_path}")
+
+ # 加载 PyTorch 模型
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+ print(f"使用设备: {self.device}")
+
+ # 使用 YOLOv5 或通用方式加载模型
+ try:
+ # 尝试使用 YOLOv5 加载
+ import sys
+ sys.path.append(os.path.dirname(model_dir)) # 添加父目录到路径
+
+ try:
+ # 方法1: 如果安装了 YOLOv5
+ import yolov5
+ self.model = yolov5.load(model_path, device=self.device)
+ self.yolov5_api = True
+ print("使用 YOLOv5 包加载模型")
+ except (ImportError, ModuleNotFoundError):
+ # 方法2: 直接加载 YOLO 代码
+ from models.yolov5_utils import attempt_load
+ self.model = attempt_load(model_path, device=self.device)
+ self.yolov5_api = False
+ print("使用内置 YOLOv5 工具加载模型")
+
+ except Exception as e:
+ # 方法3: 通用 PyTorch 加载
+ print(f"YOLOv5 加载失败: {e}")
+ print("使用通用 PyTorch 加载")
+ self.model = torch.load(model_path, map_location=self.device)
+ if isinstance(self.model, dict) and 'model' in self.model:
+ self.model = self.model['model']
+ self.yolov5_api = False
+
+ # 如果是 ScriptModule,设置为评估模式
+ if isinstance(self.model, torch.jit.ScriptModule):
+ self.model.eval()
+ elif hasattr(self.model, 'eval'):
+ self.model.eval()
+
+ # 加载类别名称
+ self.classes = []
+ classes_path = os.path.join(model_dir, "classes.txt")
+ if os.path.exists(classes_path):
+ with open(classes_path, 'r', encoding='utf-8') as f:
+ self.classes = [line.strip() for line in f.readlines() if line.strip()]
+ print(f"已加载 {len(self.classes)} 个类别")
+ else:
+ # 如果模型自带类别信息
+ if hasattr(self.model, 'names') and self.model.names:
+ self.classes = self.model.names
+ print(f"使用模型自带类别,共 {len(self.classes)} 个类别")
+ else:
+ print("未找到类别文件,将使用数字索引作为类别名")
+
+ # 设置识别参数
+ self.conf_threshold = 0.25 # 置信度阈值
+ self.img_size = 640 # 默认输入图像大小
+
+ print("垃圾识别模型加载完成")
+
+ def preprocess(self, image: np.ndarray) -> np.ndarray:
+ """预处理图像"""
+ # 如果是使用 YOLOv5 API,不需要预处理
+ if hasattr(self, 'yolov5_api') and self.yolov5_api:
+ return image
+
+ # 默认预处理:调整大小并归一化
+ img = cv2.resize(image, (self.img_size, self.img_size))
+
+ # BGR 转 RGB
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
+
+ # 归一化 [0, 255] -> [0, 1]
+ img = img / 255.0
+
+ # HWC -> CHW (高度,宽度,通道 -> 通道,高度,宽度)
+ img = img.transpose(2, 0, 1)
+
+ # 转为 torch tensor
+ img = torch.from_numpy(img).float()
+
+ # 添加批次维度
+ img = img.unsqueeze(0)
+
+ # 移至设备
+ img = img.to(self.device)
+
+ return img
+
+ def predict(self, image: np.ndarray) -> List[Dict[str, Any]]:
+ """模型推理"""
+ original_height, original_width = image.shape[:2]
+
+ try:
+ # 如果使用 YOLOv5 API
+ if hasattr(self, 'yolov5_api') and self.yolov5_api:
+ # YOLOv5 API 直接处理图像
+ results = self.model(image)
+
+ # 提取检测结果
+ predictions = results.pred[0] # 第一批次的预测
+
+ detections = []
+ for *xyxy, conf, cls_id in predictions.cpu().numpy():
+ x1, y1, x2, y2 = xyxy
+
+ # 转换为归一化坐标 (x, y, w, h)
+ x = x1 / original_width
+ y = y1 / original_height
+ w = (x2 - x1) / original_width
+ h = (y2 - y1) / original_height
+
+ # 整数类别 ID
+ cls_id = int(cls_id)
+
+ # 获取类别名称
+ class_name = f"cls{cls_id}"
+ if 0 <= cls_id < len(self.classes):
+ class_name = self.classes[cls_id]
+
+ # 添加检测结果
+ if conf >= self.conf_threshold:
+ detections.append({
+ 'bbox': (x, y, w, h),
+ 'class_id': cls_id,
+ 'confidence': float(conf)
+ })
+
+ return detections
+
+ else:
+ # 通用 PyTorch 模型处理
+ # 预处理图像
+ img = self.preprocess(image)
+
+ # 推理
+ with torch.no_grad():
+ outputs = self.model(img)
+
+ # 后处理结果(这里需要根据模型输出格式调整)
+ detections = []
+
+ # 假设输出格式是 YOLO 风格:[batch_idx, x1, y1, x2, y2, conf, cls_id]
+ if isinstance(outputs, torch.Tensor) and outputs.dim() == 2 and outputs.size(1) >= 6:
+ for *xyxy, conf, cls_id in outputs.cpu().numpy():
+ if conf >= self.conf_threshold:
+ x1, y1, x2, y2 = xyxy
+
+ # 转换为归一化坐标 (x, y, w, h)
+ x = x1 / original_width
+ y = y1 / original_height
+ w = (x2 - x1) / original_width
+ h = (y2 - y1) / original_height
+
+ # 整数类别 ID
+ cls_id = int(cls_id)
+
+ detections.append({
+ 'bbox': (x, y, w, h),
+ 'class_id': cls_id,
+ 'confidence': float(conf)
+ })
+ # 处理其他可能的输出格式
+ else:
+ # 这里需要根据模型的实际输出格式进行适配
+ print("警告:无法识别的模型输出格式,请检查模型类型")
+
+ return detections
+
+ except Exception as e:
+ print(f"推理过程中出错: {str(e)}")
+ # 出错时返回空结果
+ return []
+
+ @property
+ def applies_nms(self) -> bool:
+ """模型是否内部应用了 NMS"""
+ # YOLOv5 会自动应用 NMS
+ return True
+
+ def close(self):
+ """释放资源"""
+ if hasattr(self, 'model'):
+ # 删除模型以释放 GPU 内存
+ del self.model
+ if torch.cuda.is_available():
+ torch.cuda.empty_cache()
+ print("垃圾识别模型已关闭")
\ No newline at end of file
diff --git a/rtsp-vue/src/views/video/model/dict.vue b/rtsp-vue/src/views/video/model/dict.vue
new file mode 100644
index 0000000..4084aeb
--- /dev/null
+++ b/rtsp-vue/src/views/video/model/dict.vue
@@ -0,0 +1,341 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+ {{ scope.row.dictLabel }}
+ {{ scope.row.dictLabel }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+