Ver código fonte

refactor(panorama_camera): 移除人体跟踪相关代码,优化全景摄像头模块

- 删除了 PersonTracker 类及相关跟踪逻辑
- 保持全景摄像头视频流获取与物体检测功能完整
- 简化代码结构,便于后续维护和扩展
- 保持对视频流的断线重连和异常处理机制
- 保留检测器接口和检测物体功能实现
wenhongquan 3 dias atrás
pai
commit
97c4f2c0fa

+ 9 - 9
dual_camera_system/config/detection.py

@@ -4,13 +4,13 @@
 
 # 检测配置
 DETECTION_CONFIG = {
-    'target_classes': ['person', '人'],   # 检测目标类别 (支持中英文)
+    'target_classes': ['person'],   # 检测目标类别 (支持中英文)
     'confidence_threshold': 0.5,     # 置信度阈值
     'detection_fps': 2,              # 检测帧率(每秒检测帧数),替代原来的detection_interval
     'detection_interval': 0.5,       # 兼容保留:检测间隔(秒),当detection_fps=2时间隔为0.5秒
     
     # 检测图片保存配置
-    'save_detection_image': False,   # 是否保存检测到人的图片
+    'save_detection_image': True,   # 是否保存检测到人的图片
     'detection_image_dir': '/home/admin/dsh/detection_images',  # 图片保存目录
     
     # RK3588 平台使用 RKNN 安全检测模型 (包含人体检测)
@@ -21,9 +21,9 @@ DETECTION_CONFIG = {
     
     # 安全检测模型的类别映射
     'class_map': {
-        0: '安全帽',
-        3: '',
-        4: '反光衣'
+        0: 'hat',
+        3: 'person',
+        4: 'reflective'
     },
     'person_class_id': 3,           # 人员在模型中的类别ID
     'person_threshold': 0.8,        # 人员检测置信度阈值
@@ -44,9 +44,9 @@ SAFETY_DETECTION_CONFIG = {
     
     # 检测类别映射
     'class_map': {
-        0: '安全帽',
-        3: '',
-        4: '反光衣'
+        0: 'hat',
+        3: 'person',
+        4: 'reflective'
     },
     
     # 检测帧率配置
@@ -58,7 +58,7 @@ SAFETY_DETECTION_CONFIG = {
     'max_alerts_per_minute': 10,     # 每分钟最大告警数
     
     # 检测图片保存配置
-    'save_detection_image': False,    # 是否保存检测到人的图片
+    'save_detection_image': True,     # 是否保存检测到人的图片
     'detection_image_dir': '/home/admin/dsh/detection_images',  # 图片保存目录
     'detection_image_max_count': 1000,  # 最大保存图片数量,超过后自动清理旧图片
 }

+ 1 - 1
dual_camera_system/config/system.py

@@ -17,7 +17,7 @@ SYSTEM_CONFIG = {
     
     # 检测模块
     'enable_detection': True,            # 启用人体检测 (YOLO)
-    'enable_safety_detection': True,     # 启用安全检测 (安全帽/反光衣)
+    'enable_safety_detection': False,     # 启用安全检测 (安全帽/反光衣)
     
     # 联动与校准
     'enable_calibration': True,          # 启用自动校准

+ 22 - 17
dual_camera_system/panorama_camera.py

@@ -540,7 +540,7 @@ class ObjectDetector:
     
     def _save_detection_image(self, frame: np.ndarray, detections: List[DetectedObject]):
         """
-        保存带有检测标记的图片
+        保存带有检测标记的图片(标记人体边界框和序号)
         Args:
             frame: 原始图像
             detections: 检测结果列表
@@ -557,30 +557,35 @@ class ObjectDetector:
             # 复制图像避免修改原图
             marked_frame = frame.copy()
             
-            # 绘制检测结果
-            for det in detections:
+            # 绘制检测结果(带序号)
+            for idx, det in enumerate(detections, 1):
                 x, y, w, h = det.bbox
-                # 绘制边界框(绿色)
-                cv2.rectangle(marked_frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                 
-                # 绘制标签背景
-                label = f"{det.class_name}: {det.confidence:.2f}"
+                # 根据类别选择颜色:人=绿色,其他=蓝色
+                is_person = det.class_name in ['person']
+                box_color = (0, 255, 0) if is_person else (255, 165, 0)  # 绿色/橙色
+                
+                # 绘制边界框
+                cv2.rectangle(marked_frame, (x, y), (x + w, y + h), box_color, 2)
+                
+                # 绘制序号标签(大号白色背景)
+                label = f"#{det.class_name}_{idx}"
                 (label_w, label_h), baseline = cv2.getTextSize(
-                    label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2
+                    label, cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2
                 )
                 cv2.rectangle(
                     marked_frame, 
-                    (x, y - label_h - 10), 
-                    (x + label_w, y), 
-                    (0, 255, 0), 
+                    (x, y - label_h - 8),
+                    (x + label_w, y),
+                    box_color, 
                     -1
                 )
                 
                 # 绘制标签文字(黑色)
                 cv2.putText(
                     marked_frame, label,
-                    (x, y - 5),
-                    cv2.FONT_HERSHEY_SIMPLEX, 0.6,
+                    (x, y - 4),
+                    cv2.FONT_HERSHEY_SIMPLEX, 0.8,
                     (0, 0, 0), 2
                 )
                 
@@ -588,22 +593,22 @@ class ObjectDetector:
                 cv2.circle(marked_frame, det.center, 5, (0, 0, 255), -1)
             
             
-            # 生成文件名(时间戳)
+            # 生成文件名(时间戳+人数
             timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
-            filename = f"detection_{timestamp}.jpg"
+            filename = f"panorama_{timestamp}_n{len(detections)}.jpg"
             filepath = self._image_save_dir / filename
             
             # 保存图片
             cv2.imwrite(str(filepath), marked_frame, [cv2.IMWRITE_JPEG_QUALITY, 90])
             self._last_save_time = current_time
             
-            logger.info(f"已保存检测图片: {filepath},检测到 {len(detections)} 个目标")
+            logger.info(f"[全景] 已保存检测图片: {filepath},检测到 {len(detections)} ")
             
             # 定期清理旧图片
             self._cleanup_old_images()
             
         except Exception as e:
-            logger.error(f"保存检测图片失败: {e}")
+            logger.error(f"[全景] 保存检测图片失败: {e}")
     
     def _letterbox(self, image, size=(640, 640)):
         """Letterbox 预处理"""

+ 97 - 5
dual_camera_system/ptz_person_tracker.py

@@ -6,8 +6,13 @@
 import time
 import cv2
 import numpy as np
+import logging
 from typing import Optional, List, Tuple, Dict
 from dataclasses import dataclass
+from pathlib import Path
+from datetime import datetime
+
+logger = logging.getLogger(__name__)
 
 try:
     from ultralytics import YOLO
@@ -60,7 +65,8 @@ class PTZPersonDetector:
     """
     
     def __init__(self, model_path: str = None, model_type: str = 'auto',
-                 confidence_threshold: float = 0.5, use_gpu: bool = False):
+                 confidence_threshold: float = 0.5, use_gpu: bool = False,
+                 save_image: bool = True, image_dir: str = '/home/admin/dsh/ptz_detection_images'):
         """
         初始化检测器
         Args:
@@ -68,6 +74,8 @@ class PTZPersonDetector:
             model_type: 模型类型 ('yolo', 'rknn', 'auto')
             confidence_threshold: 置信度阈值
             use_gpu: 是否使用GPU
+            save_image: 是否保存检测图片
+            image_dir: 图片保存目录
         """
         self.model_path = model_path
         self.model_type = model_type
@@ -76,9 +84,27 @@ class PTZPersonDetector:
         self.model = None
         self.person_class_id = 0  # YOLO默认person类别ID
         
+        # 图片保存配置
+        self._save_image_enabled = save_image
+        self._image_save_dir = Path(image_dir)
+        self._last_save_time = 0
+        self._save_interval = 0.5  # 最小保存间隔(秒)
+        
+        if self._save_image_enabled:
+            self._ensure_save_dir()
+        
         if model_path:
             self._load_model(model_path, model_type)
     
+    def _ensure_save_dir(self):
+        """确保保存目录存在"""
+        try:
+            self._image_save_dir.mkdir(parents=True, exist_ok=True)
+            logger.info(f"[球机] 检测图片保存目录: {self._image_save_dir}")
+        except Exception as e:
+            logger.error(f"[球机] 创建检测图片目录失败: {e}")
+            self._save_image_enabled = False
+    
     def _load_model(self, model_path: str, model_type: str = 'auto'):
         """加载模型"""
         if model_type == 'auto':
@@ -248,11 +274,77 @@ class PTZPersonDetector:
         return persons
     
     def detect_largest_person(self, frame: np.ndarray) -> Optional[DetectedPerson]:
-        """检测最大的人体"""
+        """检测最大的人体并保存图片"""
         persons = self.detect(frame)
-        if not persons:
-            return None
-        return max(persons, key=lambda p: p.area)
+        if persons:
+            self._save_detection_image(frame, persons)
+            return max(persons, key=lambda p: p.area)
+        return None
+    
+    def _save_detection_image(self, frame: np.ndarray, persons: List[DetectedPerson]):
+        """
+        保存带有检测标记的图片(标记人体边界框和序号)
+        Args:
+            frame: 原始图像
+            persons: 检测到的人体列表
+        """
+        if not self._save_image_enabled or not persons:
+            return
+        
+        # 检查保存间隔
+        current_time = time.time()
+        if current_time - self._last_save_time < self._save_interval:
+            return
+        
+        try:
+            # 复制图像避免修改原图
+            marked_frame = frame.copy()
+            
+            # 绘制检测结果(带序号)
+            for idx, person in enumerate(persons, 1):
+                x1, y1, x2, y2 = person.bbox
+                
+                # 绘制边界框(绿色)
+                cv2.rectangle(marked_frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
+                
+                # 绘制序号标签
+                label = f"#{idx} person"
+                (label_w, label_h), baseline = cv2.getTextSize(
+                    label, cv2.FONT_HERSHEY_SIMPLEX, 0.8, 2
+                )
+                cv2.rectangle(
+                    marked_frame,
+                    (x1, y1 - label_h - 8),
+                    (x1 + label_w, y1),
+                    (0, 255, 0),
+                    -1
+                )
+                
+                # 绘制标签文字(黑色)
+                cv2.putText(
+                    marked_frame, label,
+                    (x1, y1 - 4),
+                    cv2.FONT_HERSHEY_SIMPLEX, 0.8,
+                    (0, 0, 0), 2
+                )
+                
+                # 绘制中心点(红色)
+                cv2.circle(marked_frame, (int(person.center[0]), int(person.center[1])), 5, (0, 0, 255), -1)
+            
+            
+            # 生成文件名(时间戳+人数)
+            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
+            filename = f"ptz_{timestamp}_n{len(persons)}.jpg"
+            filepath = self._image_save_dir / filename
+            
+            # 保存图片
+            cv2.imwrite(str(filepath), marked_frame, [cv2.IMWRITE_JPEG_QUALITY, 90])
+            self._last_save_time = current_time
+            
+            logger.info(f"[球机] 已保存检测图片: {filepath},检测到 {len(persons)} 人")
+            
+        except Exception as e:
+            logger.error(f"[球机] 保存检测图片失败: {e}")
 
 
 class PTZAutoZoomController: