소스 검색

refactor(coordinator): 重构联动控制器以实现异步多线程和目标管理

- 采用检测线程与PTZ控制线程分离,提高系统响应性能
- 增加多目标跟踪与粘性跟踪机制,避免频繁切换目标
- 实现目标去重算法,合并重叠检测框提高识别准确度
- 引入目标选择器支持多策略目标排序和优先级控制
- 支持配对图片保存功能,增强数据管理和分析能力
- 改进PTZ控制,增加位置变化阈值判断减少不必要移动
- 优化OCR频率控制,防止过多重复识别调用
- 增加事件驱动模式,支持基于事件触发的联动追踪
- 完善统计信息采集与日志记录,提升系统可观测性
- 添加异常处理保证主工作线程稳定运行
wenhongquan 2 일 전
부모
커밋
cf5bb3eb09
2개의 변경된 파일83개의 추가작업 그리고 15개의 파일을 삭제
  1. BIN
      dual_camera_system/__pycache__/coordinator.cpython-310.pyc
  2. 83 15
      dual_camera_system/coordinator.py

BIN
dual_camera_system/__pycache__/coordinator.cpython-310.pyc


+ 83 - 15
dual_camera_system/coordinator.py

@@ -1102,9 +1102,57 @@ class AsyncCoordinator(Coordinator):
             print(f"[AsyncCoordinator] 球机端检测器初始化失败: {e}")
             self.enable_ptz_detection = False
 
+    def _deduplicate_detections(self, detections: List[DetectedObject], 
+                                frame_size: Tuple[int, int]) -> List[DetectedObject]:
+        """
+        去重检测结果(按位置合并重叠的检测框)
+        
+        Args:
+            detections: 检测列表
+            frame_size: 帧尺寸
+            
+        Returns:
+            去重后的人员检测列表
+        """
+        # 过滤有效人员
+        person_threshold = DETECTION_CONFIG.get('person_threshold', 0.5)
+        valid_persons = [d for d in detections 
+                        if d.class_name == 'person' and d.confidence >= person_threshold]
+        
+        if not valid_persons:
+            return []
+        
+        # 去重:按位置合并重叠的检测框
+        DEDUP_DISTANCE = 0.05  # 画面比例 5%
+        dedup_persons = []
+        
+        for det in valid_persons:
+            det_x = det.center[0] / frame_size[0]
+            det_y = det.center[1] / frame_size[1]
+            
+            # 检查是否与已有人员重叠
+            is_duplicate = False
+            for i, existing in enumerate(dedup_persons):
+                ex_x = existing.center[0] / frame_size[0]
+                ex_y = existing.center[1] / frame_size[1]
+                
+                dist = math.sqrt((det_x - ex_x)**2 + (det_y - ex_y)**2)
+                if dist < DEDUP_DISTANCE:
+                    # 重叠,保留置信度更高的
+                    is_duplicate = True
+                    if det.confidence > existing.confidence:
+                        dedup_persons[i] = det
+                    break
+            
+            if not is_duplicate:
+                dedup_persons.append(det)
+        
+        
+        return dedup_persons
+
     def _create_detection_batch(self, frame: np.ndarray, 
                                  detections: List[DetectedObject],
-                                 frame_size: Tuple[int, int]):
+                                 frame_size: Tuple[int, int]) -> List[DetectedObject]:
         """
         创建检测批次,用于配对图片保存
         
@@ -1112,9 +1160,12 @@ class AsyncCoordinator(Coordinator):
             frame: 全景帧
             detections: 检测到的人员列表
             frame_size: 帧尺寸
+            
+        Returns:
+            去重后的人员检测列表
         """
         if self._paired_saver is None:
-            return
+            return []
         
         # 过滤有效人员(必须是 person 且置信度 >= 阈值)
         person_threshold = DETECTION_CONFIG.get('person_threshold', 0.8)
@@ -1152,7 +1203,7 @@ class AsyncCoordinator(Coordinator):
         
         if not dedup_persons:
             logger.debug(f"[配对保存] 无有效人员(阈值={person_threshold}),跳过批次创建")
-            return
+            return []
         
         
         logger.info(f"[配对保存] 检测结果去重: {len(valid_persons)} -> {len(dedup_persons)} 个人员")
@@ -1181,6 +1232,8 @@ class AsyncCoordinator(Coordinator):
         if batch_id:
             self._current_batch_id = batch_id
             logger.info(f"[配对保存] 创建批次: {batch_id}, 有效人员={len(persons)}/{len(detections)}")
+        
+        return dedup_persons
 
     def _save_ptz_image_for_person(self, track_id: int, 
                                     ptz_frame: np.ndarray,
@@ -1699,18 +1752,33 @@ class SequentialCoordinator(AsyncCoordinator):
                                 tracking_count = len(self.tracking_targets)
                             logger.info(f"[顺序模式] 检测到 {len(detections)} 个目标, 跟踪列表 {tracking_count} 个")
                             
-                            # 获取有效目标列表
-                            targets = self._get_all_valid_targets()
+                            # 【关键修复】先创建配对批次并获取去重后的人员列表
+                            dedup_persons = []
+                            if self._enable_paired_saving and self._paired_saver is not None:
+                                dedup_persons = self._create_detection_batch(frame, detections, frame_size)
+                            else:
+                                # 如果未启用配对保存,也需要去重
+                                dedup_persons = self._deduplicate_detections(detections, frame_size)
                             
-                            # 【调试日志】显示有效目标数量
-                            logger.info(f"[顺序模式] 有效目标数量: {len(targets) if targets else 0}")
+                            # 【调试日志】显示去重后目标数量
+                            logger.info(f"[顺序模式] 去重后有效目标数量: {len(dedup_persons)}")
                             
-                            if targets:
-                                logger.info(f"[顺序模式] 检测到 {len(targets)} 个目标,开始顺序抓拍")
+                            if dedup_persons:
+                                # 将去重后的检测结果转换为抓拍目标
+                                capture_targets = []
+                                for i, det in enumerate(dedup_persons):
+                                    x_ratio = det.center[0] / frame_size[0]
+                                    y_ratio = det.center[1] / frame_size[1]
+                                    target = TrackingTarget(
+                                        track_id=det.track_id,
+                                        position=(x_ratio, y_ratio),
+                                        last_update=current_time,
+                                        area=det.bbox[2] * det.bbox[3],
+                                        confidence=det.confidence
+                                    )
+                                    capture_targets.append(target)
                                 
-                                # 创建配对保存批次
-                                if self._enable_paired_saving and self._paired_saver is not None:
-                                    self._create_detection_batch(frame, detections, frame_size)
+                                logger.info(f"[顺序模式] 检测到 {len(capture_targets)} 个目标,开始顺序抓拍")
                                 
                                 # 【关键修复】切换到抓拍状态后立即清空 tracking_targets
                                 # 防止后续检测再次获取到同一批目标
@@ -1718,10 +1786,10 @@ class SequentialCoordinator(AsyncCoordinator):
                                     self.tracking_targets.clear()
                                     logger.info("[顺序模式] 已清空跟踪目标,防止重复抓拍")
                                 
-                                # 切换到抓拍状态
-                                self._start_capture_sequence(targets)
+                                # 切换到抓拍状态(使用去重后的目标)
+                                self._start_capture_sequence(capture_targets)
                             else:
-                                logger.warning(f"[顺序模式] 有效目标为空,跳过抓拍")
+                                logger.warning(f"[顺序模式] 去重后无有效目标,跳过抓拍")
                         else:
                             # 未检测到人员
                             if current_time - last_no_detect_log_time >= no_detect_log_interval: