# 模型评测框架使用指南 > 入口脚本:`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 计算方法:voc2010(11点插值)/ 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. 常见问题 ### Q1:No 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/ ``` ### 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` 即可: ```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*