# 两级路径下重复 Case 名称问题修复 ## 问题描述 在使用两级路径结构(`path_depth: 2`)时,发现: - **数据加载阶段**: 找到 155 个 cases - **评测阶段**: 只处理了 59 个 cases ``` Found 155 case(s) in detection root: ... (path_depth=2) Processing case [1/59]: seq-03 (1278 frames) ``` ## 问题原因 在两级路径结构下,不同的 level1 目录可能包含相同名称的 case: ``` det_root/ dataset_A/ seq-03/ ← 同名 case seq-27/ dataset_B/ seq-03/ ← 同名 case seq-28/ ``` 原有代码在评测阶段按 case 名称分组时,只使用了 `case_name` 作为键: ```python # 原有代码(有问题) cases = {} for pair in self.image_pairs: case_name = pair['case'] # 只使用 case 名称 if case_name not in cases: cases[case_name] = [] cases[case_name].append(pair) ``` 这导致: - `dataset_A/seq-03` 和 `dataset_B/seq-03` 都被归到 `cases['seq-03']` 中 - 两个不同的 case 被合并成一个 - 155 个实际 case 被合并成 59 个唯一名称的 case ## 解决方案 ### 修改分组逻辑 在 `evaluate_2d()` 和 `evaluate_3d()` 方法中,使用唯一的 case 标识符进行分组: ```python # 修复后的代码 cases = {} for pair in self.image_pairs: # 创建唯一的 case 标识符 level1_name = pair.get('level1_name') case_name = pair['case'] if level1_name: case_key = f"{level1_name}/{case_name}" # 两级路径: "dataset_A/seq-03" else: case_key = case_name # 一级路径: "seq-03" if case_key not in cases: cases[case_key] = [] cases[case_key].append(pair) ``` ### 更新循环变量 将循环中的 `case_name` 改为 `case_key`,并在所有相关位置使用: ```python # 修复前 for case_idx, (case_name, case_pairs) in enumerate(cases.items(), 1): print(f"Processing case [{case_idx}/{len(cases)}]: {case_name} ...") self.per_case_metrics_2d[case_name] = ... # 修复后 for case_idx, (case_key, case_pairs) in enumerate(cases.items(), 1): print(f"Processing case [{case_idx}/{len(cases)}]: {case_key} ...") self.per_case_metrics_2d[case_key] = ... ``` ## 修改的文件 **eval_tools/evaluator/evaluator.py** 修改了三个方法: 1. `evaluate_2d()` - 2D 评测的分组逻辑(第 494-556 行) 2. `evaluate_3d()` - 3D 评测的分组逻辑(第 580-662 行) 3. `_write_per_case_reports()` - Per-case 报告生成,添加文件名安全处理(第 862-864 行) 主要改动: - 第 494-507 行:2D 评测的分组和循环 - 第 556 行:保存 per-case 结果时使用 `case_key` - 第 580-596 行:3D 评测的分组和循环 - 第 627、636、640、662 行:保存 detailed matches 时使用 `case_key` - 第 862-864 行:生成报告文件时将 "/" 替换为 "_" ## 测试验证 创建了测试脚本 `eval_tools/tests/test_duplicate_case_names.py`: ```bash python eval_tools/tests/test_duplicate_case_names.py ``` 测试结果: ``` ============================================================ Test Summary ============================================================ ✓ TEST PASSED The fix correctly handles duplicate case names across different level1 directories by using unique case identifiers. ``` 测试验证了: 1. 数据加载阶段正确识别所有 cases(包括重名的) 2. 评测阶段正确处理所有 cases(不会合并重名的) 3. Per-case 报告使用唯一标识符 ## 预期效果 修复后,运行评测时: ### 修复前 ``` Found 155 case(s) in detection root: ... (path_depth=2) Processing case [1/59]: seq-03 (1278 frames) ← 合并了多个同名 case ``` ### 修复后 ``` Found 155 case(s) in detection root: ... (path_depth=2) Processing case [1/155]: dataset_A/seq-03 (640 frames) Processing case [2/155]: dataset_B/seq-03 (638 frames) ← 正确分开 Processing case [3/155]: dataset_A/seq-27 (512 frames) ... ``` ## Case 标识符格式 ### 一级路径(`path_depth: 1`) - Case 标识符: `seq-03` - 显示格式: `seq-03` ### 两级路径(`path_depth: 2`) - Case 标识符: `dataset_A/seq-03` - 显示格式: `dataset_A/seq-03` ## Per-Case 报告 Per-case 报告文件名会将 "/" 替换为 "_" 以兼容文件系统: ### 一级路径 ``` per_case_reports/ seq-03_report.txt seq-27_report.txt ``` ### 两级路径 ``` per_case_reports/ dataset_A_seq-03_report.txt ← "/" 被替换为 "_" dataset_B_seq-03_report.txt dataset_A_seq-27_report.txt ``` **重要**: 由于文件系统不允许文件名中包含 "/",在生成报告文件时,`case_key` 中的 "/" 会被自动替换为 "_"。这个转换在 `_write_per_case_reports()` 方法中自动完成: ```python # 在 evaluator.py 中 for case_name in sorted(case_names): # 将 "/" 替换为 "_" 以兼容文件系统 safe_case_name = case_name.replace('/', '_') case_report_path = os.path.join(per_case_dir, f'{safe_case_name}_report.txt') ``` ## 向后兼容性 - 对于一级路径结构(`path_depth: 1`),`level1_name` 为 `None`,`case_key` 就是 `case_name` - 行为与之前完全相同,不会有任何变化 - 所有现有配置和脚本无需修改 ## 使用示例 ### 示例 1: 查看评测输出 修复前(合并了重名 case): ``` Processing case [1/59]: seq-03 (1278 frames) seq-03: 100%|████████| 1278/1278 [00:05<00:00, 245.67it/s] ``` 修复后(正确分开): ``` Processing case [1/155]: dataset_A/seq-03 (640 frames) dataset_A/seq-03: 100%|████████| 640/640 [00:02<00:00, 248.12it/s] Processing case [2/155]: dataset_B/seq-03 (638 frames) dataset_B/seq-03: 100%|████████| 638/638 [00:02<00:00, 246.89it/s] ``` ### 示例 2: 查看 Per-Case 报告 ```bash # 列出所有 per-case 报告 ls evaluation_results/*/per_case_reports/ # 输出(修复后): dataset_A_seq-03_report.txt dataset_A_seq-27_report.txt dataset_B_seq-03_report.txt dataset_B_seq-28_report.txt ... ``` ### 示例 3: 查看评测报告 JSON ```python import json with open('evaluation_results/.../evaluation_report.json', 'r') as f: report = json.load(f) # 查看 per-case 2D 结果 for case_key, metrics in report['per_case_2d'].items(): print(f"{case_key}: mAP={metrics['overall']['map']:.4f}") # 输出(修复后): # dataset_A/seq-03: mAP=0.8234 # dataset_A/seq-27: mAP=0.8156 # dataset_B/seq-03: mAP=0.8312 # dataset_B/seq-28: mAP=0.8089 ``` ## 故障排查 ### 问题:仍然看到 case 数量不匹配 **检查项**: 1. 确认已经更新到最新代码 2. 确认 `path_depth: 2` 已设置 3. 检查是否有其他原因导致 case 被跳过(如缺少文件) **调试方法**: ```python # 在 load_data_from_paths 后添加调试输出 print(f"Total image pairs: {len(evaluator.image_pairs)}") print(f"Unique cases: {len(set(pair['case'] for pair in evaluator.image_pairs))}") print(f"Unique case_keys: {len(set(f\"{pair.get('level1_name', '')}/{pair['case']}\" for pair in evaluator.image_pairs))}") ``` ### 问题:Per-case 报告文件名包含特殊字符 **原因**: `case_key` 中的 "/" 在文件名中不合法,会被操作系统误认为是目录分隔符 **错误示例**: ```python # 错误:会尝试创建 per_case_reports/20251115/seq-03_report.txt case_report_path = os.path.join(per_case_dir, f'{case_key}_report.txt') # 如果 case_key = "20251115/seq-03",会导致 FileNotFoundError ``` **解决方案**: 代码会自动将 "/" 替换为 "_" ```python # 正确:创建 per_case_reports/20251115_seq-03_report.txt safe_case_name = case_key.replace('/', '_') case_report_path = os.path.join(per_case_dir, f'{safe_case_name}_report.txt') ``` **修复位置**: `evaluator.py` 第 862-864 行 ## 相关文档 - [TWO_LEVEL_PATH_SUPPORT.md](TWO_LEVEL_PATH_SUPPORT.md) - 两级路径功能说明 - [CALIBRATION_FILE_FIX_CN.md](CALIBRATION_FILE_FIX_CN.md) - 校准文件问题修复 ## 总结 此修复确保了评测系统在两级路径结构下能够正确处理重复的 case 名称,通过使用唯一的 case 标识符(`level1/case`)来区分不同 level1 目录下的同名 case。修改保持了向后兼容性,不影响现有的一级路径结构使用。 修复后,评测结果将更加准确,每个 case 都会被独立评测和报告,不会因为名称重复而被错误地合并。