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
30
31
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.

Returns:

  • ConstantFormatVideoNode

    Denoised clip.

Raises:

  • CustomRuntimeError

    If the selected backend is not available or unsupported.

Source code
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def NLMeans(self, clip: vs.VideoNode, *args: Any, **kwargs: Any) -> ConstantFormatVideoNode:
    """
    Applies the Non-Local Means denoising filter using the plugin associated with the selected backend.

    :param clip:                    Source clip.
    :param *args:                   Positional arguments passed to the selected plugin.
    :param **kwargs:                Keyword arguments passed to the selected plugin.
    :raises CustomRuntimeError:     If the selected backend is not available or unsupported.
    :return:                        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 | dict(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 | dict(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:

  • __call__

    :param wref: Amount of original pixel to contribute to the filter output,

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
103
104
105
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
129
130
131
132
133
134
135
136
137
138
139
def __call__(self, wref: float | None = None) -> NLMeans.WeightMode:
    """
    :param wref:    Amount of original pixel to contribute to the filter output,
                    relative to the weight of the most similar pixel found.

    :return:        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
33
34
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
) -> VideoNode

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:

  • VideoNode

    Denoised clip.

Source code
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
@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
) -> vs.VideoNode:
    """
    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, ...)
        ```

    :param clip:            Source clip.
    :param h:               Controls the strength of the filtering.
                            Larger values will remove more noise.
    :param 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.
    :param 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.
    :param 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.
    :param backend:         Set the backend to use for processing.
    :param ref:             Reference clip to do weighting calculation.
                            Also known as the `rclip` parameter.
    :param wmode:           Weighting function to use.
    :param planes:          Which planes to process.
    :param kwargs:          Additional arguments passed to the plugin.

    :return:                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) -> vs.VideoNode:
        return backend.NLMeans(
            clip,
            **{k: p[i] for k, p in params.items()},
            **dict(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})