For the technically curious
The math, briefly.
Measuring noise without a chart
Everything is calibrated in units of the noise standard deviation σ. The primary
estimator for video is the frame you just watched — on static pixels, frame difference
is pure noise with spread √2σ, regardless of spatial correlation:
σ_T = median(|I_t − I_t±1|) / (0.6745 · √2)
Motion robustness: the unbiased median and 20th-percentile estimates agree on static
footage; motion inflates the median first, so disagreement means "trust only the static
quantile." Spatially, Immerkær's operator (response std = 6σ, rejects gradients) runs at
two scales — and per brightness band, using a 35th percentile so a band full of real
texture can't fake a high reading:
σ_fine = median(|L ∗ I|) / (6 · 0.6745) L = [1 −2 1; −2 4 −2; 1 −2 1]
gain(band) = 1 + w · (σ_band/σ_ref − 1) w = n/(n + 2000) (confidence)
The anti-ghosting stack: a gate that cannot ghost
w = 1 − smoothstep(1.13·g·σ_T, 1.13·g·σ_T + k·g·σ_T, |Δpatch|) g = gain(brightness band)
Full weight while a difference is explainable by noise; exactly zero past the
knee. An earlier version used a Gaussian falloff — its fat tail blended pixels several σ
apart at 30–40% weight and turned slow pans into soup. The hard knee ended that
structurally, and a slow-drift regression test now makes ghosting a build failure.
Since v3.4 the knee is calibrated per brightness band (the g above): shadows on a
real camera run 1.5–2.2× noisier than mids, so a flat knee starved them of averaging —
they read as motion — while highlights got a knee that was too wide. Measured: +1.35 dB
in the shadows on a brightness-profiled scene, highlights unchanged.
Three more layers back the knee up. Motion Tracking re-aims each neighbour
patch before gating — a step-4 coarse search on 2×2 block means (so a big step sees
structure, not aliasing), then a converging ±1 walk, out to ~±8 px — and any shifted
winner faces a 1.7× steeper roll-off plus an acceptance margin, so misaligned
periodic texture cannot blend. Ghost Guard gates the signed patch mean:
w ×= 1 − smoothstep(1.13·g·σ_T, …, |mean_signed(Δpatch)|) σ_mean = √2σ/3 → knee ≈ 2.4σ_mean
Pure-noise differences cancel when averaged with their signs — coherent motion
doesn't — so the slow, subtle smear the magnitude gate physically can't see gets its
weight cut, for ~0.01 dB on static footage. And the firefly zapper clips
single-frame impulses (hot pixels, sensor sparkles) to the 3-frame temporal median —
only when three independent tests agree, so thin fast-moving detail survives. It stays
deliberately un-scaled by brightness: at 6σ, the gain curve cannot flip a genuine
impulse.
Lock what you see (v3.4)
Unlocked, the profile is measured live on every frame — park somewhere and the
numbers follow the content under the box. Lock Profile freezes the playhead
measurement, hardened by a tracked sweep: the patch is followed across the ±4
neighbouring frames by SAD over sampled luma with a zero-motion prior
(featureless patches score every candidate alike, so the prior keeps the box still
instead of letting it random-walk along the noise), measured at its tracked position,
and a frame votes only if its σ lands within 1.6× of the playhead's and its patch
brightness within 0.13. A subject crossing the patch is rejected; the worst case is
exactly the measurement you approved. Auto Region places the box for you: every
candidate patch is scored by local variance minus the expected noise variance at its
brightness, so a dark noisy flat — the best measurement spot on most night footage —
is not mistaken for texture.
Spatial filtering that measures its own inheritance
Compression correlates noise across frames, so temporal averaging removes less
than √N theory promises. Hush re-measures the residual on the merged image itself — at
two scales since v3.2, so blotch that survives the merge cannot hide from a fine-scale
estimator — and tunes non-local means to that measurement: filter strength
h = 1.15σ_residual for luma, 2.2σ for chroma, patch distances bias-corrected by the 2σ²
noise alone contributes. Since v3.3 every chroma estimate is per-channel (blue-channel
night noise puts several times more energy on Cb than Cr, and a combined median splits
the difference badly for both), and Deep Clean can run a gentle 0.6·h pre-pass
whose corrections are hard-clamped to noise size — structure-safe by construction. The
Noise EQ's larger bands work on block means with corrections clipped to noise scale, so
each band removes only its own size; a sparse luma-guided pass reaches 23 px for chroma
blotches and 47 px for luma stains.
Detail Rescue: why cranking it can't blur (v3.1)
out = in − a · clamp(in − NLM(in), −k, +k) k = σ · (2 + 6·(1 − rescue))
Ordinary NR applies the filter's correction in full — push it hard and the correction
at an edge is huge, which is exactly what blur is. Detail Rescue clamps the correction to
a noise-sized bound: in flat areas the correction is noise-sized, so smoothing
passes through untouched, but at an edge or in texture the over-large correction is cut
back to k. The worst case any pixel can move is bounded by construction — so the
strength sliders (now up to 150, EQ Fine to 300 with real h-widening) become safe to
crank. At full crank on the test scene it recovers +3.5 dB of structure.
Measurement hygiene (v3.1)
Exactly-flat samples — letterbox bars, crushed blacks, generated graphics, pixels
bit-identical to all eight neighbours — carry no noise evidence, and enough of them
collapses every median. All estimators now skip them: on a letterboxed test the measured
σ went from collapsed to within 0.3% of truth. The Noise Removed view auto-gains to the
measured noise level with a soft knee (0.5 + 0.5·d/(0.5+|d|)), so clean footage still
reads and highlights compress instead of clipping into blobs.
Engineering
One reference implementation in C++; Metal, CUDA and OpenCL kernels are line-by-line
ports, and a parity harness drives the actual plugin GPU entry point — the one Resolve
calls — asserting agreement with the reference to ~10⁻⁶ across 56 configurations, with
146 synthetic-truth checks gating every build (including "Auto Setup must beat stock
defaults on every footage class", a slow-drift no-ghosting regression, and v3.4's
tracked-sweep suite: the tracker must recover planted shifts, stay put on featureless
patches, and reject an occlusion crossing the sampling region). Even the film grain is
bit-deterministic (integer-hash value noise). Honest limitations: no motion-vector
compensation and no neural net — that's what keeps it fast, predictable, and free.
| Resolution | Best quality (NLM r3 · 5 frames · blotch) | Locked profile | Render Boost (sequential) | Faster mode |
| HD 1920×1080 | 10.2 ms/frame — 98 fps | 8.5 ms — 118 fps | 11.0 ms — 91 fps | 6.2 ms — 162 fps |
| UHD 3840×2160 | 39 ms/frame — 26 fps | 33 ms — 30 fps | 41 ms — 24 fps | 22 ms — 45 fps |