Skip to content

qtgmc

Classes:

  • QTempGaussMC

    Quasi Temporal Gaussian Motion Compensated (QTGMC)

QTempGaussMC

QTempGaussMC(
    clip: VideoNode,
    input_type: InputType = INTERLACE,
    tff: FieldBasedT | bool | None = None,
)

Bases: vs_object

Quasi Temporal Gaussian Motion Compensated (QTGMC)

A very high quality deinterlacer with a range of features for both quality and convenience. These include extensive noise processing capabilities, support for repair of progressive material, precision source matching, shutter speed simulation, etc. Originally based on TempGaussMC by Didée.

Basic usage:

deinterlace = (
    QTempGaussMC(clip)
    .prefilter()
    .denoise()
    .basic()
    .source_match()
    .lossless()
    .sharpen()
    .back_blend()
    .sharpen_limit()
    .final()
    .motion_blur()
    .process()
)

Parameters:

  • clip

    (VideoNode) –

    Clip to process.

  • input_type

    (InputType, default: INTERLACE ) –

    Indicates processing routine.

  • tff

    (FieldBasedT | bool | None, default: None ) –

    Field order of the clip.

Classes:

  • BackBlendMode

    When to back blend (blurred) difference between pre & post sharpened clip.

  • InputType

    Processing routine to use for the input.

  • LosslessMode

    When to put exact source fields into result & clean any artefacts.

  • NoiseDeintMode

    When noise is taken from interlaced source, how to 'deinterlace' it before restoring.

  • NoiseProcessMode

    How to handle processing noise in the source.

  • SearchPostProcess

    Prefiltering to apply in order to assist with motion search.

  • SharpLimitMode

    How to limit and when to apply re-sharpening of the clip.

  • SharpMode

    How to re-sharpen the clip after temporally blurring.

  • SourceMatchMode

    Creates higher fidelity output with extra processing. will capture more source detail and reduce oversharpening / haloing.

Methods:

Attributes:

  • basic_output (ConstantFormatVideoNode) –

    Output of the basic stage.

  • bobbed (ConstantFormatVideoNode) –

    High quality bobbed clip, initial spatial interpolation.

  • clip (ConstantFormatVideoNode) –

    Clip to process.

  • denoise_output (ConstantFormatVideoNode) –

    Output of the denoise stage.

  • draft (ConstantFormatVideoNode) –

    Draft processed clip, used as a base for prefiltering & denoising.

  • field
  • final_output (ConstantFormatVideoNode) –

    Output of the final stage.

  • input_type
  • motion_blur_output (ConstantFormatVideoNode) –

    Output of the motion blur stage.

  • noise (ConstantFormatVideoNode) –

    Extracted noise when noise processing is enabled.

  • prefilter_output (ConstantFormatVideoNode) –

    Output of the prefilter stage.

  • tff
Source code
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def __init__(
    self,
    clip: vs.VideoNode,
    input_type: InputType = InputType.INTERLACE,
    tff: FieldBasedT | bool | None = None,
) -> None:
    """
    :param clip:          Clip to process.
    :param input_type:    Indicates processing routine.
    :param tff:           Field order of the clip.
    """

    assert check_variable(clip, self.__class__)

    clip_fieldbased = FieldBased.from_param_or_video(tff, clip, True, self.__class__)

    self.clip = clip
    self.input_type = input_type
    self.tff = clip_fieldbased.is_tff
    self.field = -1 if not clip_fieldbased.is_inter else clip_fieldbased.field + 2

    if self.input_type == self.InputType.PROGRESSIVE and clip_fieldbased.is_inter:
        raise CustomRuntimeError(f'{self.input_type} incompatible with interlaced video!', self.__class__)

basic_output instance-attribute

basic_output: ConstantFormatVideoNode

Output of the basic stage.

bobbed instance-attribute

bobbed: ConstantFormatVideoNode

High quality bobbed clip, initial spatial interpolation.

clip instance-attribute

clip: ConstantFormatVideoNode = clip

Clip to process.

denoise_output instance-attribute

denoise_output: ConstantFormatVideoNode

Output of the denoise stage.

draft instance-attribute

draft: ConstantFormatVideoNode

Draft processed clip, used as a base for prefiltering & denoising.

field instance-attribute

field = -1 if not is_inter else field + 2

final_output instance-attribute

final_output: ConstantFormatVideoNode

Output of the final stage.

input_type instance-attribute

input_type = input_type

motion_blur_output instance-attribute

motion_blur_output: ConstantFormatVideoNode

Output of the motion blur stage.

noise instance-attribute

noise: ConstantFormatVideoNode

Extracted noise when noise processing is enabled.

prefilter_output instance-attribute

prefilter_output: ConstantFormatVideoNode

Output of the prefilter stage.

tff instance-attribute

tff = is_tff

BackBlendMode

Bases: CustomIntEnum

When to back blend (blurred) difference between pre & post sharpened clip.

Attributes:

  • BOTH

    Perform back-blending both before and after sharpness limiting.

  • NONE

    No back-blending.

  • POSTLIMIT

    Perform back-blending after sharpness limiting.

  • PRELIMIT

    Perform back-blending prior to sharpness limiting.

BOTH class-attribute instance-attribute

BOTH = 3

Perform back-blending both before and after sharpness limiting.

NONE class-attribute instance-attribute

NONE = 0

No back-blending.

POSTLIMIT class-attribute instance-attribute

POSTLIMIT = 2

Perform back-blending after sharpness limiting.

PRELIMIT class-attribute instance-attribute

PRELIMIT = 1

Perform back-blending prior to sharpness limiting.

InputType

Bases: CustomIntEnum

Processing routine to use for the input.

Attributes:

  • INTERLACE

    Deinterlace interlaced input.

  • PROGRESSIVE

    Deshimmer general progressive material that contains less severe problems.

  • REPAIR

    Repair badly deinterlaced material with considerable horizontal artefacts.

INTERLACE class-attribute instance-attribute

INTERLACE = 0

Deinterlace interlaced input.

PROGRESSIVE class-attribute instance-attribute

PROGRESSIVE = 1

Deshimmer general progressive material that contains less severe problems.

REPAIR class-attribute instance-attribute

REPAIR = 2

Repair badly deinterlaced material with considerable horizontal artefacts.

LosslessMode

Bases: CustomIntEnum

When to put exact source fields into result & clean any artefacts.

Attributes:

  • NONE

    Do not restore source fields.

  • POSTSMOOTH

    Restore source fields after final temporal smooth. True lossless but less stable.

  • PRESHARPEN

    Restore source fields prior to re-sharpening. Not exactly lossless.

NONE class-attribute instance-attribute

NONE = 0

Do not restore source fields.

POSTSMOOTH class-attribute instance-attribute

POSTSMOOTH = 2

Restore source fields after final temporal smooth. True lossless but less stable.

PRESHARPEN class-attribute instance-attribute

PRESHARPEN = 1

Restore source fields prior to re-sharpening. Not exactly lossless.

NoiseDeintMode

Bases: CustomIntEnum

When noise is taken from interlaced source, how to 'deinterlace' it before restoring.

Attributes:

  • BOB

    Bob source noise, results in coarse noise.

  • GENERATE

    Gnerates fresh noise lines.

  • WEAVE

    Double weave source noise, lags behind by one frame.

BOB class-attribute instance-attribute

BOB = 1

Bob source noise, results in coarse noise.

GENERATE class-attribute instance-attribute

GENERATE = 2

Gnerates fresh noise lines.

WEAVE class-attribute instance-attribute

WEAVE = 0

Double weave source noise, lags behind by one frame.

NoiseProcessMode

Bases: CustomIntEnum

How to handle processing noise in the source.

Attributes:

  • DENOISE

    Denoise source & optionally restore some noise back at the end of basic or final stages.

  • IDENTIFY

    Identify noise only & optionally restore some noise back at the end of basic or final stages.

  • NONE

    No noise processing.

DENOISE class-attribute instance-attribute

DENOISE = 1

Denoise source & optionally restore some noise back at the end of basic or final stages.

IDENTIFY class-attribute instance-attribute

IDENTIFY = 2

Identify noise only & optionally restore some noise back at the end of basic or final stages.

NONE class-attribute instance-attribute

NONE = 0

No noise processing.

SearchPostProcess

Bases: CustomIntEnum

Prefiltering to apply in order to assist with motion search.

Attributes:

GAUSSBLUR class-attribute instance-attribute

GAUSSBLUR = 1

Gaussian blur.

GAUSSBLUR_EDGESOFTEN class-attribute instance-attribute

GAUSSBLUR_EDGESOFTEN = 2

Gaussian blur & edge softening.

NONE class-attribute instance-attribute

NONE = 0

No post-processing.

SharpLimitMode

Bases: CustomIntEnum

How to limit and when to apply re-sharpening of the clip.

Attributes:

NONE class-attribute instance-attribute

NONE = 0

No sharpness limiting.

SPATIAL_POSTSMOOTH class-attribute instance-attribute

SPATIAL_POSTSMOOTH = 3

Spatial sharpness limiting after the final stage.

SPATIAL_PRESMOOTH class-attribute instance-attribute

SPATIAL_PRESMOOTH = 1

Spatial sharpness limiting prior to final stage.

TEMPORAL_POSTSMOOTH class-attribute instance-attribute

TEMPORAL_POSTSMOOTH = 4

Temporal sharpness limiting after the final stage.

TEMPORAL_PRESMOOTH class-attribute instance-attribute

TEMPORAL_PRESMOOTH = 2

Temporal sharpness limiting prior to final stage.

SharpMode

Bases: CustomIntEnum

How to re-sharpen the clip after temporally blurring.

Attributes:

  • NONE

    No re-sharpening.

  • UNSHARP

    Re-sharpening using unsharpening.

  • UNSHARP_MINMAX

    Re-sharpening using unsharpening clamped to the local 3x3 min/max average.

NONE class-attribute instance-attribute

NONE = 0

No re-sharpening.

UNSHARP class-attribute instance-attribute

UNSHARP = 1

Re-sharpening using unsharpening.

UNSHARP_MINMAX class-attribute instance-attribute

UNSHARP_MINMAX = 2

Re-sharpening using unsharpening clamped to the local 3x3 min/max average.

SourceMatchMode

Bases: CustomIntEnum

Creates higher fidelity output with extra processing. will capture more source detail and reduce oversharpening / haloing.

Attributes:

  • BASIC

    Conservative halfway stage that rarely introduces artefacts.

  • NONE

    No source match processing.

  • REFINED

    Restores almost exact source detail but is sensitive to noise & can introduce occasional aliasing.

  • TWICE_REFINED

    Restores almost exact source detail.

BASIC class-attribute instance-attribute

BASIC = 1

Conservative halfway stage that rarely introduces artefacts.

NONE class-attribute instance-attribute

NONE = 0

No source match processing.

REFINED class-attribute instance-attribute

REFINED = 2

Restores almost exact source detail but is sensitive to noise & can introduce occasional aliasing.

TWICE_REFINED class-attribute instance-attribute

TWICE_REFINED = 3

Restores almost exact source detail.

apply_back_blend

apply_back_blend(flt: VideoNode, src: VideoNode) -> ConstantFormatVideoNode
Source code
843
844
845
846
847
848
849
def apply_back_blend(self, flt: vs.VideoNode, src: vs.VideoNode) -> ConstantFormatVideoNode:
    assert check_variable(flt, self.apply_back_blend)

    if self.backblend_sigma:
        flt = flt.std.MakeDiff(gauss_blur(flt.std.MakeDiff(src), self.backblend_sigma))

    return flt

apply_basic

apply_basic() -> None
Source code
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
def apply_basic(self) -> None:
    if self.input_type == self.InputType.PROGRESSIVE:
        self.bobbed = self.denoise_output
    else:
        self.bobbed = self.basic_bobber.interpolate(
            self.denoise_output, False, **self.basic_bobber.get_aa_args(self.denoise_output)
        )

    if self.basic_mask_args is not False and self.input_type == self.InputType.REPAIR:
        mask = self.mv.mask(
            self.prefilter_output, direction=MVDirection.BACKWARD,
            kind=MaskMode.SAD, thscd=self.thscd, **self.basic_mask_args,
        )
        self.bobbed = self.denoise_output.std.MaskedMerge(self.bobbed, mask)

    smoothed = self.binomial_degrain(self.bobbed, self.basic_tr)
    if self.basic_tr:
        smoothed = self.mask_shimmer(smoothed, self.bobbed, **self.basic_mask_shimmer_args)

    if self.match_mode:
        smoothed = self.apply_source_match(smoothed)

    if self.lossless_mode == self.LosslessMode.PRESHARPEN and self.input_type != self.InputType.PROGRESSIVE:
        smoothed = self.apply_lossless(smoothed)

    resharp = self.apply_sharpen(smoothed)

    if self.backblend_mode in (self.BackBlendMode.PRELIMIT, self.BackBlendMode.BOTH):
        resharp = self.apply_back_blend(resharp, smoothed)

    if self.limit_mode in (self.SharpLimitMode.SPATIAL_PRESMOOTH, self.SharpLimitMode.TEMPORAL_PRESMOOTH):
        resharp = self.apply_sharpen_limit(resharp)

    if self.backblend_mode in (self.BackBlendMode.POSTLIMIT, self.BackBlendMode.BOTH):
        resharp = self.apply_back_blend(resharp, smoothed)

    self.basic_output = self.apply_noise_restore(resharp, self.basic_noise_restore)

apply_denoise

apply_denoise() -> None
Source code
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
def apply_denoise(self) -> None:
    if not self.denoise_mode:
        self.denoise_output = self.clip
    else:
        if self.denoise_tr:
            denoised = self.mv.compensate(
                self.draft, tr=self.denoise_tr, thscd=self.thscd,
                temporal_func=lambda clip: self.denoise_func(clip, tr=self.denoise_tr),
                **self.denoise_func_comp_args,
            )
        else:
            denoised = cast(ConstantFormatVideoNode, self.denoise_func(self.draft))

        if self.input_type == self.InputType.INTERLACE:
            denoised = reinterlace(denoised, self.tff)

        noise = self.clip.std.MakeDiff(denoised)

        if self.basic_noise_restore or self.final_noise_restore:
            if self.input_type == self.InputType.INTERLACE:
                match self.denoise_deint:
                    case self.NoiseDeintMode.WEAVE:
                        noise = noise.std.SeparateFields(self.tff).std.DoubleWeave(self.tff)
                    case self.NoiseDeintMode.BOB:
                        noise = noise.resize.Bob(tff=self.tff)
                    case self.NoiseDeintMode.GENERATE:
                        noise_source = noise.std.SeparateFields(self.tff)

                        noise_max = Morpho.maximum(Morpho.maximum(noise_source), coords=Coordinates.HORIZONTAL)
                        noise_min = Morpho.minimum(Morpho.minimum(noise_source), coords=Coordinates.HORIZONTAL)

                        noise_new = AddNoise.GAUSS.grain(
                            noise_source, 2048, protect_chroma=False, fade_limits=False, neutral_out=True
                        )
                        noise_new = norm_expr([noise_max, noise_min, noise_new], 'x y - z * range_size / y +')

                        noise = core.std.Interleave([noise_source, noise_new]).std.DoubleWeave(self.tff)[::2]

            if self.denoise_stabilize:
                weight1, weight2 = self.denoise_stabilize

                noise_comp, _ = self.mv.compensate(
                    noise, direction=MVDirection.BACKWARD,
                    tr=1, thscd=self.thscd, interleave=False,
                    **self.denoise_stabilize_comp_args,
                )

                noise = norm_expr(
                    [noise, *noise_comp],
                    'x neutral - abs y neutral - abs > x y ? {weight1} * x y + {weight2} * +',
                    weight1=weight1, weight2=weight2,
                )

        self.noise = noise
        self.denoise_output = denoised if self.denoise_mode == self.NoiseProcessMode.DENOISE else self.clip

    if self.input_type == self.InputType.REPAIR:
        self.denoise_output = reinterlace(self.denoise_output, self.tff)

apply_final

apply_final() -> None
Source code
877
878
879
880
881
882
883
884
885
886
887
888
889
def apply_final(self) -> None:
    smoothed = self.mv.degrain(
        self.basic_output, tr=self.final_tr, thsad=self.final_thsad, thscd=self.thscd, **self.final_degrain_args
    )
    smoothed = self.mask_shimmer(smoothed, self.bobbed, **self.final_mask_shimmer_args)

    if self.limit_mode in (self.SharpLimitMode.SPATIAL_POSTSMOOTH, self.SharpLimitMode.TEMPORAL_POSTSMOOTH):
        smoothed = self.apply_sharpen_limit(smoothed)

    if self.lossless_mode == self.LosslessMode.POSTSMOOTH and self.input_type != self.InputType.PROGRESSIVE:
        smoothed = self.apply_lossless(smoothed)

    self.final_output = self.apply_noise_restore(smoothed, self.final_noise_restore)

apply_lossless

apply_lossless(flt: VideoNode) -> ConstantFormatVideoNode
Source code
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
def apply_lossless(self, flt: vs.VideoNode) -> ConstantFormatVideoNode:
    fields_src = self.denoise_output.std.SeparateFields(self.tff)

    if self.input_type == self.InputType.REPAIR:
        fields_src = core.std.SelectEvery(fields_src, 4, (0, 3))

    fields_flt = flt.std.SeparateFields(self.tff).std.SelectEvery(4, (1, 2))

    woven = reweave(fields_src, fields_flt, self.tff)

    median_diff = woven.std.MakeDiff(median_blur(woven, mode=ConvMode.VERTICAL))
    fields_diff = median_diff.std.SeparateFields(self.tff).std.SelectEvery(4, (1, 2))

    processed_diff = norm_expr(
        [fields_diff, median_blur(fields_diff, mode=ConvMode.VERTICAL)],
        'x neutral - X! y neutral - Y! X@ Y@ xor neutral X@ abs Y@ abs < x y ? ?',
    )
    processed_diff = repair.Mode.MINMAX_SQUARE1(processed_diff, remove_grain.Mode.MINMAX_AROUND2(processed_diff))

    return reweave(fields_src, fields_flt.std.MakeDiff(processed_diff), self.tff)

apply_motion_blur

apply_motion_blur() -> None
Source code
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
def apply_motion_blur(self) -> None:
    angle_in, angle_out = self.motion_blur_shutter_angle

    if not angle_out * self.motion_blur_fps_divisor == angle_in:
        blur_level = (angle_out * self.motion_blur_fps_divisor - angle_in) * 100 / 360

        processed = self.mv.flow_blur(self.final_output, blur=blur_level, thscd=self.thscd, **self.motion_blur_args)

        if self.motion_blur_mask_args is not False:
            mask = self.mv.mask(
                self.prefilter_output, direction=MVDirection.BACKWARD,
                kind=MaskMode.MOTION, thscd=self.thscd, **self.motion_blur_mask_args,
            )

            processed = self.final_output.std.MaskedMerge(processed, mask)
    else:
        processed = self.final_output

    if self.motion_blur_fps_divisor > 1:
        processed = processed[:: self.motion_blur_fps_divisor]

    self.motion_blur_output = processed

apply_noise_restore

apply_noise_restore(
    clip: VideoNode, restore: float = 0.0
) -> ConstantFormatVideoNode
Source code
869
870
871
872
873
874
875
def apply_noise_restore(self, clip: vs.VideoNode, restore: float = 0.0) -> ConstantFormatVideoNode:
    assert check_variable(clip, self.apply_noise_restore)

    if restore and hasattr(self, "noise"):
        clip = norm_expr([clip, self.noise], 'y neutral - {restore} * x +', restore=restore)

    return clip

apply_prefilter

apply_prefilter() -> None
Source code
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
def apply_prefilter(self) -> None:
    if self.input_type == self.InputType.REPAIR:
        search = BlurMatrix.BINOMIAL()(self.draft, mode=ConvMode.VERTICAL)
    else:
        search = self.draft

    if self.prefilter_tr:
        scenechange = self.prefilter_sc_threshold is not False

        scenes = search.misc.SCDetect(self.prefilter_sc_threshold) if scenechange else search
        smoothed = BlurMatrix.BINOMIAL(self.prefilter_tr, mode=ConvMode.TEMPORAL, scenechange=scenechange)(scenes)
        smoothed = self.mask_shimmer(smoothed, search, **self.prefilter_mask_shimmer_args)
    else:
        smoothed = search

    if self.prefilter_postprocess:
        gauss_sigma, blend_weight = self.prefilter_blur_strength

        blurred = core.std.Merge(gauss_blur(smoothed, gauss_sigma), smoothed, blend_weight)

        if self.prefilter_postprocess == self.SearchPostProcess.GAUSSBLUR_EDGESOFTEN:
            lim1, lim2, lim3 = [scale_delta(thr, 8, self.clip) for thr in self.prefilter_soften_limit]

            blurred = norm_expr(
                [blurred, smoothed, search],
                'z y {lim1} - y {lim1} + clip TWEAK! '
                'x {lim2} + TWEAK@ < x {lim3} + x {lim2} - TWEAK@ > x {lim3} - x 0.51 * TWEAK@ 0.49 * + ? ?',
                lim1=lim1, lim2=lim2, lim3=lim3,
            )
    else:
        blurred = smoothed

    if self.prefilter_range_expansion_args is not False:
        blurred = prefilter_to_full_range(blurred, **self.prefilter_range_expansion_args)

    self.prefilter_output = blurred

apply_sharpen

apply_sharpen(clip: VideoNode) -> ConstantFormatVideoNode
Source code
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
def apply_sharpen(self, clip: vs.VideoNode) -> ConstantFormatVideoNode:
    assert check_variable(clip, self.apply_sharpen)

    match self.sharp_mode:
        case self.SharpMode.NONE:
            resharp = clip
        case self.SharpMode.UNSHARP:
            resharp = unsharpen(clip, self.sharp_strength, BlurMatrix.BINOMIAL())
        case self.SharpMode.UNSHARP_MINMAX:
            undershoot, overshoot = self.sharp_clamp

            source_min = Morpho.minimum(clip, coords=Coordinates.VERTICAL)
            source_max = Morpho.maximum(clip, coords=Coordinates.VERTICAL)

            clamp = norm_expr(
                [clip, source_min, source_max],
                'y z + 2 / AVG! AVG@ x {undershoot} - x {overshoot} + clip',
                undershoot=scale_delta(undershoot, 8, clip),
                overshoot=scale_delta(overshoot, 8, clip),
            )
            resharp = unsharpen(clip, self.sharp_strength, BlurMatrix.BINOMIAL()(clamp))

    if self.sharp_thin:
        median_diff = norm_expr(
            [clip, median_blur(clip, mode=ConvMode.VERTICAL)], 'y x - {thin} * neutral +', thin=self.sharp_thin
        )
        blurred_diff = BlurMatrix.BINOMIAL(mode=ConvMode.HORIZONTAL)(median_diff)

        resharp = norm_expr(
            [resharp, blurred_diff, BlurMatrix.BINOMIAL()(blurred_diff)],
            'y neutral - Y! Y@ abs z neutral - abs < x Y@ + x ?',
        )

    return resharp

apply_sharpen_limit

apply_sharpen_limit(clip: VideoNode) -> ConstantFormatVideoNode
Source code
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
def apply_sharpen_limit(self, clip: vs.VideoNode) -> ConstantFormatVideoNode:
    assert check_variable(clip, self.apply_sharpen_limit)

    if self.sharp_mode:
        if self.limit_mode in (self.SharpLimitMode.SPATIAL_PRESMOOTH, self.SharpLimitMode.SPATIAL_POSTSMOOTH):
            if self.limit_radius == 1:
                clip = repair.Mode.MINMAX_SQUARE1(clip, self.bobbed)
            elif self.limit_radius > 1:
                clip = repair.Mode.MINMAX_SQUARE1(clip, repair.Mode.MINMAX_SQUARE_REF2(clip, self.bobbed))

        if self.limit_mode in (self.SharpLimitMode.TEMPORAL_PRESMOOTH, self.SharpLimitMode.TEMPORAL_POSTSMOOTH):
            clip = mc_clamp(
                clip, self.bobbed, self.mv, clamp=self.limit_clamp,
                tr=self.limit_radius, thscd=self.thscd, **self.limit_comp_args,
            )

    return clip

apply_source_match

apply_source_match(clip: VideoNode) -> ConstantFormatVideoNode
Source code
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
def apply_source_match(self, clip: vs.VideoNode) -> ConstantFormatVideoNode:
    assert check_variable(clip, self.apply_source_match)

    def _error_adjustment(clip: ConstantFormatVideoNode, ref: ConstantFormatVideoNode, tr: int) -> ConstantFormatVideoNode:
        tr_f = 2 * tr - 1
        binomial_coeff = factorial(tr_f) // factorial(tr) // factorial(tr_f - tr)
        error_adj = 2**tr_f / (binomial_coeff + self.match_similarity * (2**tr_f - binomial_coeff))

        return norm_expr([clip, ref], 'y {adj} 1 + * x {adj} * -', adj=error_adj)

    if self.input_type != self.InputType.PROGRESSIVE:
        clip = reinterlace(clip, self.tff)

    adjusted1 = _error_adjustment(clip, self.denoise_output, self.basic_tr)
    if self.input_type == self.InputType.PROGRESSIVE:
        bobbed1 = adjusted1
    else:
        bobbed1 = self.basic_bobber.interpolate(adjusted1, False, **self.basic_bobber.get_aa_args(adjusted1))
    match1 = self.binomial_degrain(bobbed1, self.basic_tr)

    if self.match_mode > self.SourceMatchMode.BASIC:
        if self.match_enhance:
            match1 = unsharpen(match1, self.match_enhance, BlurMatrix.BINOMIAL())

        if self.input_type != self.InputType.PROGRESSIVE:
            clip = reinterlace(match1, self.tff)

        diff = self.denoise_output.std.MakeDiff(clip)
        if self.input_type == self.InputType.PROGRESSIVE:
            bobbed2 = diff
        else:
            bobbed2 = self.match_bobber.interpolate(diff, False, **self.match_bobber.get_aa_args(diff))
        match2 = self.binomial_degrain(bobbed2, self.match_tr)

        if self.match_mode == self.SourceMatchMode.TWICE_REFINED:
            adjusted2 = _error_adjustment(match2, bobbed2, self.match_tr)
            match2 = self.binomial_degrain(adjusted2, self.match_tr)

        out = match1.std.MergeDiff(match2)
    else:
        out = match1

    return out

back_blend

back_blend(*, mode: BackBlendMode = BOTH, sigma: float = 1.4) -> Self

Configure parameters for the back_blend stage.

Parameters:

  • mode

    (BackBlendMode, default: BOTH ) –

    Specifies at which stage to perform back-blending.

  • sigma

    (float, default: 1.4 ) –

    Gaussian blur sigma.

Source code
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
def back_blend(
    self,
    *,
    mode: BackBlendMode = BackBlendMode.BOTH,
    sigma: float = 1.4,
) -> Self:
    """
    Configure parameters for the back_blend stage.

    :param mode:     Specifies at which stage to perform back-blending.
    :param sigma:    Gaussian blur sigma.
    """

    self.backblend_mode = mode
    self.backblend_sigma = sigma

    return self

basic

basic(
    *,
    tr: int = 2,
    thsad: int | tuple[int, int] = 640,
    bobber: Interpolater = Nnedi3(nsize=1, nns=4, qual=2, pscrn=1),
    noise_restore: float = 0.0,
    degrain_args: KwargsT | None = None,
    mask_args: KwargsT | None | Literal[False] = None,
    mask_shimmer_args: KwargsT | None = KwargsT(erosion_distance=0)
) -> Self

Configure parameters for the basic stage.

Parameters:

  • tr

    (int, default: 2 ) –

    Temporal radius of the motion compensated binomial smooth.

  • thsad

    (int | tuple[int, int], default: 640 ) –

    Thsad of the motion compensated binomial smooth.

  • bobber

    (Interpolater, default: Nnedi3(nsize=1, nns=4, qual=2, pscrn=1) ) –

    Bobber to use for initial spatial interpolation.

  • noise_restore

    (float, default: 0.0 ) –

    How much noise to restore after this stage.

  • degrain_args

    (KwargsT | None, default: None ) –

    Arguments passed to binomial_degrain.

  • mask_args

    (KwargsT | None | Literal[False], default: None ) –

    Arguments passed to MVTools.mask for InputType.REPAIR.

  • mask_shimmer_args

    (KwargsT | None, default: KwargsT(erosion_distance=0) ) –

    Arguments passed to mask_shimmer.

Source code
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
def basic(
    self,
    *,
    tr: int = 2,
    thsad: int | tuple[int, int] = 640,
    bobber: Interpolater = Nnedi3(nsize=1, nns=4, qual=2, pscrn=1),
    noise_restore: float = 0.0,
    degrain_args: KwargsT | None = None,
    mask_args: KwargsT | None | Literal[False] = None,
    mask_shimmer_args: KwargsT | None = KwargsT(erosion_distance=0),
) -> Self:
    """
    Configure parameters for the basic stage.

    :param tr:                  Temporal radius of the motion compensated binomial smooth.
    :param thsad:               Thsad of the motion compensated binomial smooth.
    :param bobber:              Bobber to use for initial spatial interpolation.
    :param noise_restore:       How much noise to restore after this stage.
    :param degrain_args:        Arguments passed to [binomial_degrain][vsdeinterlace.qtgmc.QTempGaussMC.binomial_degrain].
    :param mask_args:           Arguments passed to [MVTools.mask][vsdenoise.mvtools.mvtools.MVTools.mask]
                                for [InputType.REPAIR][vsdeinterlace.qtgmc.QTempGaussMC.InputType.REPAIR].
    :param mask_shimmer_args:   Arguments passed to [mask_shimmer][vsdeinterlace.qtgmc.QTempGaussMC.mask_shimmer].
    """

    self.basic_tr = tr
    self.basic_thsad = thsad
    self.basic_bobber = bobber.copy(field=self.field)
    self.basic_noise_restore = noise_restore
    self.basic_degrain_args = fallback(degrain_args, KwargsT())
    self.basic_mask_shimmer_args = fallback(mask_shimmer_args, KwargsT())
    self.basic_mask_args: KwargsT | Literal[False] = fallback(mask_args, KwargsT())

    return self

binomial_degrain

binomial_degrain(clip: VideoNode, tr: int) -> ConstantFormatVideoNode
Source code
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
def binomial_degrain(self, clip: vs.VideoNode, tr: int) -> ConstantFormatVideoNode:
    def _get_weights(n: int) -> list[int]:
        k, rhs = 1, list[int]()
        mat = zeros((n + 1, n + 1))

        for i in range(1, n + 2):
            mat[n + 1 - i, i - 1] = mat[n, i - 1] = 1 / 3
            rhs.append(k)
            k = k * (2 * n + 1 - i) // i

        mat[n, 0] = 1

        return list(linalg.solve(mat, rhs))

    assert check_variable(clip, self.binomial_degrain)

    if not tr:
        return clip

    backward, forward = self.mv.get_vectors(tr=tr)
    vectors = MotionVectors()
    degrained = list[ConstantFormatVideoNode]()

    for delta in range(tr):
        vectors.set_vector(backward[delta], MVDirection.BACKWARD, 1)
        vectors.set_vector(forward[delta], MVDirection.FORWARD, 1)
        vectors.tr = 1

        degrained.append(
            self.mv.degrain(
                clip, vectors=vectors, thsad=self.basic_thsad, thscd=self.thscd, **self.basic_degrain_args
            )
        )
        vectors.clear()

    return core.std.AverageFrames([clip, *degrained], _get_weights(tr))

denoise

denoise(
    *,
    tr: int = 2,
    func: _DenoiseFuncTr | VSFunctionKwArgs[VideoNode, VideoNode] = partial(
        denoise, sigma=8
    ),
    mode: NoiseProcessMode = IDENTIFY,
    deint: NoiseDeintMode = GENERATE,
    stabilize: tuple[float, float] | Literal[False] = (0.6, 0.2),
    func_comp_args: KwargsT | None = None,
    stabilize_comp_args: KwargsT | None = None
) -> Self

Configure parameters for the denoise stage.

Parameters:

  • tr

    (int, default: 2 ) –

    Temporal radius of the denoising function & it's motion compensation.

  • func

    (_DenoiseFuncTr | VSFunctionKwArgs[VideoNode, VideoNode], default: partial(denoise, sigma=8) ) –

    Denoising function to use.

  • mode

    (NoiseProcessMode, default: IDENTIFY ) –

    Noise handling method to use.

  • deint

    (NoiseDeintMode, default: GENERATE ) –

    Noise deinterlacing method to use.

  • stabilize

    (tuple[float, float] | Literal[False], default: (0.6, 0.2) ) –

    Weights to use when blending source noise with compensated noise.

  • func_comp_args

    (KwargsT | None, default: None ) –

    Arguments passed to MVTools.compensate for denoising.

  • stabilize_comp_args

    (KwargsT | None, default: None ) –

    Arguments passed to MVTools.compensate for stabilization.

Source code
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
def denoise(
    self,
    *,
    tr: int = 2,
    func: _DenoiseFuncTr | VSFunctionKwArgs[vs.VideoNode, vs.VideoNode] = partial(DFTTest.denoise, sigma=8),
    mode: NoiseProcessMode = NoiseProcessMode.IDENTIFY,
    deint: NoiseDeintMode = NoiseDeintMode.GENERATE,
    stabilize: tuple[float, float] | Literal[False] = (0.6, 0.2),
    func_comp_args: KwargsT | None = None,
    stabilize_comp_args: KwargsT | None = None,
) -> Self:
    """
    Configure parameters for the denoise stage.

    :param tr:                     Temporal radius of the denoising function & it's motion compensation.
    :param func:                   Denoising function to use.
    :param mode:                   Noise handling method to use.
    :param deint:                  Noise deinterlacing method to use.
    :param stabilize:              Weights to use when blending source noise with compensated noise.
    :param func_comp_args:         Arguments passed to [MVTools.compensate][vsdenoise.mvtools.mvtools.MVTools.compensate] for denoising.
    :param stabilize_comp_args:    Arguments passed to [MVTools.compensate][vsdenoise.mvtools.mvtools.MVTools.compensate] for stabilization.
    """

    self.denoise_tr = tr
    self.denoise_func = func
    self.denoise_mode = mode
    self.denoise_deint = deint
    self.denoise_stabilize: tuple[float, float] | Literal[False] = stabilize
    self.denoise_func_comp_args = fallback(func_comp_args, KwargsT())
    self.denoise_stabilize_comp_args = fallback(stabilize_comp_args, KwargsT())

    return self

final

final(
    *,
    tr: int = 3,
    thsad: int | tuple[int, int] = 256,
    noise_restore: float = 0.0,
    degrain_args: KwargsT | None = None,
    mask_shimmer_args: KwargsT | None = None
) -> Self

Configure parameters for the final stage.

Parameters:

  • tr

    (int, default: 3 ) –

    Temporal radius of the motion compensated smooth.

  • thsad

    (int | tuple[int, int], default: 256 ) –

    Thsad of the motion compensated smooth.

  • noise_restore

    (float, default: 0.0 ) –

    How much noise to restore after this stage.

  • degrain_args

    (KwargsT | None, default: None ) –

    Arguments passed to MVTools.degrain.

  • mask_shimmer_args

    (KwargsT | None, default: None ) –

    Arguments passed to mask_shimmer.

Source code
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
def final(
    self,
    *,
    tr: int = 3,
    thsad: int | tuple[int, int] = 256,
    noise_restore: float = 0.0,
    degrain_args: KwargsT | None = None,
    mask_shimmer_args: KwargsT | None = None,
) -> Self:
    """
    Configure parameters for the final stage.

    :param tr:                   Temporal radius of the motion compensated smooth.
    :param thsad:                Thsad of the motion compensated smooth.
    :param noise_restore:        How much noise to restore after this stage.
    :param degrain_args:         Arguments passed to [MVTools.degrain][vsdenoise.mvtools.mvtools.MVTools.degrain].
    :param mask_shimmer_args:    Arguments passed to [mask_shimmer][vsdeinterlace.qtgmc.QTempGaussMC.mask_shimmer].
    """

    self.final_tr = tr
    self.final_thsad = thsad
    self.final_noise_restore = noise_restore
    self.final_degrain_args = fallback(degrain_args, KwargsT())
    self.final_mask_shimmer_args = fallback(mask_shimmer_args, KwargsT())

    return self

lossless

lossless(*, mode: LosslessMode = NONE) -> Self

Configure parameter for the lossless stage.

Parameters:

  • mode

    (LosslessMode, default: NONE ) –

    Specifies at which stage to re-weave the original fields.

Source code
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def lossless(
    self,
    *,
    mode: LosslessMode = LosslessMode.NONE,
) -> Self:
    """
    Configure parameter for the lossless stage.

    :param mode:    Specifies at which stage to re-weave the original fields.
    """

    self.lossless_mode = mode

    return self

mask_shimmer

mask_shimmer(
    flt: VideoNode,
    src: VideoNode,
    threshold: int | float = 1,
    erosion_distance: int = 4,
    over_dilation: int = 0,
) -> ConstantFormatVideoNode

Compare processed clip with reference clip, only allow thin, horizontal areas of difference, i.e. bob shimmer fixes.

Parameters:

  • flt

    (VideoNode) –

    Processed clip to perform masking on.

  • src

    (VideoNode) –

    Unprocessed clip to restore from.

  • threshold

    (int | float, default: 1 ) –

    Threshold of change to perform masking.

  • erosion_distance

    (int, default: 4 ) –

    How much to deflate then reflate to remove thin areas.

  • over_dilation

    (int, default: 0 ) –

    Extra inflation to ensure areas to restore back are fully caught.

Source code
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
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
558
559
560
561
562
563
564
565
566
567
568
569
570
def mask_shimmer(
    self,
    flt: vs.VideoNode,
    src: vs.VideoNode,
    threshold: int | float = 1,
    erosion_distance: int = 4,
    over_dilation: int = 0,
) -> ConstantFormatVideoNode:
    """
    Compare processed clip with reference clip,
    only allow thin, horizontal areas of difference, i.e. bob shimmer fixes.

    :param flt:                 Processed clip to perform masking on.
    :param src:                 Unprocessed clip to restore from.
    :param threshold:           Threshold of change to perform masking.
    :param erosion_distance:    How much to deflate then reflate to remove thin areas.
    :param over_dilation:       Extra inflation to ensure areas to restore back are fully caught.
    """

    assert check_variable(flt, self.mask_shimmer)

    if not erosion_distance:
        return flt

    iter1 = 1 + (erosion_distance + 1) // 3
    iter2 = 1 + (erosion_distance + 2) // 3

    over1 = over_dilation // 3
    over2 = over_dilation % 3

    diff = src.std.MakeDiff(flt)

    opening = Morpho.minimum(diff, iterations=iter1, coords=Coordinates.VERTICAL)
    closing = Morpho.maximum(diff, iterations=iter1, coords=Coordinates.VERTICAL)

    if erosion_distance % 3:
        opening = Morpho.deflate(opening)
        closing = Morpho.inflate(closing)

        if erosion_distance % 3 == 2:
            opening = median_blur(opening)
            closing = median_blur(closing)

    opening = Morpho.maximum(opening, iterations=iter2, coords=Coordinates.VERTICAL)
    closing = Morpho.minimum(closing, iterations=iter2, coords=Coordinates.VERTICAL)

    if over_dilation:
        opening = Morpho.maximum(opening, iterations=over1)
        closing = Morpho.minimum(closing, iterations=over1)

        opening = Morpho.inflate(opening, iterations=over2)
        closing = Morpho.deflate(closing, iterations=over2)

    return norm_expr(
        [flt, diff, opening, closing],
        'y neutral - abs {thr} > y a neutral min z neutral max clip y ? neutral - x +',
        thr=scale_delta(threshold, 8, flt)
    )

motion_blur

motion_blur(
    *,
    shutter_angle: tuple[int | float, int | float] = (180, 180),
    fps_divisor: int = 1,
    blur_args: KwargsT | None = None,
    mask_args: KwargsT | None | Literal[False] = KwargsT(ml=4)
) -> Self

Configure parameters for the motion blur stage.

Parameters:

  • shutter_angle

    (tuple[int | float, int | float], default: (180, 180) ) –

    Tuple containing the source and output shutter angle. Will apply motion blur if they do not match.

  • fps_divisor

    (int, default: 1 ) –

    Factor by which to reduce framerate.

  • blur_args

    (KwargsT | None, default: None ) –

    Arguments passed to MVTools.flow_blur.

  • mask_args

    (KwargsT | None | Literal[False], default: KwargsT(ml=4) ) –

    Arguments passed to MVTools.mask.

Source code
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
def motion_blur(
    self,
    *,
    shutter_angle: tuple[int | float, int | float] = (180, 180),
    fps_divisor: int = 1,
    blur_args: KwargsT | None = None,
    mask_args: KwargsT | None | Literal[False] = KwargsT(ml=4),
) -> Self:
    """
    Configure parameters for the motion blur stage.

    :param shutter_angle:    Tuple containing the source and output shutter angle. Will apply motion blur if they do not match.
    :param fps_divisor:      Factor by which to reduce framerate.
    :param blur_args:        Arguments passed to [MVTools.flow_blur][vsdenoise.mvtools.mvtools.MVTools.flow_blur].
    :param mask_args:        Arguments passed to [MVTools.mask][vsdenoise.mvtools.mvtools.MVTools.mask].
    """

    self.motion_blur_shutter_angle = shutter_angle
    self.motion_blur_fps_divisor = fps_divisor
    self.motion_blur_args = fallback(blur_args, KwargsT())
    self.motion_blur_mask_args: KwargsT | Literal[False] = fallback(mask_args, KwargsT())

    return self

prefilter

prefilter(
    *,
    tr: int = 2,
    sc_threshold: float | None | Literal[False] = None,
    postprocess: SearchPostProcess = GAUSSBLUR_EDGESOFTEN,
    strength: tuple[float, float] = (1.9, 0.1),
    limit: tuple[int | float, int | float, int | float] = (3, 7, 2),
    range_expansion_args: KwargsT | None | Literal[False] = None,
    mask_shimmer_args: KwargsT | None = None
) -> Self

Configure parameters for the prefilter stage.

Parameters:

  • tr

    (int, default: 2 ) –

    Radius of the initial temporal binomial smooth.

  • sc_threshold

    (float | None | Literal[False], default: None ) –

    Threshold for scene changes, disables sc detection if False.

  • postprocess

    (SearchPostProcess, default: GAUSSBLUR_EDGESOFTEN ) –

    Post-processing routine to use.

  • strength

    (tuple[float, float], default: (1.9, 0.1) ) –

    Tuple containing gaussian blur sigma & blend weight of the blur.

  • limit

    (tuple[int | float, int | float, int | float], default: (3, 7, 2) ) –

    3-step limiting thresholds for the gaussian blur post-processing.

  • range_expansion_args

    (KwargsT | None | Literal[False], default: None ) –

    Arguments passed to prefilter_to_full_range.

  • mask_shimmer_args

    (KwargsT | None, default: None ) –

    Arguments passed to mask_shimmer.

Source code
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
def prefilter(
    self,
    *,
    tr: int = 2,
    sc_threshold: float | None | Literal[False] = None,
    postprocess: SearchPostProcess = SearchPostProcess.GAUSSBLUR_EDGESOFTEN,
    strength: tuple[float, float] = (1.9, 0.1),
    limit: tuple[int | float, int | float, int | float] = (3, 7, 2),
    range_expansion_args: KwargsT | None | Literal[False] = None,
    mask_shimmer_args: KwargsT | None = None,
) -> Self:
    """
    Configure parameters for the prefilter stage.

    :param tr:                      Radius of the initial temporal binomial smooth.
    :param sc_threshold:            Threshold for scene changes, disables sc detection if False.
    :param postprocess:             Post-processing routine to use.
    :param strength:                Tuple containing gaussian blur sigma & blend weight of the blur.
    :param limit:                   3-step limiting thresholds for the gaussian blur post-processing.
    :param range_expansion_args:    Arguments passed to [prefilter_to_full_range][vsdenoise.prefilters.prefilter_to_full_range].
    :param mask_shimmer_args:       Arguments passed to [mask_shimmer][vsdeinterlace.qtgmc.QTempGaussMC.mask_shimmer].
    """

    self.prefilter_tr = tr
    self.prefilter_sc_threshold = sc_threshold
    self.prefilter_postprocess = postprocess
    self.prefilter_blur_strength = strength
    self.prefilter_soften_limit = limit
    self.prefilter_range_expansion_args: KwargsT | Literal[False] = fallback(range_expansion_args, KwargsT())
    self.prefilter_mask_shimmer_args = fallback(mask_shimmer_args, KwargsT())

    return self

process

process(
    *,
    force_tr: int = 1,
    preset: MVToolsPreset = HQ_SAD,
    blksize: int | tuple[int, int] = 16,
    refine: int = 1,
    thsad_recalc: int | None = None,
    thscd: int | tuple[int | None, int | float | None] | None = (180, 38.5)
) -> ConstantFormatVideoNode

Start the deinterlacing process.

Parameters:

  • force_tr

    (int, default: 1 ) –

    Always analyze motion to at least this, even if otherwise unnecessary.

  • preset

    (MVToolsPreset, default: HQ_SAD ) –

    MVTools preset defining base values for the MVTools object.

  • blksize

    (int | tuple[int, int], default: 16 ) –

    Size of a block. Larger blocks are less sensitive to noise, are faster, but also less accurate.

  • refine

    (int, default: 1 ) –

    Number of times to recalculate motion vectors with halved block size.

  • thsad_recalc

    (int | None, default: None ) –

    Only bad quality new vectors with a SAD above this will be re-estimated by search. thsad value is scaled to 8x8 block size.

  • thscd

    (int | tuple[int | None, int | float | None] | None, default: (180, 38.5) ) –

    Scene change detection thresholds: - First value: SAD threshold for considering a block changed between frames. - Second value: Percentage of changed blocks needed to trigger a scene change.

Returns:

  • ConstantFormatVideoNode

    Deinterlaced clip.

Source code
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
def process(
    self,
    *,
    force_tr: int = 1,
    preset: MVToolsPreset = MVToolsPresets.HQ_SAD,
    blksize: int | tuple[int, int] = 16,
    refine: int = 1,
    thsad_recalc: int | None = None,
    thscd: int | tuple[int | None, int | float | None] | None = (180, 38.5),
) -> ConstantFormatVideoNode:
    """
    Start the deinterlacing process.

    :param force_tr:        Always analyze motion to at least this, even if otherwise unnecessary.
    :param preset:          MVTools preset defining base values for the MVTools object.
    :param blksize:         Size of a block. Larger blocks are less sensitive to noise, are faster, but also less accurate.
    :param refine:          Number of times to recalculate motion vectors with halved block size.
    :param thsad_recalc:    Only bad quality new vectors with a SAD above this will be re-estimated by search.
                            thsad value is scaled to 8x8 block size.
    :param thscd:           Scene change detection thresholds:
                             - First value: SAD threshold for considering a block changed between frames.
                             - Second value: Percentage of changed blocks needed to trigger a scene change.

    :return:                Deinterlaced clip.
    """

    def _floor_div_tuple(x: tuple[int, int]) -> tuple[int, int]:
        return (x[0] // 2, x[1] // 2)

    self.draft = self.clip.resize.Bob(tff=self.tff) if self.input_type == self.InputType.INTERLACE else self.clip
    self.thscd = thscd

    tr = max(1, force_tr, self.denoise_tr, self.basic_tr, self.match_tr, self.final_tr)
    blksize = blksize if isinstance(blksize, tuple) else (blksize, blksize)
    preset.pop('search_clip', None)

    self.apply_prefilter()

    self.mv = MVTools(self.draft, self.prefilter_output, **preset)
    self.mv.analyze(tr=tr, blksize=blksize, overlap=_floor_div_tuple(blksize))

    if refine:
        if thsad_recalc is None:
            thsad_recalc = round(
                (self.basic_thsad[0] if isinstance(self.basic_thsad, tuple) else self.basic_thsad) / 2
            )

        for _ in range(refine):
            blksize = _floor_div_tuple(blksize)
            overlap = _floor_div_tuple(blksize)

            self.mv.recalculate(thsad=thsad_recalc, blksize=blksize, overlap=overlap)

    self.apply_denoise()
    self.apply_basic()
    self.apply_final()
    self.apply_motion_blur()

    return core.std.SetFieldBased(self.motion_blur_output, 0)

sharpen

sharpen(
    *,
    mode: SharpMode | None = None,
    strength: float = 1.0,
    clamp: int | float | tuple[int | float, int | float] = 1,
    thin: float = 0.0
) -> Self

Configure parameters for the sharpen stage.

Parameters:

Source code
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
def sharpen(
    self,
    *,
    mode: SharpMode | None = None,
    strength: float = 1.0,
    clamp: int | float | tuple[int | float, int | float] = 1,
    thin: float = 0.0,
) -> Self:
    """
    Configure parameters for the sharpen stage.

    :param mode:        Specifies the type of sharpening to use.
    :param strength:    Sharpening strength.
    :param clamp:       Clamp the sharpening strength of
                        [SharpMode.UNSHARP_MINMAX][vsdeinterlace.qtgmc.QTempGaussMC.SharpMode.UNSHARP_MINMAX]
                        to the min/max average plus/minus this.
    :param thin:        How much to vertically thin edges.
    """

    if mode is None:
        self.sharp_mode = self.SharpMode.NONE if self.match_mode else self.SharpMode.UNSHARP_MINMAX
    else:
        self.sharp_mode = mode

    self.sharp_strength = strength
    self.sharp_clamp = normalize_seq(clamp, 2)
    self.sharp_thin = thin

    return self

sharpen_limit

sharpen_limit(
    *,
    mode: SharpLimitMode | None = None,
    radius: int = 3,
    clamp: int | float | tuple[int | float, int | float] = 0,
    comp_args: KwargsT | None = None
) -> Self

Configure parameters for the sharpen_limit stage.

Parameters:

  • mode

    (SharpLimitMode | None, default: None ) –

    Specifies type of limiting & at which stage to perform it.

  • radius

    (int, default: 3 ) –

    Radius of sharpness limiting.

  • clamp

    (int | float | tuple[int | float, int | float], default: 0 ) –

    How much undershoot/overshoot to allow.

  • comp_args

    (KwargsT | None, default: None ) –

    Arguments passed to MVTools.compensate for temporal limiting.

Source code
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
def sharpen_limit(
    self,
    *,
    mode: SharpLimitMode | None = None,
    radius: int = 3,
    clamp: int | float | tuple[int | float, int | float] = 0,
    comp_args: KwargsT | None = None,
) -> Self:
    """
    Configure parameters for the sharpen_limit stage.

    :param mode:         Specifies type of limiting & at which stage to perform it.
    :param radius:       Radius of sharpness limiting.
    :param clamp:        How much undershoot/overshoot to allow.
    :param comp_args:    Arguments passed to [MVTools.compensate][vsdenoise.mvtools.mvtools.MVTools.compensate] for temporal limiting.
    """

    if mode is None:
        self.limit_mode = self.SharpLimitMode.NONE if self.match_mode else self.SharpLimitMode.TEMPORAL_PRESMOOTH
    else:
        self.limit_mode = mode

    self.limit_radius = radius
    self.limit_clamp = clamp
    self.limit_comp_args = fallback(comp_args, KwargsT())

    return self

source_match

source_match(
    *,
    tr: int = 1,
    bobber: Interpolater | None = None,
    mode: SourceMatchMode = NONE,
    similarity: float = 0.5,
    enhance: float = 0.5,
    degrain_args: KwargsT | None = None
) -> Self

Configure parameters for the source_match stage.

Parameters:

  • tr

    (int, default: 1 ) –

    Temporal radius of the refinement motion compensated binomial smooth.

  • bobber

    (Interpolater | None, default: None ) –

    Bobber to use for refined spatial interpolation.

  • mode

    (SourceMatchMode, default: NONE ) –

    Specifies number of refinement steps to perform.

  • similarity

    (float, default: 0.5 ) –

    Temporal similarity of the error created by smoothing.

  • enhance

    (float, default: 0.5 ) –

    Sharpening strength prior to source match refinement.

  • degrain_args

    (KwargsT | None, default: None ) –

    Arguments passed to binomial_degrain.

Source code
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
def source_match(
    self,
    *,
    tr: int = 1,
    bobber: Interpolater | None = None,
    mode: SourceMatchMode = SourceMatchMode.NONE,
    similarity: float = 0.5,
    enhance: float = 0.5,
    degrain_args: KwargsT | None = None,
) -> Self:
    """
    Configure parameters for the source_match stage.

    :param tr:              Temporal radius of the refinement motion compensated binomial smooth.
    :param bobber:          Bobber to use for refined spatial interpolation.
    :param mode:            Specifies number of refinement steps to perform.
    :param similarity:      Temporal similarity of the error created by smoothing.
    :param enhance:         Sharpening strength prior to source match refinement.
    :param degrain_args:    Arguments passed to [binomial_degrain][vsdeinterlace.qtgmc.QTempGaussMC.binomial_degrain].
    """

    self.match_tr = tr
    self.match_bobber = fallback(bobber, self.basic_bobber).copy(field=self.field)
    self.match_mode = mode
    self.match_similarity = similarity
    self.match_enhance = enhance
    self.match_degrain_args = fallback(degrain_args, KwargsT())

    return self