diff --git a/biz/checkpoint/checkpoint_biz.py b/biz/checkpoint/checkpoint_biz.py index 2536bd3..e65d648 100644 --- a/biz/checkpoint/checkpoint_biz.py +++ b/biz/checkpoint/checkpoint_biz.py @@ -1,9 +1,10 @@ - import cv2 import numpy as np from typing import Dict, Any import threading import queue +from collections import deque + from biz.base_frame_processor import BaseFrameProcessorWorker @@ -13,10 +14,13 @@ from algorithm.common.npu_yolo_pose_onnx import YOLOv8_Pose_ONNX from yolox.tracker.byte_tracker import BYTETracker from utils.logger import get_logger -from common.constants import MODEL_ROOT_PATH logger = get_logger(__name__) -DETECT_MODEL_PATH = 'YOLO_Weight/Kadian.onnx' +# ========================= 配置区 ========================= +# Kadian 模型路径与ROI(可根据实际情况修改) +#DETECT_MODEL_PATH = 'YOLO_Weight/Kadian.onnx' +DETECT_MODEL_PATH = r'D:\Python_Save\PoliceProject\Yolo_Weight\Kadian\Kadian_wanjia_xinkailing.onnx' +#POSE_MODEL_PATH = 'YOLO_Weight/yolov8l-pose.onnx' # 默认相对ROI(与原文件一致) #ROI_RELATIVE = np.array([ @@ -39,7 +43,6 @@ ROI_RELATIVE=np.array([ [0.15,0.15], [0.37,0.15], [0.55,0.2], - [0.9,0.85], [0.35,0.85] ]) @@ -60,17 +63,9 @@ class KadianDetector: # 摄像头额外参数 self.params = params if params is not None else {} - # 模型路径:从 params 读取,未配置则使用默认值 DETECT_MODEL_PATH - model_path = self.params.get('model_path') - if model_path: - full_model_path = f"{MODEL_ROOT_PATH}/{model_path}" - else: - full_model_path = DETECT_MODEL_PATH - logger.info(f"Loading model from: {full_model_path}") - # 模型加载 self.detector = YOLOv8_ONNX( - full_model_path, + DETECT_MODEL_PATH, conf_threshold=0.6, iou_threshold=0.65, input_size=PERSON_CAR_INPUT_SIZE @@ -125,6 +120,8 @@ class KadianDetector: self.height = 0 + + # 车辆注册表 (字典) self.roi_car_registry = {} # 违规车辆记录 @@ -140,6 +137,12 @@ class KadianDetector: self.nobody_frames = 0 # 累计无人在场帧数 self.only_one_frames = 0 # 累计单人在场帧数 + self.max_car_frames = int((3.0 + self.TIME_TOLERANCE_CAR) * self.fps) # 50帧 + self.frame_buffer_ignore_untrunk = deque(maxlen=self.max_car_frames) + + self.untrunk_rollback_time = 3.0 # 未检查后备箱需要回溯的时间 + self.ignored_rollback_time = 1.0 # 漏检需要回溯的时间 + def _get_roi_points(self, frame_width: int, frame_height: int): """ @@ -206,6 +209,21 @@ class KadianDetector: x1, y1, x2, y2 = box return x1 < px < x2 and y1 < py < y2 + def find_target_frame(self, target_time_sec): + + target_frame = None + min_time_diff = float('inf') + for buffered in self.frame_buffer_ignore_untrunk: + time_diff = abs(buffered['frame_idx'] - target_time_sec) + if time_diff < min_time_diff: + min_time_diff = time_diff + target_frame = buffered['frame'] + # 如果没找到,返回最早的帧 + if target_frame is None and len(self.frame_buffer_ignore_untrunk) > 0: + target_frame = self.frame_buffer_ignore_untrunk[0]['frame'] + + return target_frame + def process_frame(self, frame, camera_id: int, timestamp: float) -> Dict[str, Any]: h, w = frame.shape[:2] self.width, self.height = w, h @@ -217,6 +235,8 @@ class KadianDetector: current_time_sec = timestamp + + # ========= 主检测(删除pose检测)========= detections = self.detector(frame) @@ -267,7 +287,8 @@ class KadianDetector: # ========= 处理跟踪结果 ========= for t in tracks: tid = t.track_id - REVALIDATE_FRAME_INTERVAL = 10 + + REVALIDATE_FRAME_INTERVAL = 2 # 定期重新匹配跟踪ID的类别 if (self.current_frame_idx % REVALIDATE_FRAME_INTERVAL == 0) or (tid not in self.track_role): @@ -299,13 +320,17 @@ class KadianDetector: # 车辆注册表初始化 if tid not in self.roi_car_registry: self.roi_car_registry[tid] = { - 'first_seen': self.current_frame_idx, - 'last_seen': self.current_frame_idx, + # 'first_seen': self.current_frame_idx, + # 'last_seen': self.current_frame_idx, + 'first_seen': current_time_sec, + 'last_seen': current_time_sec, 'trunk_frames': 0, 'is_checked': False, + #'frame_buffer': deque(maxlen=self.max_car_frames), # 新增 } else: - self.roi_car_registry[tid]['last_seen'] = self.current_frame_idx + #self.roi_car_registry[tid]['last_seen'] = self.current_frame_idx + self.roi_car_registry[tid]['last_seen'] = current_time_sec label += " IN" elif role == "opentrunk": color = (255, 165, 0) # 橙色 @@ -325,11 +350,13 @@ class KadianDetector: # 警察注册表初始化 if tid not in self.roi_police_registry: self.roi_police_registry[tid] = { - 'first_seen': self.current_frame_idx, - 'last_seen': self.current_frame_idx, + # 'first_seen': self.current_frame_idx, + # 'last_seen': self.current_frame_idx, + 'first_seen': current_time_sec, + 'last_seen': current_time_sec, } else: - self.roi_police_registry[tid]['last_seen'] = self.current_frame_idx + self.roi_police_registry[tid]['last_seen'] = current_time_sec label += " IN" else: color = (255, 255, 255) # 白色 @@ -339,6 +366,16 @@ class KadianDetector: cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) + # 每帧保存到缓存 + # self.roi_car_registry[tid]['frame_buffer'].append({ + # 'frame_idx': current_time_sec, + # 'frame': frame.copy(), + # }) + self.frame_buffer_ignore_untrunk.append({ + 'frame_idx': current_time_sec, + 'frame': frame.copy(), + }) + # ========================================== # 关联分析: 哪个后备箱属于哪辆车? # ========================================== @@ -365,11 +402,16 @@ class KadianDetector: for car_id, info in self.roi_car_registry.items(): last_seen = info['last_seen'] - if (self.current_frame_idx - last_seen) <= self.frame_buffer_limit_car: + # if (self.current_frame_idx - last_seen) <= self.frame_buffer_limit_car: + if (current_time_sec - last_seen) <= self.frame_buffer_limit_car: active_car_ids.append(car_id) else: cars_to_remove.append(car_id) + + unchecked_trunk_frame = None # 保存未检查后备箱的帧 + ignored_trunk_frame = None # 保存漏检的帧 + # 处理离场车辆,生成违规告警 for car_id in cars_to_remove: car_info = self.roi_car_registry[car_id] @@ -378,13 +420,24 @@ class KadianDetector: # 情况1:通过时间太短 -> Ignore (Too Fast) if duration_frames < self.frame_thresh_car_min_duration: logger.info(f"ALARM: Car {car_id} passed too fast -> Regarded as Ignore Checked!") - self.fast_pass_alerts[car_id] = self.current_frame_idx + int(self.ignore_show_seconds * self.fps) + #self.fast_pass_alerts[car_id] = self.current_frame_idx + int(self.ignore_show_seconds * self.fps) + self.fast_pass_alerts[car_id] = current_time_sec + int(self.ignore_show_seconds * self.fps) + + target_time_sec = car_info['last_seen'] - self.ignored_rollback_time + ignored_trunk_frame = self.find_target_frame(target_time_sec) + + # 情况2:时间够长,但没检查后备箱 -> Unchecked Trunk elif not car_info['is_checked']: logger.info(f"ALARM: Car {car_id} left without checking trunk!") - self.unchecked_trunk_alerts[car_id] = self.current_frame_idx + int( - self.openTrunk_show_seconds * self.fps) + #self.unchecked_trunk_alerts[car_id] = self.current_frame_idx + int(self.openTrunk_show_seconds * self.fps) + self.unchecked_trunk_alerts[car_id] = current_time_sec + int(self.openTrunk_show_seconds * self.fps) + + target_time_sec = car_info['last_seen'] - self.untrunk_rollback_time + unchecked_trunk_frame = self.find_target_frame(target_time_sec) + + del self.roi_car_registry[car_id] @@ -398,7 +451,8 @@ class KadianDetector: for police_id, info in self.roi_police_registry.items(): last_seen = info['last_seen'] - if (self.current_frame_idx - last_seen) <= self.frame_buffer_limit_police: + #if (self.current_frame_idx - last_seen) <= self.frame_buffer_limit_police: + if (current_time_sec - last_seen) <= self.frame_buffer_limit_police: active_police_ids.append(police_id) else: polices_to_remove.append(police_id) @@ -430,8 +484,10 @@ class KadianDetector: # break # 只显示一次 # B. 显示 Unchecked Trunk (离场未检查后备箱) - expired_alerts = [cid for cid, end_frame in self.unchecked_trunk_alerts.items() if - self.current_frame_idx > end_frame] + #expired_alerts = [cid for cid, end_frame in self.unchecked_trunk_alerts.items() if self.current_frame_idx > end_frame] + expired_alerts = [cid for cid, end_frame in self.unchecked_trunk_alerts.items() if current_time_sec > end_frame] + + for cid in expired_alerts: del self.unchecked_trunk_alerts[cid] @@ -440,13 +496,16 @@ class KadianDetector: current_frame_alerts.append({ 'time': current_time_sec, 'action': "Unchecked Trunk", + 'image': unchecked_trunk_frame }) #self.draw_alert(frame, alert_text, (0, 0, 255), offset_y=alert_offset) alert_offset += 100 # C. 显示 Ignore (通过过快) - expired_fast_alerts = [cid for cid, end_frame in self.fast_pass_alerts.items() if - self.current_frame_idx > end_frame] + #expired_fast_alerts = [cid for cid, end_frame in self.fast_pass_alerts.items() if self.current_frame_idx > end_frame] + expired_fast_alerts = [cid for cid, end_frame in self.fast_pass_alerts.items() if current_time_sec > end_frame] + + for cid in expired_fast_alerts: del self.fast_pass_alerts[cid] @@ -455,18 +514,25 @@ class KadianDetector: current_frame_alerts.append({ 'time': current_time_sec, 'action': "Ignore", + 'image': ignored_trunk_frame }) #self.draw_alert(frame, alert_text, (0, 0, 255), offset_y=alert_offset) alert_offset += 100 + + # D. 显示警察在场状态 (Nobody/Only One) # 清理过期的 Nobody 告警 - expired_nobody = [k for k, v in self.nobody_alerts.items() if self.current_frame_idx > v] + #expired_nobody = [k for k, v in self.nobody_alerts.items() if self.current_frame_idx > v] + expired_nobody = [k for k, v in self.nobody_alerts.items() if current_time_sec > v] + for k in expired_nobody: del self.nobody_alerts[k] # 清理过期的 Only One 告警 - expired_only_one = [k for k, v in self.only_one_alerts.items() if self.current_frame_idx > v] + # expired_only_one = [k for k, v in self.only_one_alerts.items() if self.current_frame_idx > v] + expired_only_one = [k for k, v in self.only_one_alerts.items() if current_time_sec > v] + for k in expired_only_one: del self.only_one_alerts[k] @@ -488,7 +554,8 @@ class KadianDetector: if effective_police_count == 0 and self.nobody_frames >= self.frame_thresh_nobody: alert_text = "Nobody" if "Nobody" not in self.nobody_alerts: - self.nobody_alerts["Nobody"] = self.current_frame_idx + int(self.police_show_seconds * self.fps) + # self.nobody_alerts["Nobody"] = self.current_frame_idx + int(self.police_show_seconds * self.fps) + self.nobody_alerts["Nobody"] = current_time_sec + int(self.police_show_seconds * self.fps) current_frame_alerts.append({ 'time': current_time_sec, 'action': "Nobody", @@ -498,7 +565,8 @@ class KadianDetector: elif effective_police_count == 1 and self.only_one_frames >= self.frame_thresh_only_one: alert_text = "Only One" if "Only One" not in self.only_one_alerts: - self.only_one_alerts["Only One"] = self.current_frame_idx + int(self.police_show_seconds * self.fps) + # self.only_one_alerts["Only One"] = self.current_frame_idx + int(self.police_show_seconds * self.fps) + self.only_one_alerts["Only One"] = current_time_sec + int(self.police_show_seconds * self.fps) current_frame_alerts.append({ 'time': current_time_sec, 'action': "Only One",