对漏检和未检后备箱的帧做时间回溯
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user