380 lines
10 KiB
Markdown
380 lines
10 KiB
Markdown
|
|
# Example: Using 2-Level Path Structure for Model Evaluation
|
||
|
|
|
||
|
|
This example demonstrates how to evaluate models with 2-level directory structures.
|
||
|
|
|
||
|
|
## Scenario
|
||
|
|
|
||
|
|
You have organized your test data with an additional level of hierarchy:
|
||
|
|
|
||
|
|
```
|
||
|
|
/data/detections/
|
||
|
|
├── G1M3_AFS1616/ # Level 1: Dataset/configuration name
|
||
|
|
│ ├── case_001/ # Level 2: Individual test cases
|
||
|
|
│ │ └── txt_results/
|
||
|
|
│ │ ├── frame001.txt
|
||
|
|
│ │ └── frame002.txt
|
||
|
|
│ └── case_002/
|
||
|
|
│ └── txt_results/
|
||
|
|
│ └── frame001.txt
|
||
|
|
└── G1M3_AFS1920/
|
||
|
|
└── case_003/
|
||
|
|
└── txt_results/
|
||
|
|
└── frame001.txt
|
||
|
|
|
||
|
|
/data/ground_truth/
|
||
|
|
├── G1M3_AFS1616/
|
||
|
|
│ ├── case_001/
|
||
|
|
│ │ └── labels/
|
||
|
|
│ │ ├── frame001.txt
|
||
|
|
│ │ └── frame002.txt
|
||
|
|
│ └── case_002/
|
||
|
|
│ └── labels/
|
||
|
|
│ └── frame001.txt
|
||
|
|
└── G1M3_AFS1920/
|
||
|
|
└── case_003/
|
||
|
|
└── labels/
|
||
|
|
└── frame001.txt
|
||
|
|
```
|
||
|
|
|
||
|
|
## Step 1: Create Configuration File
|
||
|
|
|
||
|
|
Create `eval_tools/configs/eval_config_2level_example.yaml`:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# Evaluation Configuration for 2-Level Path Structure
|
||
|
|
dataset:
|
||
|
|
det_path: "/data/detections"
|
||
|
|
gt_path: "/data/ground_truth"
|
||
|
|
path_depth: 2 # Enable 2-level structure
|
||
|
|
|
||
|
|
image:
|
||
|
|
width: 1920
|
||
|
|
height: 1080
|
||
|
|
|
||
|
|
model:
|
||
|
|
input_size: 704
|
||
|
|
min_box_size_at_input_scale: 8
|
||
|
|
|
||
|
|
performance:
|
||
|
|
num_workers: 32
|
||
|
|
|
||
|
|
roi_gt:
|
||
|
|
enabled: true
|
||
|
|
calib_root: "/data/ground_truth"
|
||
|
|
roi_config: [1920, 960]
|
||
|
|
|
||
|
|
roi:
|
||
|
|
enabled: false
|
||
|
|
region: [0, 120, 1920, 1080]
|
||
|
|
input_size: [704, 352]
|
||
|
|
|
||
|
|
classes:
|
||
|
|
3d_classes: [0, 1, 2, 3]
|
||
|
|
2d_classes: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
|
||
|
|
class_names:
|
||
|
|
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"
|
||
|
|
|
||
|
|
matching:
|
||
|
|
iou_threshold: 0.5
|
||
|
|
|
||
|
|
metrics_2d:
|
||
|
|
enabled: true
|
||
|
|
conf_threshold: 0.3
|
||
|
|
ap_method: "voc2010"
|
||
|
|
|
||
|
|
metrics_3d:
|
||
|
|
enabled: true
|
||
|
|
heading_tolerance: "both"
|
||
|
|
distance_ranges:
|
||
|
|
- [0, 20]
|
||
|
|
- [20, 40]
|
||
|
|
- [40, 60]
|
||
|
|
- [60, 80]
|
||
|
|
- [80, 100]
|
||
|
|
- [100, 999]
|
||
|
|
lateral_distance_ranges:
|
||
|
|
- [-50, -40]
|
||
|
|
- [-40, -30]
|
||
|
|
- [-30, -20]
|
||
|
|
- [-20, -10]
|
||
|
|
- [-10, 0]
|
||
|
|
- [0, 10]
|
||
|
|
- [10, 20]
|
||
|
|
- [20, 30]
|
||
|
|
- [30, 40]
|
||
|
|
- [40, 50]
|
||
|
|
|
||
|
|
output:
|
||
|
|
save_path: "evaluation_results/2level_example/{timestamp}"
|
||
|
|
formats: ["json", "txt"]
|
||
|
|
print_details: true
|
||
|
|
per_case_reports: true
|
||
|
|
```
|
||
|
|
|
||
|
|
## Step 2: Run Evaluation
|
||
|
|
|
||
|
|
### Method 1: Using Config File
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python eval_tools/core/eval.py \
|
||
|
|
--config eval_tools/configs/eval_config_2level_example.yaml \
|
||
|
|
--save-detailed-matches
|
||
|
|
```
|
||
|
|
|
||
|
|
### Method 2: Command Line Override
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python eval_tools/core/eval.py \
|
||
|
|
--config eval_tools/configs/eval_config_2level_example.yaml \
|
||
|
|
--path-depth 2 \
|
||
|
|
--det-path /data/detections \
|
||
|
|
--gt-path /data/ground_truth \
|
||
|
|
--output-dir evaluation_results/custom_output
|
||
|
|
```
|
||
|
|
|
||
|
|
### Method 3: Without Config File
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python eval_tools/core/eval.py \
|
||
|
|
--det-path /data/detections \
|
||
|
|
--gt-path /data/ground_truth \
|
||
|
|
--path-depth 2 \
|
||
|
|
--img-width 1920 \
|
||
|
|
--img-height 1080 \
|
||
|
|
--iou-threshold 0.5 \
|
||
|
|
--conf-threshold 0.3 \
|
||
|
|
--heading-tolerance both \
|
||
|
|
--output-dir evaluation_results/2level_test
|
||
|
|
```
|
||
|
|
|
||
|
|
## Step 3: Compare Two Models with 2-Level Paths
|
||
|
|
|
||
|
|
### Create Config for Model 1
|
||
|
|
|
||
|
|
`eval_tools/configs/eval_config_model1_2level.yaml`:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
dataset:
|
||
|
|
det_path: "/data/model1/detections"
|
||
|
|
gt_path: "/data/ground_truth"
|
||
|
|
path_depth: 2
|
||
|
|
|
||
|
|
# ... other settings ...
|
||
|
|
```
|
||
|
|
|
||
|
|
### Create Config for Model 2
|
||
|
|
|
||
|
|
`eval_tools/configs/eval_config_model2_2level.yaml`:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
dataset:
|
||
|
|
det_path: "/data/model2/detections"
|
||
|
|
gt_path: "/data/ground_truth"
|
||
|
|
path_depth: 2
|
||
|
|
|
||
|
|
# ... other settings ...
|
||
|
|
```
|
||
|
|
|
||
|
|
### Update Comparison Script
|
||
|
|
|
||
|
|
Edit `eval_tools/model_comparison/compare_models_with_common_matches.sh`:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
#!/bin/bash
|
||
|
|
|
||
|
|
set -e
|
||
|
|
|
||
|
|
# Configuration
|
||
|
|
MODEL1_CONFIG="eval_tools/configs/eval_config_model1_2level.yaml"
|
||
|
|
MODEL2_CONFIG="eval_tools/configs/eval_config_model2_2level.yaml"
|
||
|
|
OUTPUT_BASE="evaluation_results/2level_comparison"
|
||
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||
|
|
|
||
|
|
MODEL1_NAME="model1"
|
||
|
|
MODEL2_NAME="model2"
|
||
|
|
|
||
|
|
# Step 1: Evaluate Model 1
|
||
|
|
echo "Step 1: Evaluating Model 1..."
|
||
|
|
MODEL1_OUTPUT="${OUTPUT_BASE}/${MODEL1_NAME}/${TIMESTAMP}"
|
||
|
|
python eval_tools/core/eval.py \
|
||
|
|
--config ${MODEL1_CONFIG} \
|
||
|
|
--output-dir ${MODEL1_OUTPUT} \
|
||
|
|
--save-detailed-matches
|
||
|
|
|
||
|
|
# Step 2: Evaluate Model 2
|
||
|
|
echo "Step 2: Evaluating Model 2..."
|
||
|
|
MODEL2_OUTPUT="${OUTPUT_BASE}/${MODEL2_NAME}/${TIMESTAMP}"
|
||
|
|
python eval_tools/core/eval.py \
|
||
|
|
--config ${MODEL2_CONFIG} \
|
||
|
|
--output-dir ${MODEL2_OUTPUT} \
|
||
|
|
--save-detailed-matches
|
||
|
|
|
||
|
|
# Step 3: Find common matches
|
||
|
|
echo "Step 3: Finding common matches..."
|
||
|
|
COMMON_MATCHES_DIR="${OUTPUT_BASE}/common_matches_${TIMESTAMP}"
|
||
|
|
mkdir -p ${COMMON_MATCHES_DIR}
|
||
|
|
|
||
|
|
python eval_tools/model_comparison/find_common_matches.py \
|
||
|
|
--model1-matches ${MODEL1_OUTPUT}/detailed_3d_matches.json \
|
||
|
|
--model2-matches ${MODEL2_OUTPUT}/detailed_3d_matches.json \
|
||
|
|
--output ${COMMON_MATCHES_DIR}/common_matches.json \
|
||
|
|
--model1-name "${MODEL1_NAME}" \
|
||
|
|
--model2-name "${MODEL2_NAME}"
|
||
|
|
|
||
|
|
# Step 4: Compare models
|
||
|
|
echo "Step 4: Comparing models..."
|
||
|
|
COMPARISON_DIR="${OUTPUT_BASE}/comparison_${TIMESTAMP}"
|
||
|
|
|
||
|
|
python eval_tools/model_comparison/compare_models.py \
|
||
|
|
--model1 ${MODEL1_OUTPUT}/evaluation_report.json \
|
||
|
|
--model2 ${MODEL2_OUTPUT}/evaluation_report.json \
|
||
|
|
--model1-name "${MODEL1_NAME}" \
|
||
|
|
--model2-name "${MODEL2_NAME}" \
|
||
|
|
--common-matches ${COMMON_MATCHES_DIR}/common_matches.json \
|
||
|
|
--output-dir ${COMPARISON_DIR}
|
||
|
|
|
||
|
|
echo "✓ Comparison complete!"
|
||
|
|
echo "Results: ${COMPARISON_DIR}/comparison_report.txt"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Run Comparison
|
||
|
|
|
||
|
|
```bash
|
||
|
|
bash eval_tools/model_comparison/compare_models_with_common_matches.sh
|
||
|
|
```
|
||
|
|
|
||
|
|
## Expected Output
|
||
|
|
|
||
|
|
```
|
||
|
|
================================================================================
|
||
|
|
YOLOv5-3D Model Evaluation
|
||
|
|
================================================================================
|
||
|
|
Detection path: /data/detections
|
||
|
|
Ground truth path: /data/ground_truth
|
||
|
|
Path depth: 2
|
||
|
|
Output directory: evaluation_results/2level_example/20260211_143022
|
||
|
|
Image size: 1920x1080
|
||
|
|
IoU threshold: 0.5
|
||
|
|
Confidence threshold: 0.3
|
||
|
|
AP method: voc2010
|
||
|
|
Heading tolerance: both
|
||
|
|
Number of workers: 32
|
||
|
|
Evaluate 2D: True
|
||
|
|
Evaluate 3D: True
|
||
|
|
Save detailed matches: Yes
|
||
|
|
================================================================================
|
||
|
|
|
||
|
|
Loading data...
|
||
|
|
Found 3 case(s) in detection root: /data/detections (path_depth=2)
|
||
|
|
Loaded 5 image pairs for evaluation
|
||
|
|
|
||
|
|
==================================================
|
||
|
|
Evaluating 2D Detection Metrics
|
||
|
|
==================================================
|
||
|
|
|
||
|
|
Processing case [1/3]: case_001 (2 frames)
|
||
|
|
case_001: 100%|████████████████████| 2/2 [00:00<00:00, 45.23it/s]
|
||
|
|
|
||
|
|
Processing case [2/3]: case_002 (1 frames)
|
||
|
|
case_002: 100%|████████████████████| 1/1 [00:00<00:00, 48.12it/s]
|
||
|
|
|
||
|
|
Processing case [3/3]: case_003 (2 frames)
|
||
|
|
case_003: 100%|████████████████████| 2/2 [00:00<00:00, 46.87it/s]
|
||
|
|
|
||
|
|
==================================================
|
||
|
|
Evaluating 3D Detection Metrics
|
||
|
|
==================================================
|
||
|
|
|
||
|
|
Processing case [1/3]: case_001 (2 frames)
|
||
|
|
case_001: 100%|████████████████████| 2/2 [00:00<00:00, 42.15it/s]
|
||
|
|
|
||
|
|
Processing case [2/3]: case_002 (1 frames)
|
||
|
|
case_002: 100%|████████████████████| 1/1 [00:00<00:00, 43.89it/s]
|
||
|
|
|
||
|
|
Processing case [3/3]: case_003 (2 frames)
|
||
|
|
case_003: 100%|████████████████████| 2/2 [00:00<00:00, 41.76it/s]
|
||
|
|
|
||
|
|
Detailed 3D matches saved to: evaluation_results/2level_example/20260211_143022/detailed_3d_matches.json
|
||
|
|
JSON report saved to: evaluation_results/2level_example/20260211_143022/evaluation_report.json
|
||
|
|
Text report saved to: evaluation_results/2level_example/20260211_143022/evaluation_report.txt
|
||
|
|
Per-case reports saved to: evaluation_results/2level_example/20260211_143022/per_case_reports/ (3 cases)
|
||
|
|
|
||
|
|
================================================================================
|
||
|
|
EVALUATION SUMMARY - OVERALL
|
||
|
|
================================================================================
|
||
|
|
|
||
|
|
2D Metrics:
|
||
|
|
Precision: 0.8542
|
||
|
|
Recall: 0.8123
|
||
|
|
mAP: 0.8234
|
||
|
|
|
||
|
|
3D Metrics:
|
||
|
|
vehicle [overall]: Lat=0.234m, Long=0.456m, Head=0.123rad (relaxed=0.098rad, rev=12) (n=145)
|
||
|
|
pedestrian [overall]: Lat=0.189m, Long=0.312m, Head=0.234rad (relaxed=0.187rad, rev=8) (n=67)
|
||
|
|
================================================================================
|
||
|
|
|
||
|
|
✓ Evaluation completed successfully!
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Issue: No cases found
|
||
|
|
|
||
|
|
**Check:**
|
||
|
|
1. Verify `path_depth` is set correctly (1 or 2)
|
||
|
|
2. Ensure directory structure matches the expected format
|
||
|
|
3. Check that level1 directory names match between det_path and gt_path
|
||
|
|
|
||
|
|
### Issue: Some cases are skipped
|
||
|
|
|
||
|
|
**Check:**
|
||
|
|
1. Each case must have `txt_results/` subdirectory (for detections)
|
||
|
|
2. Each case must have `labels/` subdirectory (for ground truth)
|
||
|
|
3. Frame names must match between detections and labels
|
||
|
|
|
||
|
|
### Issue: GT case directory not found
|
||
|
|
|
||
|
|
**Check:**
|
||
|
|
1. Level1 directory names must be identical in both paths
|
||
|
|
2. Case names must be identical in both paths
|
||
|
|
3. Path structure must be consistent
|
||
|
|
|
||
|
|
## Benefits of 2-Level Structure
|
||
|
|
|
||
|
|
1. **Better Organization**: Group related test cases by dataset or configuration
|
||
|
|
2. **Flexible Comparison**: Compare models across different datasets easily
|
||
|
|
3. **Scalability**: Handle large numbers of test cases more efficiently
|
||
|
|
4. **Backward Compatible**: Existing 1-level structures continue to work
|
||
|
|
|
||
|
|
## Migration from 1-Level to 2-Level
|
||
|
|
|
||
|
|
If you have existing 1-level structure and want to migrate:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Original structure
|
||
|
|
/data/detections/case1/txt_results/
|
||
|
|
/data/detections/case2/txt_results/
|
||
|
|
|
||
|
|
# Create 2-level structure
|
||
|
|
mkdir -p /data/detections_2level/dataset_A
|
||
|
|
mv /data/detections/case1 /data/detections_2level/dataset_A/
|
||
|
|
mv /data/detections/case2 /data/detections_2level/dataset_A/
|
||
|
|
|
||
|
|
# Update config
|
||
|
|
# path_depth: 1 → path_depth: 2
|
||
|
|
# det_path: /data/detections → /data/detections_2level
|
||
|
|
```
|