23 KiB
Executable File
模型评测框架使用指南
入口脚本:
eval_tools/core/run_eval_with_config.sh
目录
1. 框架概览
本框架用于评测 YOLOv5-3D 模型的 2D 检测与 3D 定位性能,支持:
- 2D 检测评测:全部 14 个类别的 Precision / Recall / AP / mAP
- 3D 检测评测:vehicle / pedestrian / bicycle / rider 的横向误差、纵向误差、heading 误差
- ROI GT 过滤:评测坐标系与训练时保持一致
- 距离区间分析:按纵向距离(z轴)和横向距离(x轴)分段统计 3D 指标
- 多模型对比:基于共同匹配集(Common Matches)公平比较两个模型的 3D 性能
- Heading 误差分析:提取、可视化、分析方向预测误差
架构图
eval_tools/core/run_eval_with_config.sh
│
▼
eval_tools/core/eval.py ← 主入口 Python 脚本
│
├─── eval_tools/configs/*.yaml ← 配置文件
│
└─── eval_tools/evaluator/ ← 核心评测引擎
├── evaluator.py ← 主控流程、并行调度
├── parser.py ← GT / 检测结果解析
├── matcher.py ← 2D IoU 匹配
├── metrics_2d.py ← 2D 指标计算
├── metrics_3d.py ← 3D 指标计算
└── roi_processor.py ← ROI 坐标系对齐
2. 目录结构
eval_tools/
├── core/
│ ├── eval.py # 主评测脚本
│ ├── run_eval_with_config.sh # 单模型评测入口(当前默认入口)
│ ├── run_eval.sh # 早期版本入口(无配置文件)
│ ├── run_eval_2d_only.sh # 仅 2D 评测
│ ├── run_eval_3d_only.sh # 仅 3D 评测
│ └── run_evaluation_example.sh # 示例脚本
│
├── configs/
│ ├── eval_config_mono3d-roi0.yaml # mono3d 模型 ROI0 配置
│ ├── eval_config_mono3d-roi1.yaml # mono3d 模型 ROI1 配置
│ ├── eval_config_yolov5s-roi0.yaml # yolov5s 模型 ROI0 配置
│ ├── eval_config_yolov5s-roi1.yaml # yolov5s 模型 ROI1 配置
│ ├── eval_config_yolov5s_cncap-roi0.yaml # yolov5s CNCAP 测试集 ROI0(当前默认)
│ └── eval_config_yolov5s_cncap-roi1.yaml # yolov5s CNCAP 测试集 ROI1
│
├── evaluator/
│ ├── __init__.py
│ ├── evaluator.py # 主评测器(数据加载、调度、报告生成)
│ ├── parser.py # 真值 / 检测结果解析器
│ ├── matcher.py # 2D IoU 匹配器
│ ├── metrics_2d.py # 2D 指标计算(P/R/AP/mAP)
│ ├── metrics_3d.py # 3D 指标计算(Lat/Long/Heading)
│ └── roi_processor.py # ROI GT 处理器
│
├── model_comparison/
│ ├── find_common_matches.py # 找出两模型共同匹配的 GT
│ ├── compare_models.py # 生成双模型对比报告
│ ├── compare_models_with_common_matches.sh # 一键双模型对比脚本
│ ├── generate_eval_report.py # 从 JSON 生成 Markdown 评测报告
│ ├── generate_comparison_report.py # 生成对比 Markdown 报告
│ └── ...
│
├── heading_analysis/
│ ├── extract_bad_heading_cases.py # 提取 heading 误差大的案例
│ ├── visualize_heading_errors.py # 可视化 heading 误差(BEV/图像/极坐标)
│ └── ...
│
├── visualization/
│ └── test_val_visualize_from_paths.py # 从路径可视化检测结果
│
├── utilities/
│ ├── check_img_exists.py # 检查图像文件是否存在
│ └── extract_video_frames.py # 视频帧提取
│
└── docs/ # 详细设计文档(本文件所在目录)
3. 核心流程
单模型评测(run_eval_with_config.sh)
┌─────────────────────────────────────────────────────┐
│ run_eval_with_config.sh │
│ │
│ 1. 设置 PYTHONPATH │
│ 2. 指定配置文件路径 (MODEL_CONFIG) │
│ 3. 指定输出目录 (OUTPUT_BASE/MODEL_NAME/TIMESTAMP) │
│ 4. 调用 eval_tools/core/eval.py │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ eval.py │
│ │
│ 1. 解析 YAML 配置 + 命令行参数 │
│ 2. 初始化 Evaluator(含 ROIProcessor) │
│ 3. 加载检测结果 & 真值文件对 │
│ 4. 并行处理每张图 (IoU 匹配 + 指标统计) │
│ 5. 汇总指标并生成报告 │
└─────────────────────────────────────────────────────┘
│
┌────────────┴─────────────┐
▼ ▼
evaluation_report.json detailed_3d_matches.json
evaluation_report.txt (--save-detailed-matches)
per_case_reports/
双模型对比(compare_models_with_common_matches.sh)
Model 1 评测 → detailed_3d_matches.json
┐
├─ find_common_matches.py → common_matches.json
│
Model 2 评测 → detailed_3d_matches.json ┘
│
└─ compare_models.py → 对比报告
4. 配置文件说明
配置文件位于 eval_tools/configs/,YAML 格式,分以下几个部分:
4.1 数据集路径 (dataset)
dataset:
det_path: "/data1/.../evalset_roi0" # 检测结果根目录(含 case 文件夹)
gt_path: "/data1/xdzhu/Testdata" # 真值标签根目录(含 case 文件夹)
path_depth: 1 # 目录层级:1 = det_path/case/txt_results
# 2 = det_path/level1/case/txt_results
det_format: "json" # 检测结果格式:auto / json / txt
gt_format: "json" # 真值格式:auto / json / txt
目录结构要求(path_depth: 1):
det_path/
└── case_019b178d/
└── json_results/ ← det_format="json"
├── 000000.json
└── 000001.json
gt_path/
└── case_019b178d/
└── labels_json/ ← gt_format="json"
├── 000000.json
└── 000001.json
4.2 图像尺寸 (image)
image:
width: 1920
height: 1080
4.3 模型配置与 GT 过滤 (model)
model:
input_size: 704 # 模型输入宽度(像素)
min_box_size_at_input_scale: 8 # 模型输入分辨率下的最小框尺寸
# GT 过滤阈值自动计算:min_box_size = 8 * roi_width / 704
# ROI0 (1920→704): ≈ 21.8 像素;ROI1 (704→704): 8 像素
4.4 并行性能 (performance)
performance:
num_workers: 32 # 并行 worker 数(null = auto-detect CPU 核数)
4.5 ROI GT 处理 (roi_gt)
roi_gt:
enabled: true
calib_root: "/data1/xdzhu/Testdata" # 标定文件根目录(含 camera4.json)
roi_config: [1920, 960] # ROI 配置,与训练保持一致
# ROI0: [1920, 960]
# ROI1: [704, 352]
重要:
roi_config必须与训练时的 ROI 配置完全一致,否则坐标系不对齐,IoU 计算将不准确。
4.6 类别定义 (classes)
classes:
3d_classes: [0, 1, 2, 3] # 参与 3D 评测的类别
2d_classes: [4, 5, ..., 13] # 仅参与 2D 评测的类别
class_names:
0: "vehicle"
1: "pedestrian"
...
13: "tricycle"
4.7 匹配参数 (matching)
matching:
iou_threshold: 0.5 # 2D 匹配 IoU 阈值
4.8 2D 指标 (metrics_2d)
metrics_2d:
enabled: true
conf_threshold: 0.4 # Precision/Recall 置信度阈值
ap_method: "voc2010" # AP 计算方法:voc2010(11点插值)/ coco(全点插值)
4.9 3D 指标 (metrics_3d)
metrics_3d:
enabled: true
heading_tolerance: "both" # strict / relaxed / both
distance_ranges: # 纵向距离区间(米)
- [0, 10]
- [10, 20]
- ...
- [100, 999]
lateral_distance_ranges: # 横向距离区间(米,x 轴)
- [-50, -40]
- ...
- [40, 50]
heading_tolerance 说明:
| 值 | 含义 |
|---|---|
strict |
标准计算(默认) |
relaxed |
对称物体允许 180° 对称:error = min(error, π - error) |
both |
同时计算并输出 strict 和 relaxed 两套结果 |
4.10 输出配置 (output)
output:
save_path: "evaluation_results/my_model/{timestamp}"
formats: ["json", "txt"]
print_details: true
per_case_reports: true
5. 命令行参数说明
eval_tools/core/eval.py 支持以下参数(命令行参数优先级高于配置文件):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--config |
str | — | YAML 配置文件路径 |
--det-path |
str | — | 检测结果根目录 |
--gt-path |
str | — | 真值标签根目录 |
--path-depth |
int | 1 | 目录层级(1 或 2) |
--det-format |
str | auto | 检测结果格式(auto/json/txt) |
--gt-format |
str | auto | 真值格式(auto/json/txt) |
--output-dir |
str | eval_results | 输出目录 |
--img-width |
int | 1920 | 图像宽度(像素) |
--img-height |
int | 1080 | 图像高度(像素) |
--iou-threshold |
float | 0.5 | IoU 匹配阈值 |
--conf-threshold |
float | 0.5 | 置信度阈值 |
--ap-method |
str | voc2010 | AP 计算方法(voc2010/coco) |
--heading-tolerance |
str | strict | heading 容忍模式(strict/relaxed/both) |
--num-workers |
int | auto | 并行 worker 数 |
--eval-2d-only |
flag | — | 仅评测 2D |
--eval-3d-only |
flag | — | 仅评测 3D |
--save-detailed-matches |
flag | — | 保存详细 3D 匹配信息(供模型对比使用) |
6. 数据格式
6.1 真值(Ground Truth)格式
车辆类别(50 个值):
[class, x, y, w, h, # 0-4: 类别 + 归一化 2D 框
x3d, y3d, z3d, # 5-7: 3D 中心点(相机坐标系)
l, h, w, # 8-10: 3D 尺寸(长高宽)
rot_y, # 11: 绕 Y 轴旋转角
xc, yc, # 12-13: 3D 中心点的图像投影
xc_d, yc_d, # 14-15: 深度相关投影
alpha, # 16: 观测角
0, # 17: 占位符
前面(x3d,y3d,z3d,alpha,xc,yc,score,occ), # 18-25
后面(x3d,y3d,z3d,alpha,xc,yc,score,occ), # 26-33
左面(x3d,y3d,z3d,alpha,xc,yc,score,occ), # 34-41
右面(x3d,y3d,z3d,alpha,xc,yc,score,occ)] # 42-49
行人/自行车/骑手(18 个值): 上述 0-17,无四面信息。
仅 2D 标注(6 个值):
[class, x, y, w, h, -1]
6.2 检测结果格式
车辆(15 个字段):
vehicle 0.95 368.08 574.17 437.89 617.20 cam -30.14 1.43 68.55 5.52 2.50 2.31 2.70 left
label conf x1 y1 x2 y2 cs x3d y3d z3d l h w rot_y face_type
face_type 取值:front / back(或 rear/tail)/ left / right
行人/骑手等(15 个字段):同上,face_type 固定为 whole。
纯 2D 类别(6 个字段):
plate 0.94246 532.12 203.26 558.73 214.86
label conf x1 y1 x2 y2
7. 评测指标
7.1 2D 检测指标
- Precision:TP / (TP + FP),按
conf_threshold过滤 - Recall:TP / (TP + FN)
- F1-Score:2 × P × R / (P + R)
- AP:PR 曲线下面积(IoU 阈值 0.5)
- mAP:所有类别 AP 的算术平均
匹配规则:
- 按置信度从高到低排序预测框
- 与真值框 IoU ≥ 0.5 且类别相同 → TP(每个真值框只匹配一次)
- 未匹配预测框 → FP;未被匹配真值框 → FN
类别划分(14 类):
| ID | 类别 | 3D 评测 |
|---|---|---|
| 0 | vehicle | ✓ |
| 1 | pedestrian | ✓ |
| 2 | bicycle | ✓ |
| 3 | rider | ✓ |
| 4 | roadblock | — |
| 5 | head | — |
| 6 | tsr | — |
| 7 | guideboard | — |
| 8 | plate | — |
| 9 | wheel | — |
| 10 | tl_border | — |
| 11 | tl_wick | — |
| 12 | tl_num | — |
| 13 | tricycle | — |
7.2 3D 检测指标
前提:2D IoU 匹配成功(≥ 0.5)且真值包含完整 3D 标注。
车辆类别(基于最近面中心点对比):
根据检测结果的 face_type 字段,在真值中选取对应面的中心点进行比较:
face_type → 真值面中心点索引
front → [18, 19, 20] (x3d, y3d, z3d)
back → [26, 27, 28]
left → [34, 35, 36]
right → [42, 43, 44]
行人/骑手/自行车:使用整体中心点 [5, 6, 7] 进行比较。
误差指标:
- 横向误差(Lateral Error):
|x3d_pred - x3d_gt|(米) - 纵向误差(Longitudinal Error):
|z3d_pred - z3d_gt|(米) - Heading 误差:
|rot_y_pred - rot_y_gt|(弧度)
每个指标输出:均值(Mean)、中位数(Median)、标准差(Std)、90 百分位数(P90)。
8. ROI GT 处理
动机
训练时图像经过 ROI 裁剪和缩放(坐标系改变),但历史评测代码中检测结果在 ROI 坐标系,GT 在原图坐标系,导致 IoU 计算不准确。
解决方案
ROIProcessor(eval_tools/evaluator/roi_processor.py)在评测时对 GT 标签执行与训练完全相同的 ROI 处理:
- 读取标定文件(
case_root/camera4.json)中的focal_u, focal_v, cu, cv, pitch - 计算裁剪中心:
cx = oriW // 2,cy = cv - focal_v × tan(pitch × π/180) - 以
(cx, cy)为中心按roi_config裁剪 ROI - 过滤完全在 ROI 外的目标;将部分在 ROI 内的框 clip 到边界
- 将坐标转换到 ROI 相对坐标系 → 再缩放到模型输入分辨率
配置对应关系:
| 训练 ROI 配置 | roi_config 值 |
|---|---|
| ROI0(大 crop) | [1920, 960] |
| ROI1(小 crop) | [704, 352] |
9. 模型对比(共同匹配集)
问题背景
直接比较两个模型的 3D 指标时,若双方匹配到的 GT 对象数量不同(如相差 11.7%),差异可能来自匹配集差异而非模型质量。
解决方案
只统计两个模型都成功匹配的 GT 对象(Common Matches)的 3D 指标。
使用方式
方式一:一键脚本(推荐)
bash eval_tools/model_comparison/compare_models_with_common_matches.sh
该脚本自动完成:
- 评测 Model 1,保存
detailed_3d_matches.json - 评测 Model 2,保存
detailed_3d_matches.json - 调用
find_common_matches.py得到common_matches.json - 调用
compare_models.py生成对比报告 - 调用
generate_eval_report.py为各模型生成 Markdown 报告
方式二:手动分步执行
# Step 1: 评测两个模型
python eval_tools/core/eval.py \
--config eval_tools/configs/eval_config_mono3d-roi1.yaml \
--output-dir eval_results/model1 \
--heading-tolerance both \
--save-detailed-matches
python eval_tools/core/eval.py \
--config eval_tools/configs/eval_config_yolov5s_cncap-roi1.yaml \
--output-dir eval_results/model2 \
--heading-tolerance both \
--save-detailed-matches
# Step 2: 找出共同匹配
python eval_tools/model_comparison/find_common_matches.py \
--model1-matches eval_results/model1/detailed_3d_matches.json \
--model2-matches eval_results/model2/detailed_3d_matches.json \
--output eval_results/common_matches.json \
--model1-name "mono3d" \
--model2-name "yolov5s-300w"
# Step 3: 生成对比报告
python eval_tools/model_comparison/compare_models.py \
--model1 eval_results/model1/evaluation_report.json \
--model2 eval_results/model2/evaluation_report.json \
--common-matches eval_results/common_matches.json \
--output-dir eval_results/comparison \
--model1-name "mono3d" \
--model2-name "yolov5s-300w"
生成 Markdown 报告
python eval_tools/model_comparison/generate_eval_report.py \
eval_results/model1/evaluation_report.json \
--model "yolov5s-300w-cncap" \
--date 2026-03-03
输出:eval_results/model1/EVALUATION_REPORT.md
10. Heading 误差分析
对于 heading 预测较差的案例,可使用以下工具进行深入分析:
Step 1:提取 bad cases
python eval_tools/heading_analysis/extract_bad_heading_cases.py \
--input eval_results/model1/detailed_3d_matches.json \
--threshold 1.5 \ # heading 误差阈值(弧度)
--top-k 100 \
--output bad_heading.json \
--stats
Step 2:可视化
python eval_tools/heading_analysis/visualize_heading_errors.py \
--input bad_heading.json \
--image-root /path/to/images \
--output runs/heading_viz \
--viz-types bev image angle combined
可视化类型:
bev:鸟瞰图(GT / Pred 的 3D 框 + 方向箭头)image:原图视图(2D 框 + 误差标注)angle:角度分析(极坐标 + 柱状图)combined:四宫格综合视图
11. 输出结果
11.1 文件列表
| 文件 | 说明 |
|---|---|
evaluation_report.json |
完整评测报告(JSON 格式,含所有类别和距离区间) |
evaluation_report.txt |
人类可读的文本报告 |
detailed_3d_matches.json |
每帧每目标的 3D 匹配详情(--save-detailed-matches) |
per_case_reports/*.json |
每个 case 的独立报告(per_case_reports: true) |
EVALUATION_REPORT.md |
Markdown 格式摘要报告(由 generate_eval_report.py 生成) |
11.2 JSON 报告结构
{
"evaluation_config": {...},
"2d_evaluation": {
"overall": {
"precision": 0.85,
"recall": 0.78,
"f1_score": 0.81,
"map": 0.72
},
"per_class": {
"vehicle": {"precision": ..., "recall": ..., "ap": ...},
...
}
},
"3d_evaluation": {
"vehicle": {
"overall": {
"n_samples": 440408,
"lateral_mean": 0.647,
"longitudinal_mean": 1.680,
"heading_mean_strict": 0.258
},
"by_distance": {
"[0-10m]": {...},
...
}
}
}
}
11.3 控制台输出示例
3D Metrics:
vehicle [overall]: Lat=0.647m, Long=1.680m, Head=0.258rad (n=440408)
[0-10m]: Lat=0.423m, Long=0.891m, Head=0.153rad (n=98241)
[10-20m]: Lat=0.501m, Long=1.203m, Head=0.177rad (n=142038)
[30-60m]: Lat=1.149m, Long=5.074m, Head=0.607rad (n=18432)
[100-999m]:Lat=2.341m, Long=9.221m, Head=1.036rad (n=871)
12. 使用示例
例 1:单模型评测(当前默认入口)
cd /deeplearning_team/ydong/dongying/projects/yolov5-3d
conda activate slot
bash eval_tools/core/run_eval_with_config.sh
脚本使用 eval_tools/configs/eval_config_yolov5s_cncap-roi0.yaml,结果保存至:
evaluation_results/eval_results_common_match_comparison_cncap_yolov5s_20260303_roi0/
└── yolov5s-300w-newdata-cncap-test/
└── 20260303_HHMMSS/
├── evaluation_report.json
├── evaluation_report.txt
└── detailed_3d_matches.json
例 2:命令行直接评测(不使用配置文件)
python eval_tools/core/eval.py \
--det-path /data1/.../inference_results/model/evalset_roi0 \
--gt-path /data1/xdzhu/Testdata \
--output-dir evaluation_results/my_test \
--heading-tolerance both \
--conf-threshold 0.4 \
--num-workers 16
例 3:仅 2D 评测
python eval_tools/core/eval.py \
--config eval_tools/configs/eval_config_yolov5s_cncap-roi0.yaml \
--eval-2d-only \
--output-dir evaluation_results/2d_only_test
例 4:双模型对比
bash eval_tools/model_comparison/compare_models_with_common_matches.sh
例 5:修改配置直接运行
仅修改 run_eval_with_config.sh 中的以下三个变量即可适配新模型:
MODEL_CONFIG="eval_tools/configs/eval_config_yolov5s_cncap-roi0.yaml"
OUTPUT_BASE="evaluation_results/eval_results_my_model"
MODEL_NAME="my-model-name"
13. 常见问题
Q1:No image pairs found
原因:det_path 与 gt_path 下的 case 名称不匹配,或 det_format / gt_format 设置错误。
排查:
ls ${det_path}/ # 查看 case 目录名
ls ${det_path}/case_xxx/ # 查看是 json_results/ 还是 txt_results/
Q2:IoU 始终为 0,评测指标异常
原因:roi_gt 配置未启用,或 roi_config 与训练时不匹配,导致坐标系不对齐。
排查:检查配置中 roi_gt.enabled: true 且 roi_config 与训练 YAML 中的 roi: 字段一致。
Q3:3D 指标样本数 (n=0)
原因:真值中该类别无完整 3D 标注(仅有 6 维的 2D-only 标注),或 2D 匹配 IoU < 0.5。
Q4:如何添加新模型配置
复制现有配置文件,仅修改 dataset.det_path 和 dataset.gt_path 即可:
cp eval_tools/configs/eval_config_yolov5s_cncap-roi0.yaml \
eval_tools/configs/eval_config_my_model-roi0.yaml
# 编辑新文件中的 det_path 和 output.save_path
Q5:如何加速评测
调大 performance.num_workers(建议不超过 CPU 核数 × 2):
python eval_tools/core/eval.py --config ... --num-workers 64
文档生成日期:2026-03-03