Skip to content

NLMeans

dnjulek edited this page Jul 7, 2026 · 3 revisions

Non-local means denoiser — an OpenCL port matching KNLMeansCL (knlm.KNLMeansCL).

Non-local means denoises by replacing each pixel with a weighted average of pixels across a search window, where a neighbour's weight comes from the similarity of the small patches around the two pixels. It preserves texture and detail that simpler denoisers blur away.

Output matches knlm.KNLMeansCL to the floating-point floor (≈1 ULP; the difference is only native_exp/native_divide rounding).

Usage

vszipcl.NLMeans(vnode clip[, int d=1, int a=2, int s=4, float h=1.2, data channels="auto", int wmode=0, float wref=1.0, vnode rclip, int device_id=0, int num_streams=1])

Parameters

  • clip:
    Clip to process. 8/16-bit integer, 16-bit half, or 32-bit float; Gray / YUV / RGB.
  • d: (Default: 1, range: 0..16)
    Temporal radius. 0 = spatial only (single frame); d > 0 averages over a 2d+1 frame window.
  • a: (Default: 2, range: 1..64)
    Spatial search radius. The search window is (2a+1) × (2a+1).
  • s: (Default: 4, range: 0..8)
    Patch radius (similarity window). The patch is (2s+1) × (2s+1).
  • h: (Default: 1.2)
    Filtering strength. Higher removes more noise (and more detail). Must be > 0.
  • channels: (Default: "auto")
    Which channels to denoise (like KNLMeansCL):
    • Gray: "Y" (or "auto").
    • YUV: "Y" (luma), "UV" (chroma), or "YUV" (all three jointly — requires 4:4:4).
    • RGB: "RGB" (or "auto").
  • wmode: (Default: 0, range: 0..3)
    Weight function applied to the patch distance (matching KNLMeansCL's wmode), where arg is the normalized patch distance:
    • 0: exp(-arg) (exponential decay)
    • 1: max(1 - arg, 0)
    • 2: max(1 - arg, 0)^2
    • 3: max(1 - arg, 0)^8
  • wref: (Default: 1.0)
    Weight of the reference (center) pixel in the average. Must be ≥ 0.
  • rclip: (Default: None)
    Reference / guide clip. When set, the patch weights are computed from rclip while the averaged pixels come from clip (the classic NLM oracle/guide split). Must match clip's format, dimensions and frame count.
  • device_id / num_streams: see Home.

Examples

out = core.vszipcl.NLMeans(src)                              # spatial, luma
out = core.vszipcl.NLMeans(src, d=1, a=3, s=2, h=0.8)        # temporal, stronger search
out = core.vszipcl.NLMeans(src, channels="UV")              # chroma only
out = core.vszipcl.NLMeans(src, channels="YUV")             # all planes (4:4:4)
out = core.vszipcl.NLMeans(src, rclip=prefiltered)          # guided (weights from rclip)

Notes

  • h is depth-independent (defined on the same normalized scale as KNLMeansCL at every bit depth).
  • Integer/half output matches the float-path result requantized (≈1-code from the float floor); f16 output is the IEEE-rounded f32 pipeline.

Benchmark

1080p YUV420, fps (higher is better), RTX 3070 Ti — see Benchmarks for method. d=1, a=2, s=4, h=1.2.

Implementation u16 f16 f32
vszipcl (s=1) 151.0 145.8 117.8
vszipcl (s=2) 155.3 154.7 121.2
nlm_cuda (s=1) 83.2 64.3
nlm_cuda (s=2) 88.3 73.3
knlm 75.8 75.5 62.4

~1.7× nlm_cuda, knlm on shared formats. nlm_cuda rejects f16; knlm has no streams knob. NLMeans issues many small launches/frame, so num_streams=2 gives a real (+6%) boost.

Clone this wiki locally