Files
yolov26_3d/eval_tools/docs/ROI_GT_PROCESSING_GUIDE.md
2026-06-24 09:35:46 +08:00

10 KiB
Executable File
Raw Blame History

Ground Truth ROI Processing for Evaluation

概述

为了确保评测指标的准确性评测时需要对Ground Truth真值标签进行与训练时相同的ROI过滤和截断处理。这确保了评测时GT和检测结果在同一坐标系下进行比较。

问题背景

训练时的处理

在模型训练时数据经过以下ROI处理参考 utils/dataloaders3d.py中的post_process_labels_to_roi

  1. ROI计算基于标定参数计算灭点vanishing point以灭点为中心裁剪ROI区域
  2. 标签过滤移除完全在ROI外的目标
  3. 边界截断将部分在ROI内的目标边界框clip到ROI边界
  4. 坐标转换将标签坐标从原图坐标系转换到ROI相对坐标系
  5. 特殊处理对cut-in/cut-out目标进行特殊标记

评测时的问题

之前的评测代码中:

  • 检测结果在ROI坐标系中已经过ROI裁剪和resize
  • GT标签在原图坐标系中未经ROI处理
  • 结果坐标系不匹配导致IoU为0评测指标不准确

解决方案

新增 ROIProcessor 模块在评测时对GT标签进行与训练时相同的ROI处理。

核心模块

1. ROIProcessor 类

位置:eval_tools/evaluator/roi_processor.py

主要功能

  • 加载标定参数
  • 计算ROI区域基于灭点
  • 对GT标签进行ROI过滤和截断

关键方法

class ROIProcessor:
    def __init__(self, calib_root, roi_config, ori_img_size):
        """初始化ROI处理器
        
        Args:
            calib_root: 标定文件根目录
            roi_config: ROI配置[width, height]或[x1, y1, x2, y2]
            ori_img_size: 原图尺寸 (width, height)
        """
        
    def compute_roi(self, calib_params):
        """计算ROI区域
        
        基于灭点计算规则:
        - crop_center_x = oriW // 2
        - crop_center_y = cy - fy * tan(pitch)
        - ROI以crop_center为中心裁剪
        """
        
    def process_annotations_with_roi(self, annotations, roi_bounds):
        """对标注进行ROI过滤和截断
        
        处理逻辑:
        1. 移除完全在ROI外的目标
        2. clip边界框到ROI范围
        3. 转换坐标到ROI相对坐标系
        """

2. Evaluator 更新

位置:eval_tools/evaluator/evaluator.py

主要更新

  • 集成 ROIProcessor
  • 在worker函数中对GT进行ROI处理
  • 支持多进程评测时的ROI处理

使用方法

1. 配置文件方式(推荐)

创建配置文件(参考 eval_tools/configs/eval_config_with_roi_gt.yaml

# Dataset paths
dataset:
  det_path: "/path/to/detection_results"
  gt_path: "/path/to/ground_truth"

# Image properties
image:
  width: 1920
  height: 1080

# ROI Ground Truth Processing
roi_gt:
  enabled: true                         # 启用GT的ROI处理
  calib_root: "/path/to/calibrations"   # 标定文件根目录包含各case的camera4.json
  roi_config: [1920, 960]               # ROI配置必须与训练配置一致
  
# 其他配置...

运行评测:

python eval_tools/core/eval.py --config eval_tools/configs/eval_config_with_roi_gt.yaml

2. ROI配置说明

方式1尺寸模式常用

roi_config: [1920, 960]  # [width, height]
  • ROI以灭点为中心裁剪
  • width和height指定ROI的宽度和高度
  • 必须与训练时yaml中的roi配置一致

训练配置对应(data/mono3d.yaml

# For ROI0
roi: [1920, 960]  # 训练时配置

# For ROI1
# roi: [704, 352]  # 训练时配置

方式2边界模式

roi_config: [0, 120, 1920, 1080]  # [x1, y1, x2, y2]
  • 固定的ROI边界
  • 适用于已知固定ROI的情况

3. 标定文件要求

ROI处理需要标定参数来计算灭点。标定文件结构

calib_root/
├── case1/
│   └── camera4.json    # 标定文件
├── case2/
│   └── camera4.json
└── ...

标定文件格式(camera4.json

{
  "focal_u": 1450.0,
  "focal_v": 1450.0,
  "cu": 960.0,
  "cv": 540.0,
  "yaw": 0.0,
  "pitch": -5.0,
  "distort_coeffs": [0.1, 0.05, 0.02, 0.01]
}

关键参数

  • focal_u, focal_v: 焦距
  • cu, cv: 主点坐标
  • pitch: 俯仰角(用于计算灭点)

4. 完整评测示例

ROI0模型评测

python eval_tools/core/eval.py \
  --config eval_tools/configs/eval_config_with_roi_gt.yaml \
  --det-path /data/inference_results/roi0 \
  --gt-path /data/ground_truth

配置文件中:

roi_gt:
  enabled: true
  calib_root: "/data/ground_truth"
  roi_config: [1920, 960]  # ROI0配置

ROI1模型评测

配置文件中修改:

roi_gt:
  enabled: true
  calib_root: "/data/ground_truth"
  roi_config: [704, 352]   # ROI1配置

ROI处理流程

1. GT标签ROI处理流程

原始GT标签原图坐标系
    ↓
加载标定参数
    ↓
计算灭点位置
vanish_y = cy - fy * tan(pitch)
crop_center = (oriW//2, vanish_y)
    ↓
计算ROI区域
roi_x1 = crop_center_x - roi_width/2
roi_y1 = crop_center_y - roi_height/2
roi_x2 = roi_x1 + roi_width
roi_y2 = roi_y1 + roi_height
    ↓
过滤GT标签
1. 移除完全在ROI外的目标
2. 保留完全在ROI内的目标
3. 保留部分在ROI内的目标
    ↓
截断边界框
clip bbox到ROI边界
    ↓
坐标转换
转换到ROI相对坐标系
    ↓
处理后的GT标签ROI坐标系
    ↓
与检测结果匹配和评测

2. 核心计算公式

灭点计算

vanish_y = cy - fy * tan(pitch * π/180)
crop_center_x = image_width // 2
crop_center_y = vanish_y

ROI边界计算

roi_x1 = crop_center_x - roi_width / 2
roi_y1 = crop_center_y - roi_height / 2
roi_x2 = roi_x1 + roi_width
roi_y2 = roi_y1 + roi_height

坐标转换

# 原图坐标 -> ROI相对坐标
new_x1 = x1 - roi_x1
new_y1 = y1 - roi_y1
new_x2 = x2 - roi_x1
new_y2 = y2 - roi_y1

# Clip到ROI边界
new_x1 = clip(new_x1, 0, roi_width-1)
new_y1 = clip(new_y1, 0, roi_height-1)
new_x2 = clip(new_x2, 0, roi_width-1)
new_y2 = clip(new_y2, 0, roi_height-1)

实现细节

1. 多进程支持

ROI处理支持多进程评测每个worker进程独立创建ROI处理器

@staticmethod
def _process_frame_3d(pair, ...):
    from .roi_processor import ROIProcessor
    
    # 在worker中创建ROI处理器
    if 'roi_processor_config' in pair:
        roi_processor = ROIProcessor(...)
        gts, _ = roi_processor.process_case_frame(...)

2. 标定缓存

ROI处理器会缓存已加载的标定参数避免重复读取

self.calib_cache = {}  # case_name -> calib_params

3. 处理标记

处理后的标注会添加额外标记:

{
    'bbox_2d': [x1, y1, x2, y2],  # ROI相对坐标
    'roi_relative': True,          # 标记已转换
    'roi_bounds': (x1, y1, x2, y2), # 记录ROI边界
    'was_clipped': False,          # 是否被clip
    '3d_info': {
        'partially_visible': True  # 3D目标是否部分可见
    }
}

注意事项

1. ROI配置一致性

关键评测时的ROI配置必须与训练时完全一致

  • 训练配置:data/mono3d.yaml 中的 roi: [width, height]
  • 评测配置:eval_config_with_roi_gt.yaml 中的 roi_config: [width, height]

不一致会导致评测结果不准确。

2. 标定文件路径

确保 calib_root 指向正确的标定文件目录:

calib_root/
├── case_name1/
   └── camera4.json
├── case_name2/
   └── camera4.json

3. 坐标系说明

  • 原图坐标系(0, 0)在左上角,范围[0, 1920]×[0, 1080]
  • ROI坐标系(0, 0)在ROI左上角范围[0, roi_width]×[0, roi_height]
  • 归一化坐标YOLO格式范围[0, 1]

4. 边界情况处理

  • 完全在ROI外的目标直接过滤
  • 部分在ROI内的目标保留并clip到边界
  • 3D信息处理部分可见目标标记为 partially_visible

验证方法

1. 检查ROI处理是否生效

运行评测时查看日志:

ROI processor enabled for GT filtering with config: [1920, 960]

2. 对比有无ROI处理的结果

# 不使用ROI处理
python eval_tools/core/eval.py --config eval_config_no_roi.yaml

# 使用ROI处理
python eval_tools/core/eval.py --config eval_config_with_roi_gt.yaml

预期使用ROI处理后评测指标应该更准确Precision和Recall不会异常低

3. 验证GT数量

  • ROI处理前所有原图中的GT
  • ROI处理后仅ROI内的GT数量应该减少

可以在代码中添加调试输出:

print(f"GT before ROI: {len(gts_before)}")
print(f"GT after ROI: {len(gts_after)}")

性能考虑

  1. 标定缓存每个case的标定参数只加载一次
  2. 多进程支持:支持多进程并行评测
  3. 内存优化:按需加载和处理

故障排查

问题1找不到标定文件

Warning: Calibration file not found for case xxx

解决:检查 calib_root 路径和标定文件名camera4.json

问题2评测结果仍然异常

检查项

  1. ROI配置是否与训练一致
  2. 标定文件是否正确
  3. 图像尺寸配置是否正确

问题3GT数量为0

原因所有GT都在ROI外被过滤 检查ROI配置是否过小或位置偏移

示例脚本

Python API使用

from eval_tools.evaluator import Evaluator, ROIProcessor

# 创建ROI处理器
roi_processor = ROIProcessor(
    calib_root="/path/to/calibrations",
    roi_config=[1920, 960],
    ori_img_size=(1920, 1080)
)

# 处理GT标签
annotations, roi_bounds = roi_processor.process_case_frame(
    case_name="case1",
    frame_name="frame001",
    annotations=gt_annotations
)

# 创建评测器(配置版)
config = {
    'roi_gt': {
        'enabled': True,
        'calib_root': '/path/to/calibrations',
        'roi_config': [1920, 960]
    }
}
evaluator = Evaluator(config=config)

总结

通过引入 ROIProcessor 和更新评测流程现在评测时的GT标签会经过与训练时相同的ROI处理确保了

  1. GT和检测结果在同一坐标系
  2. 完全在ROI外的目标被正确过滤
  3. 部分在ROI内的目标被正确截断
  4. 评测指标准确反映模型性能

关键要点

  • 评测配置必须与训练配置一致
  • 需要提供正确的标定文件
  • 支持多进程和不同ROI配置