feat(demo): add export and silhouette visualization outputs

Add preprocess-only silhouette export and configurable result exporters so demo runs can be persisted for offline analysis and reproducible evaluation. Include optional parquet support and CLI visualization dumps while updating tests and tracking notes for the verified pipeline/debug workflow.
This commit is contained in:
2026-02-27 17:16:20 +08:00
parent 3496a1beb7
commit f501119d43
10 changed files with 1101 additions and 217 deletions
@@ -427,3 +427,43 @@ Fixed scope-fidelity blocker in `opengait/demo/output.py` where `window` was ser
- Tasks [13/13 compliant]
- Scope [CLEAN/0 issues]
- VERDICT: APPROVE
## Fix: BBox/Mask Coordinate Mismatch (2026-02-27)
### Root Cause
YOLO segmentation outputs have masks at lower resolution than frame-space bounding boxes:
- Frame size: (1440, 2560)
- YOLO mask size: (384, 640)
- BBox in frame space: e.g., (1060, 528, 1225, 962)
When `mask_to_silhouette(mask, bbox)` was called with frame-space bbox on mask-space mask:
1. `_sanitize_bbox()` clamped bbox to mask bounds
2. Result was degenerate crop (1x1 or similar)
3. Zero nonzero pixels → silhouette returned as `None`
4. Pipeline produced no classifications
### Solution
Modified `select_person()` in `opengait/demo/window.py` to scale bbox from frame space to mask space:
1. Extract `orig_shape` from YOLO results (contains original frame dimensions)
2. Calculate scale factors: `scale_x = mask_w / frame_w`, `scale_y = mask_h / frame_h`
3. Scale bbox coordinates before returning
4. Fallback to original bbox if `orig_shape` unavailable (backward compatibility)
### Key Implementation Details
- Validates `orig_shape` is a tuple/list with at least 2 numeric values
- Handles MagicMock in tests by checking type explicitly
- Preserves backward compatibility for cases without `orig_shape`
- No changes needed to `mask_to_silhouette()` itself
### Verification Results
- All 22 window tests pass
- All 33 demo tests pass (4 skipped due to missing Docker)
- Smoke test on `record_camera_5602_20260227_145736.mp4`:
- 56 classifications from 60 frames
- Non-zero confidence values
- Labels: negative/neutral/positive as expected
### Files Modified
- `opengait/demo/window.py`: Added coordinate scaling in `select_person()`