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>
60 lines
4.4 KiB
Markdown
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).
|