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

713 lines
23 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 模型评测框架使用指南
> 入口脚本:`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*