fix: 修复10个关键bug提升系统稳定性和性能
1. YOLO11输出解析错误: 移除不存在的objectness行,正确使用class_scores.max() 2. CPU NMS逻辑错误: keep_mask同时标记保留和抑制框导致NMS失效,改用独立suppressed集合 3. 坐标映射缺失: _build_tracks中scale_info未使用,添加revert_boxes还原到ROI裁剪空间 4. batch=1限制: 恢复真正的动态batch推理(1~8),BatchPreprocessor支持多图stack 5. 帧率控制缺失: _read_frame添加time.monotonic()间隔控制,按target_fps跳帧 6. 拉流推理耦合: 新增独立推理线程(InferenceWorker),生产者-消费者模式解耦 7. 攒批形同虚设: 添加50ms攒批窗口+max_batch阈值,替代>=1立即处理 8. LeavePost双重等待: LEAVING确认后直接触发告警,不再进入OFF_DUTY二次等待 9. register_algorithm每帧调用: 添加_registered_keys缓存,O(1)快速路径跳过 10. GPU context线程安全: TensorRT infer()内部加锁,防止多线程CUDA context竞争 附带修复: - reset_algorithm中未定义algorithm_type变量(NameError) - update_roi_params中循环变量key覆盖外层key - AlertInfo缺少bind_id字段(TypeError) - _logger.log_alert在标准logger上不存在(AttributeError) - AlarmStateMachine死锁(Lock改为RLock) - ROICropper.create_mask坐标解析错误 - 更新测试用例适配新API Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -127,8 +127,8 @@ class ROICropper:
|
||||
|
||||
if roi.roi_type == ROIType.RECTANGLE:
|
||||
if len(roi.coordinates) >= 2:
|
||||
x1, y1 = int(roi.coordinates[0])
|
||||
x2, y2 = int(roi.coordinates[1])
|
||||
x1, y1 = int(roi.coordinates[0][0]), int(roi.coordinates[0][1])
|
||||
x2, y2 = int(roi.coordinates[1][0]), int(roi.coordinates[1][1])
|
||||
x1, x2 = sorted([x1, x2])
|
||||
y1, y2 = sorted([y1, y2])
|
||||
mask[y1:y2, x1:x2] = 255
|
||||
@@ -225,10 +225,10 @@ class LetterboxPreprocessor:
|
||||
|
||||
|
||||
class BatchPreprocessor:
|
||||
"""Batch预处理器类 (batch=1)"""
|
||||
|
||||
BATCH_SIZE = 1
|
||||
|
||||
"""Batch预处理器类 (支持动态 batch 1~8)"""
|
||||
|
||||
MAX_BATCH_SIZE = 8
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
target_size: Tuple[int, int] = (480, 480),
|
||||
@@ -236,12 +236,12 @@ class BatchPreprocessor:
|
||||
):
|
||||
self.target_size = target_size
|
||||
self.fp16_mode = fp16_mode
|
||||
self.batch_size = self.BATCH_SIZE
|
||||
|
||||
self.max_batch_size = self.MAX_BATCH_SIZE
|
||||
|
||||
self._logger = get_logger("preprocessor")
|
||||
|
||||
|
||||
self._logger.info(
|
||||
f"Batch预处理器: batch={self.batch_size}, "
|
||||
f"Batch预处理器: max_batch={self.max_batch_size}, "
|
||||
f"target_size={target_size}, fp16={fp16_mode}"
|
||||
)
|
||||
|
||||
@@ -272,23 +272,39 @@ class BatchPreprocessor:
|
||||
images: List[np.ndarray]
|
||||
) -> Tuple[np.ndarray, List[Tuple[float, float, float, float]]]:
|
||||
"""
|
||||
预处理批次图像 (batch=1)
|
||||
|
||||
预处理批次图像 (支持动态 batch)
|
||||
|
||||
Args:
|
||||
images: 图像列表 (只处理第一帧)
|
||||
|
||||
images: 已经过 letterbox 的图像列表
|
||||
|
||||
Returns:
|
||||
tuple: (批次数据 [1, 3, H, W], 缩放信息列表)
|
||||
tuple: (批次数据 [N, 3, H, W], 缩放信息列表)
|
||||
"""
|
||||
if not images:
|
||||
raise ValueError("Empty images list")
|
||||
|
||||
|
||||
letterbox = LetterboxPreprocessor(self.target_size)
|
||||
processed, scale_info = letterbox.preprocess(images[0])
|
||||
|
||||
batch_data = self.preprocess_single(processed)
|
||||
|
||||
return batch_data, [scale_info]
|
||||
processed_list = []
|
||||
scale_infos = []
|
||||
|
||||
for img in images:
|
||||
processed, scale_info = letterbox.preprocess(img)
|
||||
processed_list.append(processed)
|
||||
scale_infos.append(scale_info)
|
||||
|
||||
# 逐帧 normalize + transpose,然后 stack 成 [N, 3, H, W]
|
||||
batch_frames = []
|
||||
for processed in processed_list:
|
||||
normalized = processed.astype(np.float32) / 255.0
|
||||
transposed = np.transpose(normalized, (2, 0, 1))
|
||||
batch_frames.append(transposed)
|
||||
|
||||
batch_data = np.stack(batch_frames)
|
||||
|
||||
if self.fp16_mode:
|
||||
batch_data = batch_data.astype(np.float16)
|
||||
|
||||
return batch_data, scale_infos
|
||||
|
||||
|
||||
class ImagePreprocessor:
|
||||
@@ -323,7 +339,7 @@ class ImagePreprocessor:
|
||||
self._logger.info(
|
||||
f"图像预处理器初始化完成: "
|
||||
f"输入尺寸 {config.input_width}x{config.input_height}, "
|
||||
f"Batch大小 {self._batch_preprocessor.batch_size}, "
|
||||
f"最大Batch {self._batch_preprocessor.max_batch_size}, "
|
||||
f"FP16模式 {config.fp16_mode}"
|
||||
)
|
||||
|
||||
@@ -359,31 +375,36 @@ class ImagePreprocessor:
|
||||
rois: Optional[List[Optional[ROIInfo]]] = None
|
||||
) -> Tuple[np.ndarray, List[Tuple[float, float, float, float]]]:
|
||||
"""
|
||||
预处理批次图像,自动 padding 到 batch=4
|
||||
|
||||
预处理批次图像
|
||||
|
||||
Args:
|
||||
images: 原始图像列表
|
||||
rois: 可选的ROI配置列表
|
||||
|
||||
|
||||
Returns:
|
||||
tuple: (批次数据 [4, 3, H, W], 缩放信息列表)
|
||||
tuple: (批次数据 [N, 3, H, W], 缩放信息列表)
|
||||
"""
|
||||
from core.tensorrt_engine import pad_to_batch4
|
||||
|
||||
if rois is None:
|
||||
rois = [None] * len(images)
|
||||
|
||||
|
||||
processed_images = []
|
||||
scale_info_list = []
|
||||
|
||||
|
||||
for image, roi in zip(images, rois):
|
||||
processed, scale_info = self.preprocess_single(image, roi)
|
||||
processed_images.append(processed)
|
||||
scale_info_list.append(scale_info)
|
||||
|
||||
batch_data = self._batch_preprocessor.preprocess_batch(processed_images)
|
||||
|
||||
return batch_data, scale_info_list
|
||||
if roi is not None:
|
||||
cropped = self._cropper.crop(image, roi)
|
||||
if cropped is None:
|
||||
cropped = image
|
||||
else:
|
||||
cropped = image
|
||||
processed_images.append(cropped)
|
||||
|
||||
# BatchPreprocessor 处理 letterbox + normalize + stack
|
||||
batch_data, batch_scale_infos = self._batch_preprocessor.preprocess_batch(
|
||||
processed_images
|
||||
)
|
||||
|
||||
return batch_data, batch_scale_infos
|
||||
|
||||
def revert_boxes(
|
||||
self,
|
||||
@@ -408,7 +429,7 @@ class ImagePreprocessor:
|
||||
"config": {
|
||||
"input_width": self.config.input_width,
|
||||
"input_height": self.config.input_height,
|
||||
"batch_size": self._batch_preprocessor.batch_size,
|
||||
"batch_size": self._batch_preprocessor.max_batch_size,
|
||||
"fp16_mode": self.config.fp16_mode,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user