festifun/README.md
m3ultra 8dca2f5154 Coordinator hotfix: Gemini model env-overridable, default gemini-3.1-flash-lite
Google retired gemini-2.5-flash for new API keys (404). Verified
gemini-3.1-flash-lite live: inline video + structured output, 3/3 synthetic
candidates classified. FESTIVAL4D_GEMINI_MODEL overrides. Round 3 addendum
documents the local .env (gitignored) with Gemini + OpenRouter creds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:18:43 +10:00

190 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Festival 4D
Turn multiple fan-shot smartphone videos of the same concert into a synchronized,
explorable **4D experience**: time-aligned multi-video playback, 3D scene reconstruction
with camera poses, a free-roam "god's eye" viewer, AR-style overlays projected onto each
video, AI-tagged moments on a shared timeline, click-to-place 3D annotations, and keyframed
cinematic fly-throughs.
Everything works **from pixels and audio alone** — no depth sensors, no IMU logs. It runs
fully offline and local; the only optional cloud piece is moment classification.
---
## What you can do
- **Scrub a shared timeline** and watch every camera stay locked to the same instant, aligned
by their audio (no manual frame-matching).
- **Fly through the reconstructed scene** in 3D — orbit freely, snap to any real camera, or
play a keyframed cinematic path.
- **See an "x-ray" HUD**: 3D anchor points (stage corners, tagged objects) projected onto every
video, tracking even when occluded by crowd or scaffolding.
- **Jump between AI-tagged moments** (bass drops, pyro, crowd waves…) on the timeline, and
correct any misclassification.
- **Click to locate things in 3D**: draw a box around an object in one or two videos and the
app triangulates its 3D position, dropping an anchor that appears everywhere at once.
---
## Prerequisites
| Tool | Required? | Notes |
|---|---|---|
| **Python 3.11+** + [`uv`](https://docs.astral.sh/uv/) (or venv+pip) | yes | backend |
| **ffmpeg** / **ffprobe** on `PATH` | yes | audio extraction, frame sampling, clip cutting |
| **Node 18+** | yes | frontend (Vite + Three.js) |
| **COLMAP** on `PATH` | optional | 3D reconstruction. Without it the app still runs — you get synced videos + timeline, just no 3D scene. `brew install colmap` on macOS. |
| A classifier API key | optional | AI moment labels. Default **Gemini flash** (`GEMINI_API_KEY`; model `gemini-3.1-flash-lite`, override with `FESTIVAL4D_GEMINI_MODEL`); also Claude or any OpenAI-compatible/local/OpenRouter endpoint via `FESTIVAL4D_CLASSIFIER`. Without a key you still get audio-detected candidate moments, just unlabeled. |
---
## Quickstart (synthetic demo — no footage needed)
The synthetic fixture generates three fake camera videos of the same fake stage, with known
audio offsets and known 3D geometry — so you can see the whole app working end-to-end before
you have any real footage.
```bash
# 1. backend env
uv venv --python 3.12
uv pip install -e ".[dev]"
# 2. generate the synthetic project (fake videos + poses + point cloud + seeded events)
uv run python -m festival4d synthetic
# 3. serve the API (http://127.0.0.1:8000)
uv run python -m festival4d serve
# 4. in another terminal, run the frontend
cd frontend
npm install
npm run dev # opens http://localhost:5173 (or the next free port)
```
Open the printed URL. You should see the 3D scene with three camera frusta around a stage box,
the three synced video players, stage-corner anchors projected onto each video, and colored
moment markers on the timeline.
---
## Real-footage workflow
1. **Shoot / gather** 24 videos of the same performance from different positions (see
*Shooting tips* below) and drop the files into `data/raw/`.
2. **Run the pipeline:**
```bash
uv run python -m festival4d ingest # probe videos, extract 16 kHz mono audio
uv run python -m festival4d sync # GCC-PHAT audio alignment (offsets + drift)
uv run python -m festival4d reconstruct # COLMAP structure-from-motion → camera poses + point cloud
uv run python -m festival4d events # detect audio moments + AI-classify them
uv run python -m festival4d serve # serve it
```
Each step is independent and re-runnable. `sync` alone already gives you locked multi-video
playback; `reconstruct` adds the 3D scene and overlays; `events` adds the tagged timeline.
3. **Explore** in the browser (`cd frontend && npm run dev`).
If COLMAP can't reconstruct your footage (common with dark, motion-blurred, or low-overlap
clips), the pipeline says so and leaves you with the synced-video experience — it never
corrupts existing data.
To enable AI moment labels, set a key before `events`:
```bash
export GEMINI_API_KEY=... # default provider (Gemini flash, native video)
# or: export FESTIVAL4D_CLASSIFIER=claude ANTHROPIC_API_KEY=...
# or: export FESTIVAL4D_CLASSIFIER=local FESTIVAL4D_OPENAI_BASE_URL=... FESTIVAL4D_OPENAI_MODEL=...
```
---
## Using the viewer
| Action | How |
|---|---|
| Play / pause | `Space` or the ⏵ button |
| Nudge time ±1 s (±5 s) | `←` / `→` (hold `Shift`) |
| Scrub | drag the timeline |
| Pick the audio you hear | click a video (only one is unmuted at a time) |
| Enable/disable a video | ◉ on the video |
| Orbit the 3D scene | drag in the 3D pane |
| Snap the 3D view to a camera | click its frustum, its ⛶ button, or press `1``9` |
| Free roam again | `Esc` / `0` / **Free roam** |
| Correct a moment / annotate | click a timeline marker → panel |
| Place a 3D anchor | in the panel, **Annotate location**, then drag a box on a video (repeat on a 2nd video to triangulate) |
| Add a camera keyframe | ** Key** or `K` (captures the current free-roam view at the current time) |
| Play the keyframed path | **▶ Path** · export/import with ⤓ / ⤒ |
The top-right **sync error** panel shows each video's alignment error in ms while playing —
a dev aid; it should stay green (<50 ms) once footage is well-synced.
---
## Architecture
```
data/raw/ ──ingest──▶ data/work/audio ──sync────▶ videos.offset_ms / drift_ppm ┐
──frames──▶ data/work/frames ──reconstruct──▶ camera_poses + points.ply
reference audio ──events──▶ events (audio candidates → AI labels)
SQLite (data/project.db) ◀─────────┘
FastAPI (backend/festival4d/api.py)
│ /api/manifest, /poses, /pointcloud,
│ /events, /anchors, /annotations
Vite + Three.js SPA (frontend/src)
transport (master clock) · videoGrid + overlays · scene3d · timeline
annotate (M8) · camPath (M9)
```
- **Backend** Python: `ingest`/`audio_sync` (media + GCC-PHAT sync), `frames`/`sfm`/`geometry`
(COLMAP orchestration + pose math + triangulation), `events_ai` (moment detection + pluggable
classifiers), `resolve` (annotation3D), `api` (FastAPI, Range-capable video serving), `db`
(SQLAlchemy/SQLite), `synthetic` (the test fixture).
- **Frontend** vanilla JS + Three.js. A single master clock (`transport.js`) derives each
video's local time from the shared timeline and continuously corrects playback; `lib/pose.js`
is the one COLMAPThree.js pose conversion, mirrored from `geometry.py` and locked by a shared
test.
- **Timebase contract:** `t_video = (t_global offset_ms/1000) · (1 + drift_ppm·1e6)`; the
reference video has offset 0.
Full design + milestone history: [`OPUS_BUILD_INSTRUCTIONS.md`](OPUS_BUILD_INSTRUCTIONS.md);
the parallel build plan and per-lane status live in [`plan/`](plan/).
---
## Shooting tips (for good reconstructions)
Concert footage is genuinely hard for structure-from-motion. To give COLMAP a chance:
- **Spread the cameras out** but keep **overlapping views** each pair of cameras should see
some of the same stage/structure. No overlap they can't be related in 3D.
- **Keep some static structure in frame** (stage edges, truss, speaker stacks). A frame that's
all moving crowd and lights has nothing stable to triangulate.
- **Avoid pure zoom** physically moving parallax reconstructs far better than zooming.
- **Brighter, sharper is better** motion blur and near-dark frames are the main failure cause.
- **24 phones is the sweet spot** for a first reconstruction; more is fine but slower.
Audio sync is far more forgiving: any clips that share audible sound (the same music/claps)
will align, even across otherwise-unrelated angles.
---
## Tests
```bash
uv run pytest # backend
cd frontend && npm run build # frontend typecheck/build
```
---
## Non-goals (this prototype)
No accounts/auth, no cloud storage, no realtime ingest, no NeRF/Gaussian splatting, no mobile
UI, no Docker. Single local user, one project at a time.