--- title: Double Exposure Recovery emoji: 🎞️ colorFrom: indigo colorTo: gray sdk: gradio sdk_version: 6.19.0 app_file: space_app.py pinned: false --- # Double Exposure Recovery Recover the two original images from an accidentally **double-exposed film negative**. Upload a scan of the combined negative; the app proposes multiple physically-consistent scene separations, ranks them with a hybrid physics + perceptual loss, and returns the best reconstruction. > Results are best-effort AI reconstructions, not perfect recoveries. Perfect separation is > information-theoretically impossible in many cases — the goal is the *most plausible* > reconstruction consistent with the observed film densities and natural-image statistics. ## How it works Recovery is treated as an inverse problem grounded in film physics. Two exposures add **linearly in light energy** on the emulsion, so after densitometry (inverting the Hurter–Driffield characteristic curve) the total exposure `Ĥ = H₁ + H₂` is directly measurable per pixel. The problem then reduces to splitting a known non-negative field — the same structure as single-image reflection removal — where priors do the disambiguation. Candidate sources (all ranked by the same hybrid loss): - **Heuristic splits** — fast percentile/spatial baselines (the offline demo floor). - **Residual demixing + VLM front-end** — a physics-grounded alternating loop; uses hosted image/VLM models when API keys are present. - **Double-DIP** — two small untrained CNNs optimized per-image against the film physics. Fully offline, no API key. On the 50-case 256px benchmark this is the strongest offline source (beats the heuristics in 37/45 cases). Opt-in in the UI (slower: ~1–2 min/image). - Optional **physics-guided refinement** of the best candidate (opt-in; re-score gated so it never returns a result worse than its input on the ranking objective). ## Running locally ```bash python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt cp .env.example .env # optional: add REPLICATE_API_TOKEN / ANTHROPIC_API_KEY python -m app.main # serves on http://127.0.0.1:7860 ``` **Demo mode works with no API keys** — the heuristic and Double-DIP sources are fully offline. API keys unlock the generative demixing candidates. ### Tests ```bash pytest -m "not slow" # fast offline suite (~10 s) pytest # includes the slow VAE-path test (downloads weights once) ``` ## Project layout - `film_physics.py` — differentiable `PiecewiseFilmCurve` (H&D curve) + stock presets. - `densitometry.py` — scan → density → exposure, B&W and per-channel color. - `hybrid_loss.py` — physics + LPIPS + anti-degeneracy loss (scoring and refinement). - `latent_optimizer.py` — gradient-based refinement (VAE latent or pixel fallback). - `baselines/double_dip.py` — the Double-DIP offline source. - `app/` — Gradio UI, preprocessing, candidate generation, ranking. - `synth/` — synthetic benchmark + metrics. - `MASTERPLAN.md` — the binding technical strategy and work-package log. - `docs/WP9_DESIGN.md` — design for projection-guided diffusion sampling (not yet built). ## Deployment See [`DEPLOY.md`](DEPLOY.md). The repo is Hugging Face Spaces–ready (the header above configures the Space); the free CPU tier runs demo mode, and API keys are supplied via Space secrets, never committed.