Skip to content

prefilters

This module implements prefilters for denoisers

Classes:

Functions:

MultiPrefilter

MultiPrefilter(*prefilters: Prefilter)

Bases: PrefBase

Methods:

Attributes:

Source code
518
519
def __init__(self, *prefilters: Prefilter) -> None:
    self.prefilters = prefilters

prefilters instance-attribute

prefilters = prefilters

__call__

__call__(clip: VideoNode, /, **kwargs: Any) -> VideoNode
Source code
521
522
523
524
525
def __call__(self, clip: vs.VideoNode, /, **kwargs: Any) -> vs.VideoNode:  # type: ignore
    for pref in self.prefilters:
        clip = pref(clip)

    return clip

PrefBase

Prefilter

Bases: PrefilterBase

Enum representing available filters.

These are mainly thought of as prefilters for :py:attr:MVTools, but can be used standalone as-is.

Methods:

Attributes:

  • BILATERAL

    Classic bilateral filtering or edge-preserving bilateral multi pass filtering.

  • BM3D

    Normal spatio-temporal denoising using BM3D.

  • DFTTEST

    Denoising in frequency domain with dfttest and an adaptive mask for retaining details.

  • FLUXSMOOTHST

    Perform smoothing using zsmooth.FluxSmoothST

  • GAUSS

    Gaussian blur.

  • MINBLUR

    Minimum difference of a gaussian/median blur

  • NLMEANS

    Denoising with NLMeans.

  • NONE

    Don't do any prefiltering. Returns the clip as-is.

BILATERAL class-attribute instance-attribute

BILATERAL = 6

Classic bilateral filtering or edge-preserving bilateral multi pass filtering.

BM3D class-attribute instance-attribute

BM3D = 5

Normal spatio-temporal denoising using BM3D.

DFTTEST class-attribute instance-attribute

DFTTEST = 3

Denoising in frequency domain with dfttest and an adaptive mask for retaining details.

FLUXSMOOTHST class-attribute instance-attribute

FLUXSMOOTHST = 2

Perform smoothing using zsmooth.FluxSmoothST

GAUSS class-attribute instance-attribute

GAUSS = 1

Gaussian blur.

MINBLUR class-attribute instance-attribute

MINBLUR = 0

Minimum difference of a gaussian/median blur

NLMEANS class-attribute instance-attribute

NLMEANS = 4

Denoising with NLMeans.

NONE class-attribute instance-attribute

NONE = 0

Don't do any prefiltering. Returns the clip as-is.

__call__

__call__(
    clip: VideoNode,
    /,
    *,
    full_range: bool | float = False,
    temp_thr: float | Sequence[float] = 2.0,
    spat_thr: float | Sequence[float] | None = 2.0,
) -> VideoNode
__call__(
    clip: VideoNode,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    *,
    sloc: SLocT | None = {0.0: 4.0, 0.2: 9.0, 1.0: 15.0},
    pref_mask: VideoNode | Literal[False] | tuple[int, int] = (16, 75),
    tbsize: int = 1,
    sbsize: int = 12,
    sosize: int = 6,
    swin: int = 2,
    **kwargs: Any,
) -> VideoNode
__call__(
    clip: VideoNode,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    *,
    strength: SingleOrArr[float] = 7.0,
    tr: SingleOrArr[int] = 1,
    sr: SingleOrArr[int] = 2,
    simr: SingleOrArr[int] = 2,
    device_type: DeviceType = AUTO,
    **kwargs: Any,
) -> VideoNode
__call__(
    clip: VideoNode,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    *,
    arch: type[AbstractBM3D] = ...,
    gpu: bool | None = None,
    sigma: SingleOrArr[float] = ...,
    tr: SingleOrArr[int] = 1,
    profile: Profile = ...,
    ref: VideoNode | None = None,
    refine: int = 1,
) -> VideoNode
__call__(
    clip: VideoNode,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    *,
    sigmaS: float | list[float] | tuple[float | list[float], ...] = 3.0,
    sigmaR: float | list[float] | tuple[float | list[float], ...] = 0.02,
    gpu: bool | None = None,
    **kwargs: Any,
) -> VideoNode
__call__(
    clip: VideoNode,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    **kwargs: Any,
) -> VideoNode
__call__(
    *,
    full_range: bool | float = False,
    temp_thr: float | Sequence[float] = 2.0,
    spat_thr: float | Sequence[float] | None = 2.0
) -> PrefilterPartial
__call__(
    *,
    planes: PlanesT = None,
    full_range: bool | float = False,
    sloc: SLocT | None = {0.0: 4.0, 0.2: 9.0, 1.0: 15.0},
    pref_mask: VideoNode | Literal[False] | tuple[int, int] = (16, 75),
    tbsize: int = 1,
    sbsize: int = 12,
    sosize: int = 6,
    swin: int = 2,
    **kwargs: Any
) -> PrefilterPartial
__call__(
    *,
    planes: PlanesT = None,
    full_range: bool | float = False,
    strength: SingleOrArr[float] = 7.0,
    tr: SingleOrArr[int] = 1,
    sr: SingleOrArr[int] = 2,
    simr: SingleOrArr[int] = 2,
    device_type: DeviceType = AUTO,
    **kwargs: Any
) -> PrefilterPartial
__call__(
    *,
    planes: PlanesT = None,
    full_range: bool | float = False,
    arch: type[AbstractBM3D] = ...,
    gpu: bool = False,
    sigma: SingleOrArr[float] = ...,
    radius: SingleOrArr[int] = 1,
    profile: Profile = ...,
    ref: VideoNode | None = None,
    refine: int = 1
) -> PrefilterPartial
__call__(
    *,
    planes: PlanesT = None,
    full_range: bool | float = False,
    sigmaS: float | list[float] | tuple[float | list[float], ...] = 3.0,
    sigmaR: float | list[float] | tuple[float | list[float], ...] = 0.02,
    gpu: bool | None = None,
    **kwargs: Any
) -> VideoNode
__call__(
    *, planes: PlanesT = None, full_range: bool | float = False, **kwargs: Any
) -> PrefilterPartial
__call__(
    *, planes: PlanesT = None, full_range: bool | float = False, **kwargs: Any
) -> PrefilterPartial
__call__(
    clip: VideoNode,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    **kwargs: Any,
) -> VideoNode
__call__(
    clip: VideoNode | MissingT = MISSING,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    **kwargs: Any,
) -> VideoNode | PrefilterPartial
Source code
488
489
490
491
492
def __call__(  # type: ignore
    self, clip: vs.VideoNode | MissingT = MISSING, /,
    planes: PlanesT = None, full_range: bool | float = False, **kwargs: Any
) -> vs.VideoNode | PrefilterPartial:
    ...

PrefilterBase

Bases: CustomIntEnum

Methods:

__call__

__call__(
    *, planes: PlanesT = None, full_range: bool | float = False, **kwargs: Any
) -> PrefilterPartial
__call__(
    clip: VideoNode,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    **kwargs: Any,
) -> VideoNode
__call__(
    clip: VideoNode | MissingT = MISSING,
    /,
    planes: PlanesT = None,
    full_range: bool | float = False,
    **kwargs: Any,
) -> VideoNode | PrefilterPartial
Source code
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 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
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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
def __call__(  # type: ignore
    self: Prefilter, clip: vs.VideoNode | MissingT = MISSING, /,
    planes: PlanesT = None, full_range: bool | float = False, **kwargs: Any
) -> vs.VideoNode | PrefilterPartial:
    def _run(clip: vs.VideoNode, planes: PlanesT, **kwargs: Any) -> vs.VideoNode:
        assert check_variable(clip, self)

        pref_type = self

        planes = normalize_planes(clip, planes)

        if pref_type == Prefilter.NONE:
            return clip

        if pref_type == Prefilter.MINBLUR:
            return min_blur(clip, **kwargs, planes=planes)

        if pref_type == Prefilter.GAUSS:
            return gauss_blur(clip, kwargs.pop('sigma', 1.5), **kwargs, planes=planes)

        if pref_type == Prefilter.FLUXSMOOTHST:
            temp_thr, spat_thr = kwargs.pop('temp_thr', 2), kwargs.pop('spat_thr', 2)
            return flux_smooth(clip, temp_thr, spat_thr, **kwargs)

        if pref_type == Prefilter.DFTTEST:
            peak = get_peak_value(clip)
            pref_mask: vs.VideoNode | Literal[False] | tuple[int, int] | None = kwargs.pop("pref_mask", None)

            dftt = DFTTest(sloc={0.0: 4, 0.2: 9, 1.0: 15}, tr=0).denoise(
                clip, kwargs.pop("sloc", None), planes=planes, **kwargs
            )

            if pref_mask is False:
                return dftt

            lower, upper = 16., 75.

            if isinstance(pref_mask, tuple):
                lower, upper = pref_mask

            if not isinstance(pref_mask, vs.VideoNode):
                lower, upper = (scale_value(x, 8, clip) for x in (lower, upper))
                pref_mask = norm_expr(
                    get_y(clip),
                    f'x {lower} < {peak} x {upper} > 0 {peak} x {lower} - {peak} {upper} {lower} - / * - ? ?',
                    func=self
                )

            return dftt.std.MaskedMerge(clip, pref_mask, planes)

        if pref_type == Prefilter.NLMEANS:
            kwargs |= dict(strength=7.0, tr=1, sr=2, simr=2) | kwargs | dict(planes=planes)

            return nl_means(clip, **kwargs)

        if pref_type == Prefilter.BM3D:
            bm3d_arch: type[AbstractBM3D] = kwargs.pop('arch', None)
            gpu: bool | None = kwargs.pop('gpu', None)

            if gpu is None:
                gpu = hasattr(core, 'bm3dcuda')

            if bm3d_arch is None:
                if gpu:  # type: ignore
                    bm3d_arch = BM3DCudaRTC if hasattr(core, 'bm3dcuda_rtc') else BM3DCuda
                else:
                    bm3d_arch = BM3DCPU if hasattr(core, 'bm3dcpu') else BM3DM

            if bm3d_arch is BM3DM:
                sigma, profile = 10, Profile.FAST
            elif bm3d_arch is BM3DCPU:
                sigma, profile = 10, Profile.LOW_COMPLEXITY
            elif bm3d_arch in (BM3DCuda, BM3DCudaRTC):
                sigma, profile = 8, Profile.NORMAL
            else:
                raise ValueError

            sigmas = kwargs.pop(
                'sigma', [sigma if 0 in planes else 0, sigma if (1 in planes or 2 in planes) else 0]
            )

            bm3d_args = dict[str, Any](sigma=sigmas, tr=1, profile=profile) | kwargs

            return bm3d_arch.denoise(clip, **bm3d_args)

        if pref_type is Prefilter.BILATERAL:
            sigmaS = cast(float | list[float] | tuple[float | list[float], ...], kwargs.pop('sigmaS', 3.0))
            sigmaR = cast(float | list[float] | tuple[float | list[float], ...], kwargs.pop('sigmaR', 0.02))

            if isinstance(sigmaS, tuple):
                baseS, *otherS = sigmaS
            else:
                baseS, otherS = sigmaS, []

            if isinstance(sigmaR, tuple):
                baseR, *otherR = sigmaR
            else:
                baseR, otherR = sigmaR, []

            base, ref = clip, None
            max_len = max(len(otherS), len(otherR))

            if max_len:
                otherS = list[float | list[float]](reversed(normalize_seq(otherS or baseS, max_len)))
                otherR = list[float | list[float]](reversed(normalize_seq(otherR or baseR, max_len)))

                for siS, siR in zip(otherS, otherR):
                    base, ref = ref or clip, bilateral(base, ref, siS, siR, **kwargs)

            return bilateral(clip, ref, baseS, baseR, **kwargs)

        raise CustomNotImplementedError(func=self, reason=self)

    if clip is MISSING:
        return PrefilterPartial(self, planes, **kwargs)

    out = _run(clip, planes, **kwargs)

    if full_range is not False:
        if full_range is True:
            full_range = 2.0

        return prefilter_to_full_range(out, full_range)

    return out

PrefilterMeta

Bases: EnumMeta

PrefilterPartial

PrefilterPartial(prefilter: Prefilter, planes: PlanesT, **kwargs: Any)

Bases: PrefBase

Methods:

Attributes:

Source code
504
505
506
507
def __init__(self, prefilter: Prefilter, planes: PlanesT, **kwargs: Any) -> None:
    self.prefilter = prefilter
    self.planes = planes
    self.kwargs = kwargs

kwargs instance-attribute

kwargs = kwargs

planes instance-attribute

planes = planes

prefilter instance-attribute

prefilter = prefilter

__call__

__call__(
    clip: VideoNode, /, planes: PlanesT | MissingT = MISSING, **kwargs: Any
) -> VideoNode
Source code
509
510
511
512
513
514
def __call__(  # type: ignore
    self, clip: vs.VideoNode, /, planes: PlanesT | MissingT = MISSING, **kwargs: Any
) -> vs.VideoNode:
    return self.prefilter(
        clip, planes=self.planes if planes is MISSING else planes, **kwargs | self.kwargs
    )

prefilter_to_full_range

prefilter_to_full_range(
    clip: VideoNode, slope: float = 2.0, smooth: float = 0.0625
) -> VideoNode

Converts a clip to full range if necessary and amplifies dark areas. Essentially acts like a luma-based multiplier on the SAD when used as an mvtools prefilter.

Parameters:

  • clip

    (VideoNode) –

    Clip to process.

  • slope

    (float, default: 2.0 ) –

    Slope to amplify the scale of the dark areas relative to bright areas.

  • smooth

    (float, default: 0.0625 ) –

    Indicates the length of the transition between the amplified dark areas and normal range conversion.

Returns:

  • VideoNode

    Range expanded clip.

Source code
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
def prefilter_to_full_range(clip: vs.VideoNode, slope: float = 2.0, smooth: float = 0.0625) -> vs.VideoNode:
    """
    Converts a clip to full range if necessary and amplifies dark areas.
    Essentially acts like a luma-based multiplier on the SAD when used as an mvtools prefilter.

    :param clip:        Clip to process.
    :param slope:       Slope to amplify the scale of the dark areas relative to bright areas.
    :param smooth:      Indicates the length of the transition between the amplified dark areas and normal range conversion.

    :return:            Range expanded clip.
    """

    InvalidColorFamilyError.check(clip, (vs.YUV, vs.GRAY), prefilter_to_full_range)

    clip_range = ColorRange.from_video(clip)
    clip_fmt = get_video_format(clip)

    curve = (slope - 1) * smooth
    luma_expr = (
        'x yrange_in_min - 1 yrange_in_max yrange_in_min - / * 0 1 clip LUMA! '
        '{k} 1 {c} + {c} sin LUMA@ {c} + / - * LUMA@ 1 {k} - * + range_max * '
    )
    chroma_expr = 'x neutral - range_max crange_in_max crange_in_min - / * range_half + round'

    if clip_fmt.sample_type is vs.INTEGER:
        luma_expr += 'round'

    planes = 0 if clip_range.is_full or clip_fmt.sample_type is vs.FLOAT else None

    return ColorRange.FULL.apply(norm_expr(clip, (luma_expr, chroma_expr), k=curve, c=smooth, planes=planes))