"""COLMAP orchestration, model parsing, scene normalization, pose export (spec M2, lane B). STUB — lane B fills the bodies; the public signatures below are frozen (``cli.py`` calls ``run_reconstruct``). Pipeline (spec M2, for lane B's reference): - Sample frames (``frames.sample_frames``), then drive the COLMAP CLI via subprocess: ``feature_extractor`` (OPENCV model, single camera per folder) -> ``sequential_matcher`` within each video + ``exhaustive_matcher`` across -> ``mapper`` -> ``model_converter`` to a TXT model. Parse ``images.txt`` / ``cameras.txt`` / ``points3D.txt`` by hand (no pycolmap). - **Normalize** the scene: centroid -> origin, camera bounding sphere radius -> 10, world-up -> average camera up. Apply the same similarity transform to poses and points. - **Interpolate** poses for unregistered frames (slerp rotation, lerp translation, mark ``registered=False``); never extrapolate past the first/last registered frame. - Export ``config.POINTS_PLY`` (binary little-endian, matching ``synthetic.write_ply``'s format) and fill ``camera_poses`` via ``db.set_poses``. - **Failure handling** (spec M2): COLMAP optional at runtime (``shutil.which("colmap")``); if it registers < 60% of frames or < 2 videos into one model, print a clear diagnostic and leave existing poses untouched — never corrupt the DB. The app degrades to "synced videos, no 3D". """ from __future__ import annotations import logging from pathlib import Path log = logging.getLogger("festival4d.sfm") def colmap_available() -> bool: """Whether the COLMAP binary is on PATH (spec M2 optional-at-runtime rule).""" import shutil return shutil.which("colmap") is not None def parse_images_txt(path: Path) -> list[dict]: """Parse a COLMAP ``images.txt`` into per-image pose records (spec M2, lane B).""" raise NotImplementedError("lane B (M2): implement parse_images_txt") def parse_cameras_txt(path: Path) -> dict[int, dict]: """Parse a COLMAP ``cameras.txt`` into ``camera_id -> intrinsics`` (spec M2, lane B).""" raise NotImplementedError("lane B (M2): implement parse_cameras_txt") def parse_points3d_txt(path: Path): """Parse a COLMAP ``points3D.txt`` into ``(points Nx3, colors Nx3)`` (spec M2, lane B).""" raise NotImplementedError("lane B (M2): implement parse_points3d_txt") def normalize_scene(points, poses): """Similarity transform: centroid->origin, camera sphere radius->10, up->+Y (spec M2).""" raise NotImplementedError("lane B (M2): implement normalize_scene") def run_colmap(frames_dir: Path, workspace: Path) -> Path: """Drive the COLMAP CLI and return the exported TXT-model directory (spec M2, lane B).""" raise NotImplementedError("lane B (M2): implement run_colmap") def run_reconstruct() -> dict: """Full reconstruction: sample frames, run COLMAP, normalize, interpolate, export. Entrypoint for ``python -m festival4d reconstruct``. Degrades gracefully when COLMAP is absent or the reconstruction is too weak (spec M2 failure handling): existing poses are left untouched. Returns a summary dict. """ raise NotImplementedError("lane B (M2): implement run_reconstruct")