Files
yolov26_3d/eval_tools/docs/EVAL_FRAMEWORK_GUIDE.md

713 lines
23 KiB
Markdown
Raw Permalink Normal View History

2026-06-24 09:35:46 +08:00
# 模型评测框架使用指南
> 入口脚本:`eval_tools/core/run_eval_with_config.sh`
---
## 目录
1. [框架概览](#1-框架概览)
2. [目录结构](#2-目录结构)
3. [核心流程](#3-核心流程)
4. [配置文件说明](#4-配置文件说明)
5. [命令行参数说明](#5-命令行参数说明)
6. [数据格式](#6-数据格式)
7. [评测指标](#7-评测指标)
8. [ROI GT 处理](#8-roi-gt-处理)
9. [模型对比(共同匹配集)](#9-模型对比共同匹配集)
10. [Heading 误差分析](#10-heading-误差分析)
11. [输出结果](#11-输出结果)
12. [使用示例](#12-使用示例)
13. [常见问题](#13-常见问题)
---
## 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`)
```yaml
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`)
```yaml
image:
width: 1920
height: 1080
```
### 4.3 模型配置与 GT 过滤 (`model`)
```yaml
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`)
```yaml
performance:
num_workers: 32 # 并行 worker 数null = auto-detect CPU 核数)
```
### 4.5 ROI GT 处理 (`roi_gt`)
```yaml
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`)
```yaml
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`)
```yaml
matching:
iou_threshold: 0.5 # 2D 匹配 IoU 阈值
```
### 4.8 2D 指标 (`metrics_2d`)
```yaml
metrics_2d:
enabled: true
conf_threshold: 0.4 # Precision/Recall 置信度阈值
ap_method: "voc2010" # AP 计算方法voc201011点插值/ coco全点插值
```
### 4.9 3D 指标 (`metrics_3d`)
```yaml
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`)
```yaml
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 的算术平均
**匹配规则**
1. 按置信度从高到低排序预测框
2. 与真值框 IoU ≥ 0.5 且类别相同 → TP每个真值框只匹配一次
3. 未匹配预测框 → 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 处理:
1. 读取标定文件(`case_root/camera4.json`)中的 `focal_u, focal_v, cu, cv, pitch`
2. 计算裁剪中心:`cx = oriW // 2``cy = cv - focal_v × tan(pitch × π/180)`
3.`(cx, cy)` 为中心按 `roi_config` 裁剪 ROI
4. 过滤完全在 ROI 外的目标;将部分在 ROI 内的框 clip 到边界
5. 将坐标转换到 ROI 相对坐标系 → 再缩放到模型输入分辨率
**配置对应关系**
| 训练 ROI 配置 | `roi_config` 值 |
|---|---|
| ROI0大 crop | `[1920, 960]` |
| ROI1小 crop | `[704, 352]` |
---
## 9. 模型对比(共同匹配集)
### 问题背景
直接比较两个模型的 3D 指标时,若双方匹配到的 GT 对象数量不同(如相差 11.7%),差异可能来自**匹配集差异**而非模型质量。
### 解决方案
只统计两个模型**都成功匹配**的 GT 对象Common Matches的 3D 指标。
### 使用方式
#### 方式一:一键脚本(推荐)
```bash
bash eval_tools/model_comparison/compare_models_with_common_matches.sh
```
该脚本自动完成:
1. 评测 Model 1保存 `detailed_3d_matches.json`
2. 评测 Model 2保存 `detailed_3d_matches.json`
3. 调用 `find_common_matches.py` 得到 `common_matches.json`
4. 调用 `compare_models.py` 生成对比报告
5. 调用 `generate_eval_report.py` 为各模型生成 Markdown 报告
#### 方式二:手动分步执行
```bash
# 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 报告
```bash
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
```bash
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可视化
```bash
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 报告结构
```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单模型评测当前默认入口
```bash
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命令行直接评测不使用配置文件
```bash
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 评测
```bash
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
bash eval_tools/model_comparison/compare_models_with_common_matches.sh
```
### 例 5修改配置直接运行
仅修改 `run_eval_with_config.sh` 中的以下三个变量即可适配新模型:
```bash
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. 常见问题
### Q1No image pairs found
**原因**`det_path``gt_path` 下的 case 名称不匹配,或 `det_format` / `gt_format` 设置错误。
**排查**
```bash
ls ${det_path}/ # 查看 case 目录名
ls ${det_path}/case_xxx/ # 查看是 json_results/ 还是 txt_results/
```
### Q2IoU 始终为 0评测指标异常
**原因**`roi_gt` 配置未启用,或 `roi_config` 与训练时不匹配,导致坐标系不对齐。
**排查**:检查配置中 `roi_gt.enabled: true``roi_config` 与训练 YAML 中的 `roi:` 字段一致。
### Q33D 指标样本数 (n=0)
**原因**:真值中该类别无完整 3D 标注(仅有 6 维的 2D-only 标注),或 2D 匹配 IoU < 0.5。
### Q4如何添加新模型配置
复制现有配置文件,仅修改 `dataset.det_path``dataset.gt_path` 即可:
```bash
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
```bash
python eval_tools/core/eval.py --config ... --num-workers 64
```
---
*文档生成日期2026-03-03*