• v1.4.0 8eba91ea18

    jkaindl released this 2026-05-26 15:20:25 +00:00 | 72 commits to main since this release

    When sequential → exhaustive adaptive-retry exhausts itself (retry_hint=None), the pipeline now automatically binary-subdivides the source video, probes each leaf clip with a cheap preprocess+SfM-only run, and combines the surviving leaves through the existing multi-video path. Automates the manual 4-clip workflow that rescued max_strasse in v1.3.0 — no new command, no new flag, just a longer run when the matcher swap isn't enough.

    Added

    • Auto-bisection-rescue — third escalation in run_pipeline_with_adaptive_retry, gated on retry_hint=None, single-video input, and cfg.retry.bisect_enabled (default true).
    • New module src/autosplat/bisection.pyBisectionClip frozen dataclass, build_ffmpeg_cut_command + cut_video (stream-copy, no re-encode), probe_clip (forces colmap.matcher=exhaustive because sequential is unreliable on shorts), bisect_recursively (DFS halt-on-success-per-branch), and rescue_via_bisection orchestrator.
    • Three new [retry] config fields with conservative defaults: bisect_enabled=true, bisect_min_clip_s=60.0 (range 10–600), bisect_max_depth=3 (range 1–6 — depth 3 = ≤ 8 leaves). Disable bisect_enabled for fast-fail in CI.
    • Bisection artefacts persist under <capture_dir>/rescue/clips/<stem>_part_<clip_id>.mp4 (physical sub-clips) and <capture_dir>/rescue/probes/<clip_id>/{frames,colmap} (per-clip SfM workspace) so a partial run is debuggable. clip_id is depth-encoded (0, 0_1, 0_1_0) — leading digit is the first/second half, deeper underscores trace each recursion step.
    • 20 new tests in tests/test_bisection.py + 4 in tests/test_pipeline.py. Two opt-in real-binary integration tests sit behind AUTOSPLAT_BISECTION_E2E=1 plus the existing needs_ffmpeg / needs_colmap markers. Total 293 unit tests passing (uv run pytest -q, up from 265 in v1.3.0).
    • Docs: CAPTURE-GUIDE.md gains an "Auto-bisection internals (v1.4+)" section with the rescue/ layout and clip_id semantics; CONFIGURATION.md [retry] table grows three rows; TROUBLESHOOTING.md replaces the old "if exhaustive also fails, you're stuck" branch with the actual escalation path and structured log events to watch for.

    Fixed

    • bisect_recursively per-branch ffmpeg resiliencecut_video raising subprocess.CalledProcessError on a corrupt sub-range (broken keyframe, container quirk) is now caught, logged as bisection.cut_aborted_branch, and treated as a failed probe; the sibling branch still runs. Previously the whole rescue would abort with a raw CalledProcessError.
    • read_source_video_from_log now returns the most recent pipeline.start — after bisection appends a fresh pipeline.start with the leaf-clip list, autosplat resume and autosplat add-video on a bisected capture see the current state (the leaves) instead of silently re-feeding the original failed input. Pre-v1.4 captures with only one start event behave identically.

    Design Notes

    • Why a separate module? pipeline.py was already 600 LOC; bisection's ~330 LOC of recursion + ffmpeg-cut + probe logic gets isolated in bisection.py and exposes only the orchestrator to pipeline.py. Each unit is testable without the others (the recursion is monkeypatch-friendly via the _probe_fn injection point).
    • Why no WebUI per-clip progress in v1.4? Bisection runs inside the existing sfm stage from the state-machine's perspective; events surface via structured logs only. A WebUI bisect_probe stage with per-clip progress is a v1.4.1 candidate.
    • Why no standalone autosplat rescue command? The user-chosen trigger is the auto-escalation path — a normal autosplat process … just keeps running. A standalone command can be added in v1.4.1 if the auto-path proves clumsy.
    • Why no smart-split? Midpoint binary cuts are the v1.4 strategy. Smart-split at motion-change (OpenCV optical flow) is a v1.4.1 candidate and would only help when the rotation event is concentrated rather than smeared across the clip.

    Follow-ups (resolved post-v1.4.0)

    • Probe-stage compute cost (250 frames × exhaustive matcher)resolved in v1.4.1 via [retry] bisect_probe_target_frames=120 (~4× faster probes).
    • No real-world smoke against a non-trivial structurally-failing captureresolved in v1.4.4 with the max_strasse.MP4 end-to-end run: 490/493 cameras (99.4 %), 2.0 GB scene.ply, 5 h 36 min on M5. Fly-through video.
    Downloads