8.2 KiB
Executable File
两级路径下重复 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 作为键:
# 原有代码(有问题)
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 标识符进行分组:
# 修复后的代码
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,并在所有相关位置使用:
# 修复前
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
修改了三个方法:
evaluate_2d()- 2D 评测的分组逻辑(第 494-556 行)evaluate_3d()- 3D 评测的分组逻辑(第 580-662 行)_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:
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.
测试验证了:
- 数据加载阶段正确识别所有 cases(包括重名的)
- 评测阶段正确处理所有 cases(不会合并重名的)
- 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() 方法中自动完成:
# 在 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 报告
# 列出所有 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
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 数量不匹配
检查项:
- 确认已经更新到最新代码
- 确认
path_depth: 2已设置 - 检查是否有其他原因导致 case 被跳过(如缺少文件)
调试方法:
# 在 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 中的 "/" 在文件名中不合法,会被操作系统误认为是目录分隔符
错误示例:
# 错误:会尝试创建 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
解决方案: 代码会自动将 "/" 替换为 "_"
# 正确:创建 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 - 两级路径功能说明
- CALIBRATION_FILE_FIX_CN.md - 校准文件问题修复
总结
此修复确保了评测系统在两级路径结构下能够正确处理重复的 case 名称,通过使用唯一的 case 标识符(level1/case)来区分不同 level1 目录下的同名 case。修改保持了向后兼容性,不影响现有的一级路径结构使用。
修复后,评测结果将更加准确,每个 case 都会被独立评测和报告,不会因为名称重复而被错误地合并。