Skip to content

nlm

This module implements a wrapper for non local means denoisers

Functions:

  • nl_means

    Convenience wrapper for NLMeans implementations.

NLMeans

NLMeans(nl_means_func: Callable[P, R])

Bases: Generic[P, R]

Class decorator that wraps the nl_means function and adds enumerations relevant to its implementation.

It is not meant to be used directly.

Classes:

  • Backend

    Enum representing available backends on which to run the plugin.

  • WeightMode

    Enum of weighting modes for Non-Local Means (NLM) denoiser.

Methods:

Source code
36
37
def __init__(self, nl_means_func: Callable[P, R]) -> None:
    self._func = nl_means_func

Backend

Bases: CustomStrEnum

Enum representing available backends on which to run the plugin.

Methods:

  • NLMeans

    Applies the Non-Local Means denoising filter using the plugin associated with the selected backend.

Attributes:

  • ACCELERATOR

    Dedicated OpenCL accelerators.

  • AUTO

    Automatically selects the best available backend.

  • CPU

    An OpenCL device that is the host processor.

  • CUDA

    CUDA (GPU-based) implementation.

  • GPU

    An OpenCL device that is a GPU.

  • ISPC

    ISPC (CPU-based) implementation.

ACCELERATOR class-attribute instance-attribute

ACCELERATOR = 'accelerator'

Dedicated OpenCL accelerators.

AUTO class-attribute instance-attribute

AUTO = 'auto'

Automatically selects the best available backend. Priority: "cuda" -> "accelerator" -> "gpu" -> "cpu" -> "ispc".

CPU class-attribute instance-attribute

CPU = 'cpu'

An OpenCL device that is the host processor.

CUDA class-attribute instance-attribute

CUDA = 'cuda'

CUDA (GPU-based) implementation.

GPU class-attribute instance-attribute

GPU = 'gpu'

An OpenCL device that is a GPU.

ISPC class-attribute instance-attribute

ISPC = 'ispc'

ISPC (CPU-based) implementation.

NLMeans

NLMeans(clip: VideoNode, *args: Any, **kwargs: Any) -> ConstantFormatVideoNode

Applies the Non-Local Means denoising filter using the plugin associated with the selected backend.

Parameters:

  • clip
    (VideoNode) –

    Source clip.

  • *args
    (Any, default: () ) –

    Positional arguments passed to the selected plugin.

  • **kwargs
    (Any, default: {} ) –

    Keyword arguments passed to the selected plugin.

Raises:

  • CustomRuntimeError

    If the selected backend is not available or unsupported.

Returns:

  • ConstantFormatVideoNode

    Denoised clip.

Source code
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def NLMeans(self, clip: vs.VideoNode, *args: Any, **kwargs: Any) -> ConstantFormatVideoNode:  # noqa: N802
    """
    Applies the Non-Local Means denoising filter using the plugin associated with the selected backend.

    Args:
        clip: Source clip.
        *args: Positional arguments passed to the selected plugin.
        **kwargs: Keyword arguments passed to the selected plugin.

    Raises:
        CustomRuntimeError: If the selected backend is not available or unsupported.

    Returns:
        Denoised clip.
    """

    if self == NLMeans.Backend.CUDA:
        return clip.nlm_cuda.NLMeans(*args, **kwargs)

    if self in [NLMeans.Backend.ACCELERATOR, NLMeans.Backend.GPU, NLMeans.Backend.CPU]:
        return clip.knlm.KNLMeansCL(*args, **kwargs | {"device_type": self.value})

    if self == NLMeans.Backend.ISPC:
        return clip.nlm_ispc.NLMeans(*args, **kwargs)

    # Fallback selection based on available plugins
    if hasattr(core, "nlm_cuda"):
        return NLMeans.Backend.CUDA.NLMeans(clip, *args, **kwargs)

    if hasattr(core, "knlm"):
        return clip.knlm.KNLMeansCL(*args, **kwargs | {"device_type": "auto"})

    if hasattr(core, "nlm_ispc"):
        return NLMeans.Backend.ISPC.NLMeans(clip, *args, **kwargs)

    raise CustomRuntimeError(
        "No compatible plugin found. Please install one from: "
        "https://github.com/AmusementClub/vs-nlm-cuda, https://github.com/AmusementClub/vs-nlm-ispc "
        "or https://github.com/Khanattila/KNLMeansCL"
    )

WeightMode

WeightMode(value: int, wref: float | None = None)

Bases: CustomIntEnum

Enum of weighting modes for Non-Local Means (NLM) denoiser.

Methods:

Attributes:

  • BISQUARE_HR

    Modified Bisquare weighting function to be even more robust.

  • BISQUARE_LR

    Modified Bisquare weighting function to be less robust.

  • BISQUARE_THR

    Bisquare weighting function use a soft threshold to compare neighbourhoods.

  • WELSCH

    Welsch weighting function has a faster decay, but still assigns positive weights to dissimilar blocks.

  • wref (float | None) –
Source code
126
127
128
def __init__(self, value: int, wref: float | None = None) -> None:
    self._value_ = value
    self.wref = wref

BISQUARE_HR class-attribute instance-attribute

BISQUARE_HR = 3

Modified Bisquare weighting function to be even more robust.

BISQUARE_LR class-attribute instance-attribute

BISQUARE_LR = 1

Modified Bisquare weighting function to be less robust.

BISQUARE_THR class-attribute instance-attribute

BISQUARE_THR = 2

Bisquare weighting function use a soft threshold to compare neighbourhoods. The weight is 0 as soon as a given threshold is exceeded.

WELSCH class-attribute instance-attribute

WELSCH = 0

Welsch weighting function has a faster decay, but still assigns positive weights to dissimilar blocks. Original Non-local means denoising weighting function.

wref instance-attribute

wref: float | None = wref

__call__

__call__(wref: float | None = None) -> WeightMode

Parameters:

  • wref
    (float | None, default: None ) –

    Amount of original pixel to contribute to the filter output, relative to the weight of the most similar pixel found.

Returns:

Source code
152
153
154
155
156
157
158
159
160
161
162
163
164
def __call__(self, wref: float | None = None) -> NLMeans.WeightMode:
    """
    Args:
        wref: Amount of original pixel to contribute to the filter output, relative to the weight of the most
            similar pixel found.

    Returns:
        Config with weight mode and ref.
    """
    new_enum = CustomIntEnum(self.__class__.__name__, NLMeans.WeightMode.__members__)  # type: ignore
    member = getattr(new_enum, self.name)
    member.wref = wref
    return member

__call__

__call__(*args: args, **kwargs: kwargs) -> R
Source code
39
40
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
    return self._func(*args, **kwargs)

nl_means

nl_means(
    clip: VideoNode,
    h: float | Sequence[float] = 1.2,
    tr: int | Sequence[int] = 1,
    a: int | Sequence[int] = 2,
    s: int | Sequence[int] = 4,
    backend: Backend = AUTO,
    ref: VideoNode | None = None,
    wmode: WeightMode = WELSCH,
    planes: PlanesT = None,
    **kwargs: Any
) -> ConstantFormatVideoNode

Convenience wrapper for NLMeans implementations.

Filter description here.

Example
denoised = nl_means(clip, 0.4, backend=nl_means.Backend.CUDA, ...)

Parameters:

  • clip

    (VideoNode) –

    Source clip.

  • h

    (float | Sequence[float], default: 1.2 ) –

    Controls the strength of the filtering. Larger values will remove more noise.

  • tr

    (int | Sequence[int], default: 1 ) –

    Temporal Radius. Temporal size = (2 * d + 1). Sets the number of past and future frames to uses for denoising the current frame. d=0 uses 1 frame, while d=1 uses 3 frames and so on. Usually, larger values result in better denoising. Also known as the d parameter.

  • a

    (int | Sequence[int], default: 2 ) –

    Search Radius. Spatial size = (2 * a + 1)^2. Sets the radius of the search window. a=1 uses 9 pixel, while a=2 uses 25 pixels and so on. Usually, larger values result in better denoising.

  • s

    (int | Sequence[int], default: 4 ) –

    Similarity Radius. Similarity neighbourhood size = (2 * s + 1) ** 2. Sets the radius of the similarity neighbourhood window. The impact on performance is low, therefore it depends on the nature of the noise.

  • backend

    (Backend, default: AUTO ) –

    Set the backend to use for processing.

  • ref

    (VideoNode | None, default: None ) –

    Reference clip to do weighting calculation. Also known as the rclip parameter.

  • wmode

    (WeightMode, default: WELSCH ) –

    Weighting function to use.

  • planes

    (PlanesT, default: None ) –

    Which planes to process.

  • **kwargs

    (Any, default: {} ) –

    Additional arguments passed to the plugin.

Returns:

  • ConstantFormatVideoNode

    Denoised clip.

Source code
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
@NLMeans
def nl_means(
    clip: vs.VideoNode,
    h: float | Sequence[float] = 1.2,
    tr: int | Sequence[int] = 1,
    a: int | Sequence[int] = 2,
    s: int | Sequence[int] = 4,
    backend: NLMeans.Backend = NLMeans.Backend.AUTO,
    ref: vs.VideoNode | None = None,
    wmode: NLMeans.WeightMode = NLMeans.WeightMode.WELSCH,
    planes: PlanesT = None,
    **kwargs: Any,
) -> ConstantFormatVideoNode:
    """
    Convenience wrapper for NLMeans implementations.

    Filter description [here](https://github.com/Khanattila/KNLMeansCL/wiki/Filter-description).

    Example:
        ```py
        denoised = nl_means(clip, 0.4, backend=nl_means.Backend.CUDA, ...)
        ```

    Args:
        clip: Source clip.
        h: Controls the strength of the filtering. Larger values will remove more noise.
        tr: Temporal Radius. Temporal size = `(2 * d + 1)`. Sets the number of past and future frames to uses for
            denoising the current frame. d=0 uses 1 frame, while d=1 uses 3 frames and so on. Usually, larger values
            result in better denoising. Also known as the `d` parameter.
        a: Search Radius. Spatial size = `(2 * a + 1)^2`. Sets the radius of the search window. a=1 uses 9 pixel, while
            a=2 uses 25 pixels and so on. Usually, larger values result in better denoising.
        s: Similarity Radius. Similarity neighbourhood size = `(2 * s + 1) ** 2`. Sets the radius of the similarity
            neighbourhood window. The impact on performance is low, therefore it depends on the nature of the noise.
        backend: Set the backend to use for processing.
        ref: Reference clip to do weighting calculation. Also known as the `rclip` parameter.
        wmode: Weighting function to use.
        planes: Which planes to process.
        **kwargs: Additional arguments passed to the plugin.

    Returns:
        Denoised clip.
    """

    assert check_variable(clip, nl_means)

    planes = normalize_planes(clip, planes)

    if not planes:
        return clip

    params = dict[str, list[float] | list[int]](h=to_arr(h), d=to_arr(tr), a=to_arr(a), s=to_arr(s))

    # TODO: Remove legacy support for old arguments.
    for sargs, kargs in zip(["strength", "sr", "simr"], ["h", "a", "s"]):
        if sargs in kwargs:
            warnings.warn(f"nl_means: '{sargs}' argument is deprecated, use '{kargs}' instead", DeprecationWarning)
            params[kargs] = to_arr(kwargs.pop(sargs))

    def _nl_means(i: int, channels: str) -> ConstantFormatVideoNode:
        return backend.NLMeans(
            clip,
            **{k: p[i] for k, p in params.items()},
            **{"channels": channels, "rclip": ref, "wmode": wmode, "wref": wmode.wref} | kwargs,
        )

    if clip.format.color_family in {vs.GRAY, vs.RGB}:
        for doc, p in params.items():
            if len(set(p)) > 1:
                warnings.warn(
                    f'nl_means: only "{doc}" first value will be used since clip is {clip.format.color_family.name}',
                    UserWarning,
                )

        return _nl_means(0, "AUTO")

    if (
        all(len(p) < 2 for p in params.values())
        and clip.format.subsampling_w == clip.format.subsampling_h == 0
        and planes == [0, 1, 2]
    ):
        return _nl_means(0, "YUV")

    for k, p in params.items():
        params[k] = normalize_seq(p, 2)

    luma = _nl_means(0, "Y") if 0 in planes else None
    chroma = _nl_means(1, "UV") if 1 in planes or 2 in planes else None

    return join({None: clip, tuple(planes): chroma, 0: luma})