|
@@ -1115,15 +1115,44 @@ class AsyncCoordinator(Coordinator):
|
|
|
if det.class_name == 'person' and det.confidence >= person_threshold:
|
|
if det.class_name == 'person' and det.confidence >= person_threshold:
|
|
|
valid_persons.append(det)
|
|
valid_persons.append(det)
|
|
|
|
|
|
|
|
- if not valid_persons:
|
|
|
|
|
|
|
+ # 【关键修复】去重:按位置合并重叠的检测框
|
|
|
|
|
+ # 如果两个检测框的中心距离小于阈值,只保留置信度更高的
|
|
|
|
|
+ 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)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ if not dedup_persons:
|
|
|
logger.debug(f"[配对保存] 无有效人员(阈值={person_threshold}),跳过批次创建")
|
|
logger.debug(f"[配对保存] 无有效人员(阈值={person_threshold}),跳过批次创建")
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
- # 构建人员信息列表(只包含有效人员)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ logger.info(f"[配对保存] 检测结果去重: {len(valid_persons)} -> {len(dedup_persons)} 个人员")
|
|
|
|
|
+
|
|
|
|
|
+ # 构建人员信息列表(只包含去重后的人员)
|
|
|
persons = []
|
|
persons = []
|
|
|
self._person_ptz_index = {} # 重置索引映射
|
|
self._person_ptz_index = {} # 重置索引映射
|
|
|
|
|
|
|
|
- for i, det in enumerate(valid_persons):
|
|
|
|
|
|
|
+ for i, det in enumerate(dedup_persons):
|
|
|
x_ratio = det.center[0] / frame_size[0]
|
|
x_ratio = det.center[0] / frame_size[0]
|
|
|
y_ratio = det.center[1] / frame_size[1]
|
|
y_ratio = det.center[1] / frame_size[1]
|
|
|
|
|
|
|
@@ -1631,6 +1660,12 @@ class SequentialCoordinator(AsyncCoordinator):
|
|
|
state = self._get_capture_state()
|
|
state = self._get_capture_state()
|
|
|
|
|
|
|
|
if state == 'idle':
|
|
if state == 'idle':
|
|
|
|
|
+ # 【关键修复】每轮检测开始前清空跟踪目标,防止跨帧累积
|
|
|
|
|
+ with self.targets_lock:
|
|
|
|
|
+ if self.tracking_targets:
|
|
|
|
|
+ logger.debug(f"[顺序模式] 清空上一轮跟踪目标: {len(self.tracking_targets)} 个")
|
|
|
|
|
+ self.tracking_targets.clear()
|
|
|
|
|
+
|
|
|
# 空闲状态:周期性检测
|
|
# 空闲状态:周期性检测
|
|
|
if current_time - last_detection_time >= detection_interval:
|
|
if current_time - last_detection_time >= detection_interval:
|
|
|
last_detection_time = current_time
|
|
last_detection_time = current_time
|
|
@@ -1833,6 +1868,11 @@ class SequentialCoordinator(AsyncCoordinator):
|
|
|
with self._batch_targets_lock:
|
|
with self._batch_targets_lock:
|
|
|
self._batch_targets = []
|
|
self._batch_targets = []
|
|
|
self._current_capture_index = 0
|
|
self._current_capture_index = 0
|
|
|
|
|
+
|
|
|
|
|
+ # 【关键修复】清空跟踪目标,防止跨帧累积
|
|
|
|
|
+ with self.targets_lock:
|
|
|
|
|
+ self.tracking_targets.clear()
|
|
|
|
|
+ logger.info("[顺序模式] 已清空跟踪目标列表")
|
|
|
|
|
|
|
|
def _save_local_snapshot(self, frame: np.ndarray, index: int,
|
|
def _save_local_snapshot(self, frame: np.ndarray, index: int,
|
|
|
pan: float, tilt: float, zoom: int):
|
|
pan: float, tilt: float, zoom: int):
|