# 两级路径下重复 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 都会被独立评测和报告，不会因为名称重复而被错误地合并。
