feat: 新增PaddlePaddle检测支持,重构项目架构

1. 新增concurrently依赖用于并行启动服务
2. 新增服务器启动脚本统一管理环境变量和虚拟环境
3. 新增PaddlePaddle推理引擎和配套工具代码
4. 新增抽烟检测Paddle模型支持,完善模型管理
5. 重构开发启动脚本,优化开发体验
6. 更新.gitignore排除不必要的外部目录和缓存
7. 完善文档说明,新增PaddlePaddle部署指南
This commit is contained in:
wwh
2026-05-21 10:39:26 +08:00
parent 7aa71c5f83
commit e97bd503ec
31 changed files with 8759 additions and 199 deletions

View File

@@ -4,6 +4,7 @@ import numpy as np
import time
import uuid
import logging
import torch
from typing import Dict, List, Optional
from PIL import Image, ImageDraw, ImageFont
@@ -45,19 +46,60 @@ class DetectionService:
try:
results = model(image, conf=confidence, iou=iou, verbose=False)
detections = []
for result in results:
boxes = result.boxes
if len(boxes) == 0:
logger.info(f"模型 {model_id} 没有检测到目标")
continue
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
conf = float(box.conf[0].cpu().numpy())
cls = int(box.cls[0].cpu().numpy())
class_name = result.names[cls]
try:
if isinstance(box.xyxy, torch.Tensor) and box.xyxy.dim() > 0:
x1, y1, x2, y2 = float(box.xyxy[0]), float(box.xyxy[1]), float(box.xyxy[2]), float(box.xyxy[3])
elif isinstance(box.xyxy, (list, tuple)):
x1, y1, x2, y2 = float(box.xyxy[0]), float(box.xyxy[1]), float(box.xyxy[2]), float(box.xyxy[3])
else:
continue
if isinstance(box.conf, torch.Tensor):
if box.conf.dim() == 0:
conf = float(box.conf)
else:
conf = float(box.conf[0])
elif hasattr(box.conf, '__getitem__'):
conf = float(box.conf[0])
else:
conf = float(box.conf)
if isinstance(box.cls, torch.Tensor):
if box.cls.dim() == 0:
cls = int(box.cls)
else:
cls = int(box.cls[0])
elif hasattr(box.cls, '__getitem__'):
cls = int(box.cls[0])
else:
cls = int(box.cls)
except Exception as e:
import traceback
logger.error(f"访问 box 属性失败: {e}, box 类型: {type(box)}")
logger.error(f"错误堆栈: {traceback.format_exc()}")
logger.error(f"box 属性: {vars(box) if hasattr(box, '__dict__') else '无法获取'}")
continue
class_name = result.names[cls]
label_map = self.model_service.model_configs[model_id]['labels']
label = label_map.get(class_name, class_name)
detections.append({
'class': class_name,
'label': label,
@@ -120,21 +162,58 @@ class DetectionService:
detections = []
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
conf = float(box.conf[0].cpu().numpy())
cls = int(box.cls[0].cpu().numpy())
class_name = result.names[cls]
label_map = self.model_service.model_configs[model_id]['labels']
label = label_map.get(class_name, class_name)
detections.append({
'class': class_name,
'label': label,
'confidence': round(conf, 3),
'bbox': [int(x1), int(y1), int(x2), int(y2)]
})
try:
if isinstance(box.xyxy, torch.Tensor) and box.xyxy.dim() > 0:
x1, y1, x2, y2 = float(box.xyxy[0]), float(box.xyxy[1]), float(box.xyxy[2]), float(box.xyxy[3])
elif isinstance(box.xyxy, (list, tuple)):
x1, y1, x2, y2 = float(box.xyxy[0]), float(box.xyxy[1]), float(box.xyxy[2]), float(box.xyxy[3])
else:
continue
if isinstance(box.conf, torch.Tensor):
if box.conf.dim() == 0:
conf = float(box.conf)
else:
conf = float(box.conf[0])
elif hasattr(box.conf, '__getitem__'):
conf = float(box.conf[0])
else:
conf = float(box.conf)
if isinstance(box.cls, torch.Tensor):
if box.cls.dim() == 0:
cls = int(box.cls)
else:
cls = int(box.cls[0])
elif hasattr(box.cls, '__getitem__'):
cls = int(box.cls[0])
else:
cls = int(box.cls)
class_name = result.names[cls]
label_map = self.model_service.model_configs[model_id]['labels']
label = label_map.get(class_name, class_name)
detections.append({
'class': class_name,
'label': label,
'confidence': round(conf, 3),
'bbox': [int(x1), int(y1), int(x2), int(y2)]
})
except Exception as e:
import traceback
logger.error(f"VIDEO DEBUG: 访问 box 属性失败: {e}, box 类型: {type(box)}")
logger.error(f"VIDEO DEBUG: 错误堆栈: {traceback.format_exc()}")
logger.error(f"VIDEO DEBUG: box 属性: {vars(box) if hasattr(box, '__dict__') else '无法获取'}")
continue
processing_time = time.time() - start_time
fps = 1.0 / processing_time if processing_time > 0 else 0