festifun/plan/status/lane-B.md
m3ultra bb542960c2 Lane B (M2 + M8 geometry): COLMAP reconstruction, geometry math, graceful degradation
Implements spec M2 (frame sampling, COLMAP orchestration, TXT-model parsing, scene
normalization, pose interpolation, PLY + DB export) and the M8 geometry functions.

geometry.py (M8): slerp_pose (shortest-arc, double-cover), ray_from_pixel (COLMAP
+y-down back-projection), triangulate_rays (closest-point + parallel guard),
nearest_point_on_ray (in-front radius cylinder). Frozen colmap_to_threejs untouched.

frames.py (M2): variance-of-Laplacian sharpness + windowed sharpest-frame sampling.

sfm.py (M2): hand-written COLMAP images/cameras/points3D parsers; normalize_scene
(centroid->0, camera sphere r->10, up->+Y) as one similarity transform over points+poses;
interpolate_poses (slerp+lerp, no extrapolation); build-aware COLMAP CLI orchestration
(3.x/4.x option detection, CPU SIFT, single-camera-per-folder, undistort->TXT, largest
component by images.bin header count); run_reconstruct with full graceful degradation
(COLMAP absent / <60% / <2 videos / no frames / missing files -> poses left untouched).

Tests: 61 pass (24 foundation + 37 lane-B) with independent oracles (scipy Slerp,
projection-inverse, closed-form geometry) and every degradation/DB-safety branch covered.
Validated end-to-end against real COLMAP 4.1.0 (6 components, largest selected, weak ->
graceful degradation, exit 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:40:46 +10:00

60 lines
4.4 KiB
Markdown

# Status — lane-B
## Round 0 — 2026-07-16 — STATUS: ready_to_merge
**Directives acknowledged:** round 0 of plan/DIRECTIVES.md (run order C→A→D→B; merge when
acceptance passes, don't wait on siblings; maintain this status file; evidence discipline).
**Environment:** COLMAP 4.1.0 (CPU-only) + ffmpeg both installed, so the pipeline was
exercised against **real COLMAP end-to-end**, not just the synthetic export path.
**Isolation note (see blocker):** worked in a dedicated git worktree
(`/Users/m3ultra/Documents/festifun-laneB` on `lane/b-recon`) because all lane agents were
sharing one working directory. Only the 6 lane-B-owned files were touched.
**Acceptance checklist** (spec M2 + M8 geometry + lane brief) — all pass:
- [x] `geometry.py` M8 stubs implemented: `slerp_pose` (shortest-arc, double-cover), `ray_from_pixel`
(COLMAP +y-down back-projection), `triangulate_rays` (closest-point + parallel guard),
`nearest_point_on_ray` (in-front radius cylinder). Frozen `colmap_to_threejs`/`quat_to_mat`/
`mat_to_quat` and the 3 frozen POSE_TEST_VECTORS untouched (`git diff 5fa7301` shows 0 removed
lines in test_geometry.py; the frozen funcs are unchanged).
- [x] `frames.py`: `sharpness` (variance of Laplacian) + windowed sharpest-frame `sample_frames`.
- [x] COLMAP TXT parsers (images/cameras/points3D) with hand-written-snippet tests + a truncated-PARAMS
guard (clean ValueError).
- [x] `normalize_scene`: centroid≈0, camera-sphere radius≈10, up≈+Y — verified by `test_normalize_scene_invariants`
and a projection-invariance test (`test_normalize_scene_preserves_projection`).
- [x] pose interpolation (slerp+lerp, `registered=False`, NO extrapolation past first/last registered) —
both guard directions tested.
- [x] PLY export via frozen `synthetic.write_ply`; poses written via atomic `db.set_poses`.
- [x] graceful degradation: COLMAP absent / <60% frames / <2 videos / no frames / missing files
diagnostic, existing poses UNTOUCHED, DB uncorrupted. Mixed-subset case (some videos registered,
others kept) tested for the DB-safety invariant.
- [x] real end-to-end: `synthetic` `reconstruct` ran the full COLMAP CLI (feature_extractor
exhaustive_matcher mapper image_undistorter model_converter my parsers), produced 6
components (2/15/11/16/17/37 imgs), correctly selected the largest by `images.bin` header count,
judged 37/120 (31%) weak, degraded gracefully, **left all 41 synthetic poses/video untouched, exit 0.**
- [x] `pytest` green **61 passed** (24 foundation + 37 lane-B; test_geometry.py + test_sfm.py).
- [x] no edits outside owned files (`git diff 5fa7301 --name-only` = frames/geometry/sfm/test_geometry only;
untracked: test_sfm.py, this status file); no new deps.
**Bugs found & fixed while validating on real COLMAP:**
- COLMAP 4.x renamed `SiftExtraction`/`SiftMatching` `FeatureExtraction`/`FeatureMatching`; option
detected from `--help` (which COLMAP prints to **stderr**) so the pipeline runs on 3.x and 4.x.
- `_largest_model_dir` ranked binary models by file size (tracks keypoints, not image count) now reads
the exact registered-image count from the `images.bin` uint64 header (validated on the real 6-component run).
- workspace cleared before each run so a stale `database.db` can't fail reruns.
**Adversarial review:** 5-lens review + verify pass (23 agents) 18 raw findings, 5 CONFIRMED (2 code, 3
test-coverage), all applied; 13 refuted (incl. triangulate/nearest-point "behind origin" correct-as-written).
**Blockers / questions for coordinator:**
- **Shared working tree collision.** All four lane agents are operating in the *same* working directory
(`/Users/m3ultra/Documents/festifun`), not separate clones/worktrees as `plan/README.md` requires.
Mid-session the shared branch was switched to `lane/d-events` and the tree accumulated uncommitted edits
from lanes A/B/C/D at once. I isolated lane B in its own worktree so nothing is clobbered. **Recommend the
coordinator have each lane use `git worktree`/separate clones, and adjudicate merges centrally**, since a
naive `git branch -f main` from any lane could silently drop a sibling's merge. I did NOT auto-merge to
`main` for this reason lane B is ready and awaiting a safe merge.
**Next:** commit lane B to `lane/b-recon`; coordinator to merge to `main` (safely, given the shared-tree issue).