Open-Set Film Emulation with
Reference-Conditioned 3D LUTs

Deep Analog takes a single reference film scan it has never seen, extracts its color character as a neural 3D LUT plus physical film parameters, and re-renders any target photograph in that look — no per-stock training, no preset library.

Single model · unseen references Photometric ↔ optical separation Exports .cube / .xmp / .costyle Self-supervised — no paired film data All figures: actual model output

01 · Architecture

StyleLUTNet, FilmAnalyzerNet, and the physics-informed renderer

The system splits the problem: what color transform to apply (StyleLUTNet), what the reference's tonal statistics are (analyzer), and how film physically renders (grain & halation parameters). Click any block for details.

color path (LUT) tone statistics physical parameters image flow

Select a block

Click any node in the diagram to see what it does, its exact tensor shapes, and where it appears in the thesis.

02 · Processing Pipeline

Stagewise processing with inspectable intermediates

These are actual intermediate outputs of the trained model, conditioned on the Happy Together (1997) reference frame. Drag the divider to compare each stage against the previous one; use or Play.

stage
compare
BEFORE AFTER
reference
Reference — unseen film scan
Happy Together (dir. Wong Kar-wai, 1997)
green-gold cross-processed palette

RGB histogram — current stage

Diagnostic

03 · LUT Explorer

The predicted LUT as a mapping of RGB space

This is the real 33³ LUT the network predicted from the reference frame (subsampled to 17³ = 4,913 points). Each point sits at its output RGB coordinate and is painted its output color. Drag the morph slider: at identity the points form the untouched RGB cube; morphing to predicted shows how the network bends color space toward the film look.

Morph

Interpretation. A LUT is a function RGB→RGB. The identity cube maps every color to itself. The predicted LUT pulls highlights toward green-gold, compresses shadows, and desaturates cyans — the fingerprint of the cross-processed reference.

Residual parameterization. The network predicts Φ = Φ_id + α·MLP(f) — a deviation from identity, zero-initialized, so training starts from “change nothing” and stays stable (no basis bank, no gate collapse).

04 · Film Physics Lab

Interactive demonstration of the optical renderers

Stages 5–6 are differentiable physical renderers, not filters. This lab re-implements both equations in JavaScript and runs them live on the real stage-4 intermediate. Push the sliders past the fitted defaults to see what each parameter controls.

Grain — multi-scale, luminance-masked

grain = σ · M(L) · Σ wᵢ·noiseᵢ,  M(L)=sigmoid(aL²+bL+c)

Halation — per-channel highlight scatter

halation = I · c · blur(H, r·[1.4, 1.0, 0.7])  (R scatters widest)
Rationale. A LUT is pixel-independent — it can never produce texture or glow. Grain and halation are spatial phenomena, so they live in a separate renderer whose 11 parameters FilmAnalyzerNet estimates from the reference. Red halos spread widest because red light penetrates the emulsion and reflects off the film base.

05 · Formulas in Action

Interactive demonstrations of the core equations

Each widget below implements the exact equation from the paper in JavaScript and lets you probe it. Defaults are the values the system actually measured or predicted for this reference.

Stage 1 — LUT application operator

Differentiable trilinear LUT lookup
out(p) = Σᵢⱼₖ wᵢⱼₖ · Φ[r₀+i, g₀+j, b₀+k],  wᵢⱼₖ = wᵣ(i)·w₉(j)·w_b(k) — piecewise-linear in RGB ⇒ gradients flow to every LUT entry

Click the input photo to sample a pixel, or set RGB directly:

The sampled color falls inside one cell of the LUT grid. Its output is the weighted blend of the 8 surrounding grid entries — line thickness ∝ weight. This is exactly what F.grid_sample computes per pixel, which is why the whole color stage is trainable end-to-end.

input RGB
LUT output

Stage 2 — tone distribution transfer

CDF histogram matching
T(v) = F_ref⁻¹( F_src(v) )

Solid = CDF of the graded image, dashed = CDF of the reference, both computed live from the embedded images. Follow the amber path: v → F_src(v) → jump to the reference CDF → read off T(v). The full curve T (thin amber) is Gaussian-smoothed (σ=2 bins) and blended at strength 0.7 with highlight protection.

Stage 3 — analytic tone response

Black-point lift + highlight rolloff
toe: y = x(1−ℓ)+ℓ, ℓ = 0.8·s·b_ref  ·  shoulder: smoothstep rolloff above knee 0.6

Two parameters — measured from the reference by the statistical analyzer (sliders start at the real measured values) — reproduce film's lifted toe and soft shoulder. Applied after the LUT and CDF stages, it stays a small residual correction rather than a full display-referred sigmoid.

Stage 4 — luminance dependence of grain

Luminance-dependent grain mask
M(L) = sigmoid(aL² + bL + c)

The map shows M(L) evaluated on the real stage-3 intermediate — bright = more grain (contrast-stretched for display). With the fitted defaults the quadratic peaks in the mid-tones, matching silver-halide density: shadows are thin, highlights saturate, grain shows most in between.

Stage 5 — wavelength-dependent halation

Per-channel halation kernels
halation = I · σ(bias) · blur(H, r·[1.4, 1.0, 0.7]),  H = img · sigmoid(20(L−θ))

The map shows which pixels pass the soft threshold — only those scatter. The red kernel spreads 1.4× wider than green and wider than blue: red light penetrates the emulsion, reflects off the film base, and re-exposes the red-sensitive layer — hence film's warm halos.

SwinLUT study — mixture-gate degeneracy

Simulated softmax gate collapse
w = softmax(MLP(v)/τ),  Φ = Σₖ wₖΦₖ  — rich-get-richer: the dominant expert gets more gradient, gets better, attracts more weight
epoch 0 max wₖ entropy H(w) / 1.61

Paper measurements: baseline V1 collapses to mean w₁ = 0.947 (H = 0.16); entropy-regularized V3 uses all five bases (H = 1.58 of 1.61 max). But even fixed, a basis bank is a closed set — which is what motivates predicting one conditional LUT instead (StyleLUTNet).

Self-supervised training protocol

RandomColorTransform pair generation
sample T ~ random(gamma ∘ temperature ∘ saturation ∘ matrix) → reference = T(I_a), input = T(I_b), ground truth = I_b ⇒ network learns T⁻¹
Reference = T(I_a)
what the network is shown
Input = T(I_b)
gets the LUT applied
Ground truth = I_b
reconstruction target

The same random transform hits two different images: one becomes the reference, the other the input. To reconstruct the original, the network must infer the inverse transform from the reference alone — no film scans, no paired data. Transform ranges are set to span and slightly exceed real film variation, which is what buys open-set generalization.

06 · Training at Scale

Datasets, loss functions, and training infrastructure

Behind the demo sit >120M trained parameters across three networks, a curated 5,000-image expert-retouched dataset, a self-supervised pair-generation protocol, and seven model generations trained on RIT Research Computing (SPORC cluster, NVIDIA A100 80 GB). All values below are taken directly from the training job scripts and the experiments section of the thesis.

Compute — RIT Research Computing

SLURM-scheduled A100 jobs on the SPORC cluster
#!/bin/bash #SBATCH --job-name=deep-analog-v6 #SBATCH --account=deep-analog #SBATCH --partition=sporc-gpu #SBATCH --gres=gpu:a100:1 #SBATCH --cpus-per-task=4 #SBATCH --mem=64G #SBATCH --time=12:00:00 #SBATCH --output=/shared/rc/deep-analog/logs/v6_%j.out python train_v6_cluster.py # actual job script from training/train_v6.sh

Every model generation ran as a batch job on RIT's SPORC partition — grain/halation jobs at 48 GB / 4 h walltime, end-to-end V6 at 64 GB / 12 h. Training data, checkpoints and logs lived on cluster storage under /shared/rc/deep-analog.

GPU NVIDIA A100 · 80 GB Scheduler SLURM Precision FP16 AMP StyleLUTNet 82.6M params SwinLUT 28.3M params StyleLUT train 1 h 42 m / A100
Training recipe. AdamW with group-specific learning rates (backbone 5e-5 · heads 5e-4 · LUT params 1e-3) · cosine annealing with warm restarts (T₀=50) · batch 4 · paired random crops 552→480 + flips + input-only brightness jitter · early stopping, patience 25 · StyleLUTNet 100 epochs, FilmAnalyzerNet 50 epochs with 5% identity-pair anchors.

A100-era engineering. Prior LUT work relied on custom C++/CUDA extensions that no longer compile on PyTorch ≥ 2.0 / A100 GPUs; the trilinear operator was re-implemented in pure PyTorch (F.grid_sample) with no throughput penalty and native AMP support.

Data — datasets and pair generation

MIT-Adobe FiveK + self-supervised pairs

5,000 RAW photographs, each hand-retouched by five professional colorists (A–E). SwinLUT trains against Expert C at 480×480, prepared and indexed on cluster storage:

3,150 train
350 test
ABC ✓DE

The open-set stage has no dataset to buy. No corpus pairs arbitrary photos with specific film stocks — building one would mean professionally grading every input against every stock. The RandomColorTransform protocol (section 05) manufactures unlimited supervised pairs with exact ground truth instead, and the 350-pair test set is generated the same way. What pixel metrics then certify — transform inversion, not emulsion fidelity — is assessed separately by a two-round user study.

Objective — loss composition and balancing

StyleLUTNet loss and regularization balance
L_style = L_pixel + λ_s·L_smooth + λ_m·L_mono + λ_h·L_hist

λ_h is the largest coefficient because the self-supervised task is about reproducing global color distributions, not per-pixel error. Push the regularizers up: at roughly 15:1 against reconstruction the optimizer discovers it is cheaper to shrink the residual scale α toward zero than to improve color — the exact failure observed in training (α: 0.10 → 0.04 over 100 epochs, near-identity output), fixed by freezing α = 1 and rebalancing. SwinLUT adds entropy + diversity terms (λ_e = 0.1, λ_d = 0.01); FilmAnalyzerNet uses magnitude-weighted MSE per parameter.

Method development — seven model generations

From closed-set diagnosis to the open-set system
closed-set study & collapse diagnosis film physics end-to-end integration open-set system (deployed)

07 · Result

Qualitative comparison on the demonstration pair

Drag to compare the original digital photograph against the final Deep Analog render conditioned on the Happy Together frame.

final
input
INPUT DEEP ANALOG
22.05 dB
PSNR — StyleLUT color stage (FiveK test, 350 img)
0.925
SSIM — StyleLUT color stage
0.308
LPIPS ↓ — perceptual distance
5.2 ms
LUT predict + apply @1080p (RTX 4060) — real-time capable
.cube
Portable export → DaVinci Resolve, Photoshop, Capture One