|
@@ -69,19 +69,12 @@ class SafetyCoordinator:
|
|
|
self.running = False
|
|
self.running = False
|
|
|
self.worker_thread = None
|
|
self.worker_thread = None
|
|
|
|
|
|
|
|
- # PTZ跟踪线程(独立于检测线程)
|
|
|
|
|
- self._ptz_thread = None
|
|
|
|
|
- self._ptz_queue: queue.Queue = queue.Queue(maxsize=10)
|
|
|
|
|
- self._ptz_cooldown = 0.15
|
|
|
|
|
- self._last_ptz_time = 0.0
|
|
|
|
|
-
|
|
|
|
|
- # 跟踪状态
|
|
|
|
|
- self.tracks = {}
|
|
|
|
|
- self.next_track_id = 1
|
|
|
|
|
-
|
|
|
|
|
self.alert_records: List[AlertRecord] = []
|
|
self.alert_records: List[AlertRecord] = []
|
|
|
self.alert_cooldown = {}
|
|
self.alert_cooldown = {}
|
|
|
|
|
|
|
|
|
|
+ # 告警冷却时间(按违规类型)
|
|
|
|
|
+ self._violation_cooldown = {}
|
|
|
|
|
+
|
|
|
self.stats = {
|
|
self.stats = {
|
|
|
'frames_processed': 0,
|
|
'frames_processed': 0,
|
|
|
'persons_detected': 0,
|
|
'persons_detected': 0,
|
|
@@ -183,11 +176,7 @@ class SafetyCoordinator:
|
|
|
self.worker_thread = threading.Thread(target=self._worker, daemon=True)
|
|
self.worker_thread = threading.Thread(target=self._worker, daemon=True)
|
|
|
self.worker_thread.start()
|
|
self.worker_thread.start()
|
|
|
|
|
|
|
|
- # 启动 PTZ 跟踪线程(如果 PTZ 可用)
|
|
|
|
|
- if self.ptz and SYSTEM_CONFIG.get('enable_ptz_tracking', True):
|
|
|
|
|
- self._ptz_thread = threading.Thread(target=self._ptz_worker, daemon=True)
|
|
|
|
|
- self._ptz_thread.start()
|
|
|
|
|
- print("[SafetyCoordinator] PTZ跟踪线程已启动")
|
|
|
|
|
|
|
+ # PTZ跟踪已禁用
|
|
|
|
|
|
|
|
with self.stats_lock:
|
|
with self.stats_lock:
|
|
|
self.stats['start_time'] = time.time()
|
|
self.stats['start_time'] = time.time()
|
|
@@ -202,10 +191,7 @@ class SafetyCoordinator:
|
|
|
if self.worker_thread:
|
|
if self.worker_thread:
|
|
|
self.worker_thread.join(timeout=3)
|
|
self.worker_thread.join(timeout=3)
|
|
|
|
|
|
|
|
- # 停止 PTZ 跟踪线程
|
|
|
|
|
- if self._ptz_thread:
|
|
|
|
|
- self._ptz_thread.join(timeout=2)
|
|
|
|
|
- self._ptz_thread = None
|
|
|
|
|
|
|
+ # PTZ跟踪已禁用
|
|
|
|
|
|
|
|
if self.event_pusher:
|
|
if self.event_pusher:
|
|
|
self.event_pusher.stop()
|
|
self.event_pusher.stop()
|
|
@@ -289,7 +275,7 @@ class SafetyCoordinator:
|
|
|
status_list = self.detector.check_safety(frame, detections)
|
|
status_list = self.detector.check_safety(frame, detections)
|
|
|
|
|
|
|
|
self._update_stats('persons_detected', len(status_list))
|
|
self._update_stats('persons_detected', len(status_list))
|
|
|
- self._update_tracks(detections)
|
|
|
|
|
|
|
+ # 轨迹追踪已禁用
|
|
|
|
|
|
|
|
has_violation = False
|
|
has_violation = False
|
|
|
for status in status_list:
|
|
for status in status_list:
|
|
@@ -326,10 +312,7 @@ class SafetyCoordinator:
|
|
|
|
|
|
|
|
self._update_stats('persons_detected', len(status_list))
|
|
self._update_stats('persons_detected', len(status_list))
|
|
|
|
|
|
|
|
- # 更新跟踪
|
|
|
|
|
- self._update_tracks(detections)
|
|
|
|
|
-
|
|
|
|
|
- # 检查违规
|
|
|
|
|
|
|
+ # 检查违规(轨迹追踪已禁用)
|
|
|
for status in status_list:
|
|
for status in status_list:
|
|
|
if status.is_violation:
|
|
if status.is_violation:
|
|
|
self._handle_violation(status, frame)
|
|
self._handle_violation(status, frame)
|
|
@@ -338,75 +321,21 @@ class SafetyCoordinator:
|
|
|
if self.on_frame_processed:
|
|
if self.on_frame_processed:
|
|
|
self.on_frame_processed(frame, detections, status_list)
|
|
self.on_frame_processed(frame, detections, status_list)
|
|
|
|
|
|
|
|
- def _update_tracks(self, detections: List[SafetyDetection]):
|
|
|
|
|
- """更新跟踪状态"""
|
|
|
|
|
- current_time = time.time()
|
|
|
|
|
- persons = [d for d in detections if d.class_id == 3] # 人
|
|
|
|
|
-
|
|
|
|
|
- # 匹配现有跟踪
|
|
|
|
|
- used_ids = set()
|
|
|
|
|
-
|
|
|
|
|
- for person in persons:
|
|
|
|
|
- best_id = None
|
|
|
|
|
- min_dist = float('inf')
|
|
|
|
|
-
|
|
|
|
|
- for track_id, track in self.tracks.items():
|
|
|
|
|
- if track_id in used_ids:
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- dist = np.sqrt(
|
|
|
|
|
- (person.center[0] - track['center'][0])**2 +
|
|
|
|
|
- (person.center[1] - track['center'][1])**2
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- if dist < min_dist and dist < 100: # 距离阈值
|
|
|
|
|
- min_dist = dist
|
|
|
|
|
- best_id = track_id
|
|
|
|
|
-
|
|
|
|
|
- if best_id is not None:
|
|
|
|
|
- # 更新现有跟踪
|
|
|
|
|
- self.tracks[best_id]['center'] = person.center
|
|
|
|
|
- self.tracks[best_id]['last_update'] = current_time
|
|
|
|
|
- person.track_id = best_id
|
|
|
|
|
- used_ids.add(best_id)
|
|
|
|
|
- else:
|
|
|
|
|
- # 新跟踪
|
|
|
|
|
- track_id = self.next_track_id
|
|
|
|
|
- self.next_track_id += 1
|
|
|
|
|
- person.track_id = track_id
|
|
|
|
|
- self.tracks[track_id] = {
|
|
|
|
|
- 'center': person.center,
|
|
|
|
|
- 'last_update': current_time,
|
|
|
|
|
- 'alerts': []
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- def _cleanup_tracks(self):
|
|
|
|
|
- """清理过期跟踪"""
|
|
|
|
|
- current_time = time.time()
|
|
|
|
|
- timeout = COORDINATOR_CONFIG.get('tracking_timeout', 5.0)
|
|
|
|
|
-
|
|
|
|
|
- expired = [
|
|
|
|
|
- tid for tid, t in self.tracks.items()
|
|
|
|
|
- if current_time - t['last_update'] > timeout
|
|
|
|
|
- ]
|
|
|
|
|
-
|
|
|
|
|
- for tid in expired:
|
|
|
|
|
- del self.tracks[tid]
|
|
|
|
|
- self.alert_cooldown.pop(tid, None)
|
|
|
|
|
|
|
+ # 轨迹追踪已禁用 - _update_tracks 和 _cleanup_tracks 方法已移除
|
|
|
|
|
|
|
|
def _handle_violation(self, status: PersonSafetyStatus, frame: np.ndarray):
|
|
def _handle_violation(self, status: PersonSafetyStatus, frame: np.ndarray):
|
|
|
"""处理违规"""
|
|
"""处理违规"""
|
|
|
current_time = time.time()
|
|
current_time = time.time()
|
|
|
- track_id = status.track_id
|
|
|
|
|
|
|
|
|
|
- # 检查冷却时间
|
|
|
|
|
|
|
+ # 检查冷却时间(按违规类型)
|
|
|
|
|
+ violation_key = status.get_violation_desc()
|
|
|
cooldown = SAFETY_DETECTION_CONFIG.get('alert_cooldown', 3.0)
|
|
cooldown = SAFETY_DETECTION_CONFIG.get('alert_cooldown', 3.0)
|
|
|
- if track_id in self.alert_cooldown:
|
|
|
|
|
- if current_time - self.alert_cooldown[track_id] < cooldown:
|
|
|
|
|
|
|
+ if violation_key in self.alert_cooldown:
|
|
|
|
|
+ if current_time - self.alert_cooldown[violation_key] < cooldown:
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
# 记录告警
|
|
# 记录告警
|
|
|
- self.alert_cooldown[track_id] = current_time
|
|
|
|
|
|
|
+ self.alert_cooldown[violation_key] = current_time
|
|
|
|
|
|
|
|
description = status.get_violation_desc()
|
|
description = status.get_violation_desc()
|
|
|
violation_type = status.violation_types[0].value if status.violation_types else "未知"
|
|
violation_type = status.violation_types[0].value if status.violation_types else "未知"
|
|
@@ -421,7 +350,7 @@ class SafetyCoordinator:
|
|
|
person_image = frame[y1:y2, x1:x2].copy()
|
|
person_image = frame[y1:y2, x1:x2].copy()
|
|
|
|
|
|
|
|
record = AlertRecord(
|
|
record = AlertRecord(
|
|
|
- track_id=track_id,
|
|
|
|
|
|
|
+ track_id=0, # 轨迹追踪已禁用
|
|
|
violation_type=violation_type,
|
|
violation_type=violation_type,
|
|
|
description=description,
|
|
description=description,
|
|
|
frame=person_image,
|
|
frame=person_image,
|
|
@@ -431,9 +360,7 @@ class SafetyCoordinator:
|
|
|
self.alert_records.append(record)
|
|
self.alert_records.append(record)
|
|
|
self._update_stats('violations_detected')
|
|
self._update_stats('violations_detected')
|
|
|
|
|
|
|
|
- # PTZ 跟踪违规人员(如果 PTZ 可用且启用)
|
|
|
|
|
- if self.ptz and SYSTEM_CONFIG.get('enable_ptz_tracking', True):
|
|
|
|
|
- self._track_violator_ptz(status, frame)
|
|
|
|
|
|
|
+ # PTZ跟踪已禁用
|
|
|
|
|
|
|
|
# 回调
|
|
# 回调
|
|
|
if self.on_violation_detected:
|
|
if self.on_violation_detected:
|
|
@@ -444,7 +371,7 @@ class SafetyCoordinator:
|
|
|
self.event_pusher.push_safety_violation(
|
|
self.event_pusher.push_safety_violation(
|
|
|
description=description,
|
|
description=description,
|
|
|
image=person_image,
|
|
image=person_image,
|
|
|
- track_id=track_id,
|
|
|
|
|
|
|
+ track_id=0, # 轨迹追踪已禁用
|
|
|
confidence=status.person_conf
|
|
confidence=status.person_conf
|
|
|
)
|
|
)
|
|
|
self._update_stats('events_pushed')
|
|
self._update_stats('events_pushed')
|
|
@@ -454,68 +381,9 @@ class SafetyCoordinator:
|
|
|
self.voice_announcer.announce_violation(description, urgent=True)
|
|
self.voice_announcer.announce_violation(description, urgent=True)
|
|
|
self._update_stats('voice_announced')
|
|
self._update_stats('voice_announced')
|
|
|
|
|
|
|
|
- print(f"[告警] {description}, 跟踪ID: {track_id}")
|
|
|
|
|
-
|
|
|
|
|
- def _track_violator_ptz(self, status: PersonSafetyStatus, frame: np.ndarray):
|
|
|
|
|
- """违规人员PTZ跟踪:将违规人员在全景画面中的位置发送给PTZ线程"""
|
|
|
|
|
- if self.ptz is None:
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- frame_h, frame_w = frame.shape[:2]
|
|
|
|
|
- x1, y1, x2, y2 = status.person_bbox
|
|
|
|
|
-
|
|
|
|
|
- # 计算违规人员在全景画面中的相对位置
|
|
|
|
|
- center_x = (x1 + x2) / 2
|
|
|
|
|
- center_y = (y1 + y2) / 2
|
|
|
|
|
- x_ratio = center_x / frame_w
|
|
|
|
|
- y_ratio = center_y / frame_h
|
|
|
|
|
-
|
|
|
|
|
- # 冷却检查
|
|
|
|
|
- current_time = time.time()
|
|
|
|
|
- if current_time - self._last_ptz_time < self._ptz_cooldown:
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- # 发送PTZ命令
|
|
|
|
|
- try:
|
|
|
|
|
- self._ptz_queue.put_nowait({
|
|
|
|
|
- 'x_ratio': x_ratio,
|
|
|
|
|
- 'y_ratio': y_ratio,
|
|
|
|
|
- 'track_id': status.track_id,
|
|
|
|
|
- 'violation_type': status.violation_types[0].value if status.violation_types else 'unknown'
|
|
|
|
|
- })
|
|
|
|
|
- self._last_ptz_time = current_time
|
|
|
|
|
- self._update_stats('ptz_commands_sent')
|
|
|
|
|
- except queue.Full:
|
|
|
|
|
- pass # 队列满则丢弃,下一个检测周期会重发
|
|
|
|
|
|
|
+ print(f"[告警] {description}")
|
|
|
|
|
|
|
|
- def _ptz_worker(self):
|
|
|
|
|
- """PTZ控制工作线程:独立处理所有PTZ命令"""
|
|
|
|
|
- while self.running:
|
|
|
|
|
- try:
|
|
|
|
|
- try:
|
|
|
|
|
- cmd = self._ptz_queue.get(timeout=0.1)
|
|
|
|
|
- except queue.Empty:
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- if self.ptz is None:
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- x_ratio = cmd['x_ratio']
|
|
|
|
|
- y_ratio = cmd['y_ratio']
|
|
|
|
|
-
|
|
|
|
|
- # 使用校准器转换坐标,或使用估算
|
|
|
|
|
- if self.calibrator and self.calibrator.is_calibrated():
|
|
|
|
|
- pan, tilt = self.calibrator.transform(x_ratio, y_ratio)
|
|
|
|
|
- zoom = self.ptz.ptz_config.get('default_zoom', 8)
|
|
|
|
|
- if self.ptz.ptz_config.get('pan_flip', False):
|
|
|
|
|
- pan = (pan + 180) % 360
|
|
|
|
|
- self.ptz.goto_exact_position(pan, tilt, zoom)
|
|
|
|
|
- else:
|
|
|
|
|
- self.ptz.track_target(x_ratio, y_ratio)
|
|
|
|
|
-
|
|
|
|
|
- except Exception as e:
|
|
|
|
|
- print(f"[SafetyCoordinator] PTZ跟踪错误: {e}")
|
|
|
|
|
- time.sleep(0.05)
|
|
|
|
|
|
|
+ # PTZ跟踪已禁用 - _track_violator_ptz 和 _ptz_worker 方法已移除
|
|
|
|
|
|
|
|
def _set_state(self, state: CoordinatorState):
|
|
def _set_state(self, state: CoordinatorState):
|
|
|
"""设置状态"""
|
|
"""设置状态"""
|