# Bad Heading Cases Extraction Tool 使用说明 ## 工具概述 `extract_bad_heading_cases.py` 用于从评估结果中提取 heading 误差较大的案例,为后续可视化分析提供数据支持。 ## 功能特性 - ✅ **多条件筛选**:支持误差阈值、类别、距离、置信度等多维度过滤 - ✅ **反转错误检测**:自动识别 ≈180° 的方向反转错误 - ✅ **统计分析**:输出详细的分类统计、误差分布、距离分布等 - ✅ **Top-K 选择**:支持仅提取误差最大的 K 个案例 - ✅ **结构化输出**:生成包含元数据和统计信息的 JSON 文件 ## 快速开始 ### 基本用法 ```bash # 提取所有误差 > 1.5 rad 的案例 python eval_tools/extract_bad_heading_cases.py \ --input eval_results_common_match_comparison/yolov5s-300w/20260203_210259/detailed_3d_matches.json \ --threshold 1.5 \ --output bad_heading_cases.json \ --stats ``` ### 运行测试脚本 ```bash # 给脚本添加执行权限 chmod +x eval_tools/test_extract_bad_cases.sh # 运行多个测试用例 ./eval_tools/test_extract_bad_cases.sh ``` ## 命令行参数 ### 必需参数 - `--input`: 输入的 detailed_3d_matches.json 文件路径 ### 可选参数 #### 筛选参数 - `--threshold`: Heading 误差阈值(弧度),默认 1.5 (≈ 85°) - `--top-k`: 仅提取误差最大的 K 个案例 - `--classes`: 指定类别(可选多个),支持: - `vehicle`: 车辆 - `pedestrian`: 行人 - `bicycle`: 自行车 - `rider`: 骑行者 - `--min-distance`: 最小距离(米) - `--max-distance`: 最大距离(米) - `--min-confidence`: 最小置信度 (0-1) - `--reversal-only`: 仅提取反转错误(误差 > π - 0.1 ≈ 174°) #### 输出参数 - `--output`: 输出 JSON 文件路径,默认:`bad_heading_cases.json` - `--stats`: 打印详细统计信息 ## 使用示例 ### 示例 1:提取所有 bad cases ```bash python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --output all_bad_cases.json \ --stats ``` **输出统计示例**: ``` Processing: 596602 matches... Filtered: 5839 cases === Statistics === vehicle: 4012 cases (68.7% reversal) pedestrian: 1523 cases (42.3% reversal) bicycle: 304 cases (55.6% reversal) Error distribution: Mean: 2.45 rad (140.4°) Median: 2.89 rad (165.6°) Std: 0.78 rad Range: [1.50, 3.14] rad ``` ### 示例 2:提取 Top 50 最差案例 ```bash python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --top-k 50 \ --output top50_worst.json \ --stats ``` **适用场景**: - 聚焦最严重的问题案例 - 快速定位典型错误模式 - 限制可视化数量以提高效率 ### 示例 3:仅提取反转错误 ```bash python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --reversal-only \ --top-k 100 \ --output reversal_errors.json \ --stats ``` **适用场景**: - 专门分析 180° 方向反转问题 - yolov5s-300w 模型的主要问题(68.7% bad cases 是反转错误) ### 示例 4:分析特定类别 ```bash # 仅分析车辆类别 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --classes vehicle \ --min-confidence 0.5 \ --top-k 50 \ --output vehicle_bad_cases.json \ --stats ``` ```bash # 分析多个类别 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --classes vehicle pedestrian \ --output vehicle_pedestrian_cases.json \ --stats ``` ### 示例 5:距离筛选 ```bash # 提取中距离范围的 bad cases (30-80m) python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --min-distance 30 \ --max-distance 80 \ --output mid_distance_cases.json \ --stats ``` ```bash # 提取近距离 bad cases (< 30m) python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --max-distance 30 \ --output near_distance_cases.json \ --stats ``` ### 示例 6:高置信度案例 ```bash # 提取高置信度但方向错误的案例(模型问题而非检测问题) python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --min-confidence 0.7 \ --top-k 50 \ --output high_confidence_bad.json \ --stats ``` ## 输出格式 ### JSON 结构 ```json { "metadata": { "source": "detailed_3d_matches.json", "threshold": 1.5, "total_processed": 596602, "total_extracted": 5839, "filters": { "classes": ["vehicle", "pedestrian"], "min_distance": 30, "max_distance": 80, "min_confidence": 0.5, "reversal_only": false, "top_k": 100 }, "extraction_time": "2026-02-04T10:30:15" }, "statistics": { "vehicle": { "count": 4012, "reversal_count": 3266, "reversal_percentage": 81.4, "avg_error": 2.85, "avg_error_deg": 163.3, "error_distribution": { "min": 1.50, "max": 3.14, "mean": 2.85, "median": 3.02, "std": 0.45 }, "distance_distribution": { "min": 5.2, "max": 98.7, "mean": 45.3, "median": 42.1 }, "confidence_distribution": { "min": 0.25, "max": 0.98, "mean": 0.67 } }, "pedestrian": { "count": 1523, "reversal_count": 644, "reversal_percentage": 42.3, "avg_error": 2.34, "avg_error_deg": 134.1, ... } }, "cases": [ { "case_id": "019b18ef-ca2b-7e97-8ca5-d4b1d7eefdbb_roi0", "frame_id": "019b18f3-8d8e-70bb-8b4e-cef51fbb7089.jpg", "class": "vehicle", "heading_error": 3.1389, "heading_error_deg": 179.87, "gt_rotation": -0.0145, "det_rotation": 3.1244, "is_reversal": true, "distance": 45.23, "confidence": 0.8567, "iou": 0.7234, "gt_center": [42.5, -3.2, 1.5], "det_center": [42.8, -3.4, 1.6], "gt_dimensions": [4.5, 1.8, 1.6], "det_dimensions": [4.3, 1.7, 1.5], "gt_bbox_2d": [120, 340, 245, 480], "det_bbox_2d": [118, 338, 243, 478], "lateral_error": 0.22, "longitudinal_error": 0.31 }, ... ] } ``` ### 字段说明 #### metadata 元数据 - `source`: 输入文件路径 - `threshold`: 使用的误差阈值 - `total_processed`: 处理的总匹配数 - `total_extracted`: 提取的案例数 - `filters`: 应用的筛选条件 - `extraction_time`: 提取时间 #### statistics 统计信息 - `count`: 该类别的案例数量 - `reversal_count`: 反转错误数量 - `reversal_percentage`: 反转错误比例 - `avg_error`: 平均误差(弧度/度) - `error_distribution`: 误差分布统计 - `distance_distribution`: 距离分布统计 - `confidence_distribution`: 置信度分布统计 #### cases 案例详情 - `case_id`, `frame_id`: 案例和帧 ID - `class`: 目标类别 - `heading_error`: Heading 误差(弧度) - `heading_error_deg`: Heading 误差(度) - `gt_rotation`, `det_rotation`: 真值和检测的旋转角度 - `is_reversal`: 是否为反转错误 - `distance`: 目标距离(米) - `confidence`: 检测置信度 - `iou`: 2D IoU - `gt_center`, `det_center`: 3D 中心坐标 - `gt_dimensions`, `det_dimensions`: 3D 尺寸 - `gt_bbox_2d`, `det_bbox_2d`: 2D 边界框 - `lateral_error`, `longitudinal_error`: 横向和纵向误差 ## 数据分析工作流 ### 1. 初步探索 ```bash # 查看所有 bad cases 的统计信息 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --output all_cases.json \ --stats ``` ### 2. 聚焦问题 ```bash # 基于统计结果,聚焦特定问题(如反转错误) python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --reversal-only \ --classes vehicle \ --top-k 100 \ --output vehicle_reversal.json \ --stats ``` ### 3. 可视化分析 ```bash # 使用提取的案例进行可视化(Tool 2) python eval_tools/visualize_heading_errors.py \ --input vehicle_reversal.json \ --gt-dir path/to/labels \ --img-dir path/to/images \ --calib-dir path/to/calib \ --output runs/heading_viz ``` ### 4. 生成报告 ```bash # 生成分析报告(Tool 4) python eval_tools/generate_heading_report.py \ --input vehicle_reversal.json \ --viz-dir runs/heading_viz \ --output heading_analysis_report.html ``` ## 典型分析场景 ### 场景 1:定位反转错误根因 **目标**:理解为什么 yolov5s-300w 有 68.7% 的反转错误 ```bash # Step 1: 提取反转错误 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --reversal-only \ --top-k 200 \ --output reversal_errors.json \ --stats # Step 2: 按类别分析 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --reversal-only \ --classes vehicle \ --output vehicle_reversal.json \ --stats # Step 3: 按距离分析 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --reversal-only \ --min-distance 30 \ --max-distance 60 \ --output mid_dist_reversal.json \ --stats ``` ### 场景 2:分析高置信度错误 **目标**:找出模型"很确信但预测错误"的案例 ```bash python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 2.0 \ --min-confidence 0.8 \ --top-k 50 \ --output high_conf_bad.json \ --stats ``` ### 场景 3:类别间差异分析 **目标**:对比不同类别的 heading 预测问题 ```bash # 车辆 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --classes vehicle \ --top-k 100 \ --output vehicle_analysis.json \ --stats # 行人 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --classes pedestrian \ --top-k 100 \ --output pedestrian_analysis.json \ --stats ``` ### 场景 4:距离敏感性分析 **目标**:分析 heading 误差与距离的关系 ```bash # 近距离 (< 30m) python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --max-distance 30 \ --output near_cases.json \ --stats # 中距离 (30-60m) python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --min-distance 30 \ --max-distance 60 \ --output mid_cases.json \ --stats # 远距离 (> 60m) python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --min-distance 60 \ --output far_cases.json \ --stats ``` ## 常见问题 ### Q1: 如何选择合适的阈值? **A**: 根据评估指标选择: - `1.0 rad (57°)`: 严格标准,仅提取严重错误 - `1.5 rad (86°)`: 推荐标准,平衡数量和质量 - `2.0 rad (115°)`: 宽松标准,包含更多一般错误 ### Q2: 提取的案例太多怎么办? **A**: 使用 `--top-k` 限制数量,或添加更多筛选条件: ```bash python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 1.5 \ --top-k 100 \ --min-confidence 0.5 \ --min-distance 20 \ --max-distance 80 ``` ### Q3: 如何只看反转错误? **A**: 使用 `--reversal-only` 参数: ```bash python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --reversal-only \ --top-k 100 ``` ### Q4: 如何验证提取的案例是否正确? **A**: 使用 `--stats` 查看统计信息,并检查输出 JSON 的前几个案例: ```bash python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --top-k 5 \ --stats # 然后查看输出文件 python -m json.tool bad_heading_cases.json | head -n 100 ``` ## 性能优化 ### 大数据集处理 对于非常大的数据集(如 596K+ 匹配),可以: 1. **先筛选再提取**: ```bash # 先用严格条件提取少量案例 python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --threshold 2.5 \ --top-k 50 ``` 2. **分批处理**: ```bash # 按类别分批 for class in vehicle pedestrian bicycle rider; do python eval_tools/extract_bad_heading_cases.py \ --input detailed_3d_matches.json \ --classes $class \ --top-k 100 \ --output ${class}_bad_cases.json done ``` ## 下一步 提取完案例后,可以使用: 1. **可视化工具** (Tool 2): `visualize_heading_errors.py` - 生成 BEV、图像投影、角度分析图 2. **交互式查看器** (Tool 3): `heading_error_viewer.py` - Web 界面浏览案例 3. **报告生成器** (Tool 4): `generate_heading_report.py` - 生成 HTML/Markdown 分析报告 ## 相关文档 - [Heading Error Visualization Plan](HEADING_ERROR_VISUALIZATION_PLAN.md) - [GT Visualization Guide](../test_scripts/GT_VISUALIZATION_COMPLETE_GUIDE.md) - [Metrics Summary](../eval_results_common_match_comparison_v1/.../METRICS_SUMMARY.md) ## 技术支持 如果遇到问题,请检查: 1. 输入文件路径是否正确 2. JSON 格式是否符合预期(包含 case_id → frames → classes → matches 结构) 3. 筛选条件是否过于严格(导致无结果) 4. Python 环境是否安装必要的库(numpy) ```bash # 检查输入文件结构 python -c "import json; data = json.load(open('detailed_3d_matches.json')); print(f'Cases: {len(data)}, First case: {list(data.keys())[0]}')" ```