10 KiB
Executable File
Ground Truth ROI Processing for Evaluation
概述
为了确保评测指标的准确性,评测时需要对Ground Truth(真值标签)进行与训练时相同的ROI过滤和截断处理。这确保了评测时GT和检测结果在同一坐标系下进行比较。
问题背景
训练时的处理
在模型训练时,数据经过以下ROI处理(参考 utils/dataloaders3d.py中的post_process_labels_to_roi):
- ROI计算:基于标定参数计算灭点(vanishing point),以灭点为中心裁剪ROI区域
- 标签过滤:移除完全在ROI外的目标
- 边界截断:将部分在ROI内的目标边界框clip到ROI边界
- 坐标转换:将标签坐标从原图坐标系转换到ROI相对坐标系
- 特殊处理:对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)}")
性能考虑
- 标定缓存:每个case的标定参数只加载一次
- 多进程支持:支持多进程并行评测
- 内存优化:按需加载和处理
故障排查
问题1:找不到标定文件
Warning: Calibration file not found for case xxx
解决:检查 calib_root 路径和标定文件名(camera4.json)
问题2:评测结果仍然异常
检查项:
- ROI配置是否与训练一致
- 标定文件是否正确
- 图像尺寸配置是否正确
问题3:GT数量为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处理,确保了:
- ✅ GT和检测结果在同一坐标系
- ✅ 完全在ROI外的目标被正确过滤
- ✅ 部分在ROI内的目标被正确截断
- ✅ 评测指标准确反映模型性能
关键要点:
- 评测配置必须与训练配置一致
- 需要提供正确的标定文件
- 支持多进程和不同ROI配置