|
@@ -15,7 +15,7 @@ import numpy as np
|
|
|
import cv2
|
|
import cv2
|
|
|
|
|
|
|
|
from config import COORDINATOR_CONFIG, SYSTEM_CONFIG, PTZ_CONFIG, DETECTION_CONFIG
|
|
from config import COORDINATOR_CONFIG, SYSTEM_CONFIG, PTZ_CONFIG, DETECTION_CONFIG
|
|
|
-from panorama_camera import PanoramaCamera, ObjectDetector, PersonTracker, DetectedObject
|
|
|
|
|
|
|
+from panorama_camera import PanoramaCamera, ObjectDetector, DetectedObject
|
|
|
from ptz_camera import PTZCamera, PTZController
|
|
from ptz_camera import PTZCamera, PTZController
|
|
|
from ocr_recognizer import NumberDetector, PersonInfo
|
|
from ocr_recognizer import NumberDetector, PersonInfo
|
|
|
from ptz_person_tracker import PTZPersonDetector, PTZAutoZoomController
|
|
from ptz_person_tracker import PTZPersonDetector, PTZAutoZoomController
|
|
@@ -238,9 +238,6 @@ class Coordinator:
|
|
|
self.ptz_detector = None
|
|
self.ptz_detector = None
|
|
|
self.auto_zoom_controller = None
|
|
self.auto_zoom_controller = None
|
|
|
|
|
|
|
|
- # 跟踪器
|
|
|
|
|
- self.tracker = PersonTracker()
|
|
|
|
|
-
|
|
|
|
|
# 状态
|
|
# 状态
|
|
|
self.state = TrackingState.IDLE
|
|
self.state = TrackingState.IDLE
|
|
|
self.state_lock = threading.Lock()
|
|
self.state_lock = threading.Lock()
|
|
@@ -430,15 +427,16 @@ class Coordinator:
|
|
|
if detections:
|
|
if detections:
|
|
|
self._update_stats('persons_detected', len(detections))
|
|
self._update_stats('persons_detected', len(detections))
|
|
|
|
|
|
|
|
- # 更新跟踪
|
|
|
|
|
- tracked = self.tracker.update(detections)
|
|
|
|
|
|
|
+ # 为检测结果分配临时序号
|
|
|
|
|
+ for idx, det in enumerate(detections):
|
|
|
|
|
+ det.track_id = idx
|
|
|
|
|
|
|
|
# 更新跟踪目标
|
|
# 更新跟踪目标
|
|
|
- self._update_tracking_targets(tracked, frame_size)
|
|
|
|
|
|
|
+ self._update_tracking_targets(detections, frame_size)
|
|
|
|
|
|
|
|
# 处理检测结果
|
|
# 处理检测结果
|
|
|
- if tracked:
|
|
|
|
|
- self._process_detections(tracked, frame, frame_size)
|
|
|
|
|
|
|
+ if detections:
|
|
|
|
|
+ self._process_detections(detections, frame, frame_size)
|
|
|
|
|
|
|
|
# 处理当前跟踪目标
|
|
# 处理当前跟踪目标
|
|
|
self._process_current_target(frame, frame_size)
|
|
self._process_current_target(frame, frame_size)
|
|
@@ -716,8 +714,10 @@ class EventDrivenCoordinator(Coordinator):
|
|
|
frame_size = (frame.shape[1], frame.shape[0])
|
|
frame_size = (frame.shape[1], frame.shape[0])
|
|
|
detections = self._detect_persons(frame)
|
|
detections = self._detect_persons(frame)
|
|
|
if detections:
|
|
if detections:
|
|
|
- tracked = self.tracker.update(detections)
|
|
|
|
|
- self._update_tracking_targets(tracked, frame_size)
|
|
|
|
|
|
|
+ # 为检测结果分配临时序号
|
|
|
|
|
+ for idx, det in enumerate(detections):
|
|
|
|
|
+ det.track_id = idx
|
|
|
|
|
+ self._update_tracking_targets(detections, frame_size)
|
|
|
self._process_current_target(frame, frame_size)
|
|
self._process_current_target(frame, frame_size)
|
|
|
|
|
|
|
|
self._cleanup_expired_targets()
|
|
self._cleanup_expired_targets()
|
|
@@ -944,20 +944,21 @@ class AsyncCoordinator(Coordinator):
|
|
|
self._update_stats('persons_detected', len(detections))
|
|
self._update_stats('persons_detected', len(detections))
|
|
|
detection_person_count += 1
|
|
detection_person_count += 1
|
|
|
|
|
|
|
|
- # 更新跟踪
|
|
|
|
|
- tracked = self.tracker.update(detections)
|
|
|
|
|
- self._update_tracking_targets(tracked, frame_size)
|
|
|
|
|
|
|
+ # 为检测结果分配临时序号
|
|
|
|
|
+ for idx, det in enumerate(detections):
|
|
|
|
|
+ det.track_id = idx
|
|
|
|
|
+ self._update_tracking_targets(detections, frame_size)
|
|
|
|
|
|
|
|
# 配对图片保存:创建新批次
|
|
# 配对图片保存:创建新批次
|
|
|
- if tracked and self._enable_paired_saving and self._paired_saver is not None:
|
|
|
|
|
- self._create_detection_batch(frame, tracked, frame_size)
|
|
|
|
|
|
|
+ if detections and self._enable_paired_saving and self._paired_saver is not None:
|
|
|
|
|
+ self._create_detection_batch(frame, detections, frame_size)
|
|
|
|
|
|
|
|
# 打印检测日志(使用连续序号,与图片标记一致)
|
|
# 打印检测日志(使用连续序号,与图片标记一致)
|
|
|
- if tracked:
|
|
|
|
|
|
|
+ if detections:
|
|
|
person_threshold = DETECTION_CONFIG.get('person_threshold', 0.8)
|
|
person_threshold = DETECTION_CONFIG.get('person_threshold', 0.8)
|
|
|
person_idx = 0
|
|
person_idx = 0
|
|
|
- for t in tracked:
|
|
|
|
|
- # tracked 是 DetectedObject,使用 center 计算位置
|
|
|
|
|
|
|
+ for t in detections:
|
|
|
|
|
+ # detections 是 DetectedObject,使用 center 计算位置
|
|
|
x_ratio = t.center[0] / frame_size[0]
|
|
x_ratio = t.center[0] / frame_size[0]
|
|
|
y_ratio = t.center[1] / frame_size[1]
|
|
y_ratio = t.center[1] / frame_size[1]
|
|
|
_, _, w, h = t.bbox
|
|
_, _, w, h = t.bbox
|
|
@@ -976,10 +977,6 @@ class AsyncCoordinator(Coordinator):
|
|
|
f"位置=({x_ratio:.3f}, {y_ratio:.3f}) "
|
|
f"位置=({x_ratio:.3f}, {y_ratio:.3f}) "
|
|
|
f"置信度={t.confidence:.2f}(低于阈值{person_threshold})"
|
|
f"置信度={t.confidence:.2f}(低于阈值{person_threshold})"
|
|
|
)
|
|
)
|
|
|
- elif detections:
|
|
|
|
|
- # 有检测但没跟踪上
|
|
|
|
|
- for d in detections:
|
|
|
|
|
- logger.debug(f"[检测] 未跟踪: {d.class_name} @ {d.center}")
|
|
|
|
|
else:
|
|
else:
|
|
|
if current_time - last_no_detect_log_time >= no_detect_log_interval:
|
|
if current_time - last_no_detect_log_time >= no_detect_log_interval:
|
|
|
logger.info(
|
|
logger.info(
|
|
@@ -988,14 +985,14 @@ class AsyncCoordinator(Coordinator):
|
|
|
)
|
|
)
|
|
|
last_no_detect_log_time = current_time
|
|
last_no_detect_log_time = current_time
|
|
|
|
|
|
|
|
- if tracked:
|
|
|
|
|
- self._process_detections(tracked, frame, frame_size)
|
|
|
|
|
|
|
+ if detections:
|
|
|
|
|
+ self._process_detections(detections, frame, frame_size)
|
|
|
|
|
|
|
|
# 选择跟踪目标并发送PTZ命令
|
|
# 选择跟踪目标并发送PTZ命令
|
|
|
target = self._select_tracking_target()
|
|
target = self._select_tracking_target()
|
|
|
if target and self.enable_ptz_tracking and self.enable_ptz_camera:
|
|
if target and self.enable_ptz_tracking and self.enable_ptz_camera:
|
|
|
self._send_ptz_command_with_log(target, frame_size)
|
|
self._send_ptz_command_with_log(target, frame_size)
|
|
|
- elif not tracked and self.current_target:
|
|
|
|
|
|
|
+ elif not detections and self.current_target:
|
|
|
# 目标消失,切回IDLE
|
|
# 目标消失,切回IDLE
|
|
|
self._set_state(TrackingState.IDLE)
|
|
self._set_state(TrackingState.IDLE)
|
|
|
logger.info("[检测] 目标丢失,球机进入IDLE状态")
|
|
logger.info("[检测] 目标丢失,球机进入IDLE状态")
|
|
@@ -1035,14 +1032,14 @@ class AsyncCoordinator(Coordinator):
|
|
|
self.enable_ptz_detection = False
|
|
self.enable_ptz_detection = False
|
|
|
|
|
|
|
|
def _create_detection_batch(self, frame: np.ndarray,
|
|
def _create_detection_batch(self, frame: np.ndarray,
|
|
|
- tracked: List[DetectedObject],
|
|
|
|
|
|
|
+ detections: List[DetectedObject],
|
|
|
frame_size: Tuple[int, int]):
|
|
frame_size: Tuple[int, int]):
|
|
|
"""
|
|
"""
|
|
|
创建检测批次,用于配对图片保存
|
|
创建检测批次,用于配对图片保存
|
|
|
|
|
|
|
|
Args:
|
|
Args:
|
|
|
frame: 全景帧
|
|
frame: 全景帧
|
|
|
- tracked: 跟踪到的人员列表
|
|
|
|
|
|
|
+ detections: 检测到的人员列表
|
|
|
frame_size: 帧尺寸
|
|
frame_size: 帧尺寸
|
|
|
"""
|
|
"""
|
|
|
if self._paired_saver is None:
|
|
if self._paired_saver is None:
|
|
@@ -1051,7 +1048,7 @@ class AsyncCoordinator(Coordinator):
|
|
|
# 过滤有效人员(必须是 person 且置信度 >= 阈值)
|
|
# 过滤有效人员(必须是 person 且置信度 >= 阈值)
|
|
|
person_threshold = DETECTION_CONFIG.get('person_threshold', 0.8)
|
|
person_threshold = DETECTION_CONFIG.get('person_threshold', 0.8)
|
|
|
valid_persons = []
|
|
valid_persons = []
|
|
|
- for det in tracked:
|
|
|
|
|
|
|
+ for det in detections:
|
|
|
# 只处理 class_name 为 person 的目标,排除安全帽、反光衣等
|
|
# 只处理 class_name 为 person 的目标,排除安全帽、反光衣等
|
|
|
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)
|
|
@@ -1083,7 +1080,7 @@ class AsyncCoordinator(Coordinator):
|
|
|
batch_id = self._paired_saver.start_new_batch(frame, persons)
|
|
batch_id = self._paired_saver.start_new_batch(frame, persons)
|
|
|
if batch_id:
|
|
if batch_id:
|
|
|
self._current_batch_id = batch_id
|
|
self._current_batch_id = batch_id
|
|
|
- logger.info(f"[配对保存] 创建批次: {batch_id}, 有效人员={len(persons)}/{len(tracked)}")
|
|
|
|
|
|
|
+ logger.info(f"[配对保存] 创建批次: {batch_id}, 有效人员={len(persons)}/{len(detections)}")
|
|
|
|
|
|
|
|
def _save_ptz_image_for_person(self, track_id: int,
|
|
def _save_ptz_image_for_person(self, track_id: int,
|
|
|
ptz_frame: np.ndarray,
|
|
ptz_frame: np.ndarray,
|