Example #1
0
def rescaler(clip: vs.VideoNode, height: int) -> vs.VideoNode:
    """
    Basic rescaling function using nnedi3.
    """
    from lvsfunc.kernels import Bicubic, BicubicSharp
    from vardefunc.mask import FDOG
    from vardefunc.scale import nnedi3_upscale
    from vsutil import Range, depth, get_w, get_y

    clip = depth(clip, 32)

    clip_y = get_y(clip)
    descale = BicubicSharp().descale(clip_y,
                                     get_w(height, clip.width / clip.height),
                                     height)
    rescale = Bicubic(b=-1 / 2,
                      c=1 / 4).scale(nnedi3_upscale(descale, pscrn=1),
                                     clip.width, clip.height)

    l_mask = FDOG().get_mask(clip_y, lthr=0.065,
                             hthr=0.065).std.Maximum().std.Minimum()
    l_mask = l_mask.std.Median().std.Convolution([1] *
                                                 9)  # stolen from varde xd
    masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask)

    scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)])

    return depth(scaled, 16)
Example #2
0
def rescaler(clip: vs.VideoNode,
             height: int) -> Tuple[vs.VideoNode, vs.VideoNode]:
    """
    Basic rescaling and mask generating function using nnedi3.
    """
    from lvsfunc.kernels import Bicubic
    from lvsfunc.scale import descale_detail_mask
    from vardefunc.mask import FDOG
    from vardefunc.scale import nnedi3_upscale
    from vsutil import Range, get_w, join, plane

    bits, clip = _get_bits(clip, expected_depth=32)

    clip_y = get_y(clip)
    descale = Bicubic().descale(clip_y, get_w(height,
                                              clip.width / clip.height),
                                height)
    rescale = Bicubic().scale(nnedi3_upscale(descale, pscrn=1), clip.width,
                              clip.height)

    l_mask = FDOG().get_mask(clip_y, lthr=0.065,
                             hthr=0.065).std.Maximum().std.Minimum()
    l_mask = l_mask.std.Median().std.Convolution([1] *
                                                 9)  # stolen from varde xd
    masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask)

    scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)])

    upscale = Bicubic().scale(descale, 1920, 1080)
    detail_mask = descale_detail_mask(clip_y, upscale, threshold=0.055)

    scaled_down = scaled if bits == 32 else depth(scaled, bits)
    mask_down = detail_mask if bits == 32 else depth(
        detail_mask, 16, range_in=Range.FULL, range=Range.LIMITED)
    return scaled_down, mask_down
Example #3
0
def debander(clip: vs.VideoNode,
             luma_grain: float = 4.0,
             **kwargs: Any) -> vs.VideoNode:
    """
        A quick 'n dirty generic debanding function.
        To be more specific, it would appear that it's faster to
        deband every plane separately (don't ask me why).

        To abuse this, we split up the clip into planes beforehand,
        and then join them back together again at the end.

        Although the vast, vast majority of video will be YUV,
        a sanity check for plane amount is done as well, just to be safe.

        :param clip:        Input clip
        :param luma_grain:  Grain added to the luma plane
        :param kwargs:        Additional parameters passed to placebo.Deband

        :return:            Debanded clip
    """
    if clip.format.num_planes == 0:
        return core.placebo.Deband(clip, grain=luma_grain, **kwargs)
    return join([
        core.placebo.Deband(plane(clip, 0), grain=luma_grain, **kwargs),
        core.placebo.Deband(plane(clip, 1), grain=0, **kwargs),
        core.placebo.Deband(plane(clip, 2), grain=0, **kwargs)
    ])
Example #4
0
def placebo_debander(clip: vs.VideoNode,
                     grain: int = 4,
                     **deband_args: Any) -> vs.VideoNode:
    return join(
        [  # Still not sure why splitting it up into planes is faster, but hey!
            core.placebo.Deband(plane(clip, 0), grain=grain, **deband_args),
            core.placebo.Deband(plane(clip, 1), grain=0, **deband_args),
            core.placebo.Deband(plane(clip, 2), grain=0, **deband_args)
        ])
Example #5
0
def placebo_debander(clip: vs.VideoNode,
                     grain: float = 4.0,
                     placebo_args: Dict[str, Any] = {}) -> vs.VideoNode:
    from vsutil import join, plane

    return join([
        core.placebo.Deband(plane(clip, 0), grain=grain, **placebo_args),
        core.placebo.Deband(plane(clip, 1), grain=0, **placebo_args),
        core.placebo.Deband(plane(clip, 2), grain=0, **placebo_args)
    ])
Example #6
0
def placebo_debander(clip: vs.VideoNode,
                     grain: float = 4.0,
                     deband_args: Dict[str, Any] = {}) -> vs.VideoNode:
    placebo_args: Dict[str, Any] = dict()
    placebo_args |= deband_args

    return join([
        core.placebo.Deband(plane(clip, 0), grain=grain, **placebo_args),
        core.placebo.Deband(plane(clip, 1), grain=0, **placebo_args),
        core.placebo.Deband(plane(clip, 2), grain=0, **placebo_args)
    ])
Example #7
0
def do_filter() -> vs.VideoNode:
    """Vapoursynth filtering"""
    src = JPBD.src_cut
    out = src

    luma = get_y(out)
    rows = [
        core.std.CropAbs(luma, out.width, 1, top=out.height - 1),
        core.std.CropAbs(luma, out.width, 1, top=out.height - 2)
    ]
    diff = core.std.Expr(rows, 'x y - abs').std.PlaneStats()

    row_fix = vdf.merge_chroma(
        luma.fb.FillBorders(bottom=1, mode="fillmargins"),
        out.fb.FillBorders(bottom=2, mode="fillmargins"))

    fixrow = core.std.FrameEval(out,
                                partial(_select_row, clip=out,
                                        row_fix=row_fix),
                                prop_src=diff)
    out = fixrow

    fixedge_a = awf.bbmod(out, 1, 1, 1, 1, 20, blur=700, u=False, v=False)

    fixedge = out
    fixedge = lvf.rfs(fixedge, fixedge_a, [(EDSTART + 309, EDEND)])
    out = fixedge

    out = depth(out, 16)

    dehalo = gf.MaskedDHA(out, rx=1.4, ry=1.4, darkstr=0.02, brightstr=1)
    dehalo = lvf.rfs(out, dehalo, [(EDEND + 1, src.num_frames - 1)])
    out = dehalo

    resize = core.std.Crop(out, right=12, bottom=8).resize.Bicubic(1920, 1080)
    resize = lvf.rfs(out, resize, [(27005, 27076)])
    out = resize

    # Denoising only the chroma
    pre = hvf.SMDegrain(out, tr=2, thSADC=300, plane=3)
    planes = split(out)
    planes[1], planes[2] = [
        mvf.BM3D(planes[i], 1.25, radius2=2, pre=plane(pre, i))
        for i in range(1, 3)
    ]
    out = join(planes)

    preden = core.dfttest.DFTTest(out, sbsize=16, sosize=12, tbsize=1)
    detail_mask = lvf.mask.detail_mask(preden, brz_a=2500, brz_b=1500)

    deband = vdf.dumb3kdb(preden, 16, threshold=[17, 17], grain=[24, 0])
    deband = core.std.MergeDiff(deband, out.std.MakeDiff(preden))
    deband = core.std.MaskedMerge(deband, out, detail_mask)
    out = deband

    decz = vdf.decsiz(out, min_in=128 << 8, max_in=192 << 8)
    out = decz

    return depth(out, 10).std.Limiter(16 << 2, [235 << 2, 240 << 2], [0, 1, 2])
Example #8
0
def rescaler(clip: vs.VideoNode,
             height: int,
             shader_file: Optional[str] = None
             ) -> Tuple[vs.VideoNode, vs.VideoNode]:
    """
    Basic rescaling function using nnedi3.
    """
    from lvsfunc.kernels import Bicubic, Lanczos
    from lvsfunc.scale import descale_detail_mask
    from vardefunc.mask import FDOG
    from vardefunc.scale import fsrcnnx_upscale, nnedi3_upscale
    from vsutil import Range, depth, get_w, get_y, iterate, join, plane

    clip = depth(clip, 32)

    clip_y = get_y(clip)
    descale = Lanczos(taps=5).descale(clip_y,
                                      get_w(height, clip.width / clip.height),
                                      height)

    if shader_file:
        rescale = fsrcnnx_upscale(descale,
                                  shader_file=shader_file,
                                  downscaler=Bicubic(b=-1 / 2, c=1 / 4).scale)
    else:
        rescale = Bicubic(b=-1 / 2,
                          c=1 / 4).scale(nnedi3_upscale(descale, pscrn=1),
                                         clip.width, clip.height)

    l_mask = FDOG().get_mask(clip_y, lthr=0.065,
                             hthr=0.065).std.Maximum().std.Minimum()
    l_mask = l_mask.std.Median().std.Convolution([1] *
                                                 9)  # stolen from varde xd
    masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask)

    scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)])

    upscale = Lanczos(taps=5).scale(descale, 1920, 1080)
    detail_mask = descale_detail_mask(clip_y, upscale, threshold=0.035)
    detail_mask = iterate(detail_mask, core.std.Inflate, 2)
    detail_mask = iterate(detail_mask, core.std.Maximum, 2)

    return depth(scaled, 16), depth(detail_mask,
                                    16,
                                    range_in=Range.FULL,
                                    range=Range.LIMITED)
Example #9
0
def rescaler(clip: vs.VideoNode, height: int,
             shader_file: Optional[str] = None, **kwargs: Any
             ) -> Tuple[vs.VideoNode, vs.VideoNode]:
    """
    Multi-descaling + reupscaling function.
    Compares multiple descales and takes darkest/brightest pixels from clips as necessary
    """
    import lvsfunc as lvf
    import muvsfunc as muf
    from vardefunc.mask import FDOG
    from vardefunc.scale import fsrcnnx_upscale, nnedi3_upscale

    bits = get_depth(clip)
    clip = depth(clip, 32)

    clip_y = get_y(clip)
    scalers: List[Callable[[vs.VideoNode, int, int], vs.VideoNode]] = [
        lvf.kernels.Spline36().descale,
        lvf.kernels.Catrom().descale,
        lvf.kernels.BicubicSharp().descale,
        lvf.kernels.Catrom().scale
    ]

    descale_clips = [scaler(clip_y, get_w(height), height) for scaler in scalers]

    descale_clip = core.std.Expr(descale_clips, 'x y z a min max min y z a max min max z a min max')
    if shader_file:
        rescale = fsrcnnx_upscale(descale_clip, shader_file=shader_file, downscaler=None)
    else:
        rescale = nnedi3_upscale(descale_clip)

    rescale = muf.SSIM_downsample(rescale, clip.width, clip.height, smooth=((3 ** 2 - 1) / 12) ** 0.5,
                                  sigmoid=True, filter_param_a=0, filter_param_b=0)

    l_mask = FDOG().get_mask(clip_y, lthr=0.065, hthr=0.065).std.Maximum().std.Minimum()
    l_mask = l_mask.std.Median().std.Convolution([1] * 9)  # stolen from varde xd
    masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask)

    scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)])

    upscale = lvf.kernels.Spline36().scale(descale_clips[0], clip.width, clip.height)
    detail_mask = lvf.scale.descale_detail_mask(clip_y, upscale, threshold=0.04)

    scaled_down = scaled if bits == 32 else depth(scaled, bits)
    mask_down = detail_mask if bits == 32 else depth(detail_mask, 16, range_in=Range.FULL, range=Range.LIMITED)
    return scaled_down, mask_down
Example #10
0
def fixbrdr(clip, thr=3):
    import vsutil as vsu
    y = vsu.plane(clip, 0)
    diff_expr = "x y - abs"

    # prepare rows with enough size for convolutions
    row_1 = y.std.Crop(top=0, bottom=y.height - 1 - 3)
#   row_1_2 = y.std.Crop(top=0, bottom=y.height - 2)
    row_2 = y.std.Crop(top=1, bottom=y.height - 2 - 3)
#   row_2_3 = y.std.Crop(top=1, bottom=y.height - 3)
#   row_2_3_4 = y.std.Crop(top=2, bottom=y.height - 4)

    # FillMargins clip
    fill_mid = row_1.std.Convolution([0, 0, 0, 0, 0, 0, 3, 2, 3])
    fill_mid = fill_mid.std.CropAbs(width=y.width, height=1)

    # no idea what convolutions work best but these are the diagonal fills
    fill_left = row_1.std.Convolution([0, 0, 0, 0, 0, 0, 5, 3, 1])
    fill_left = fill_left.std.CropAbs(width=y.width, height=1)

    fill_right = row_1.std.Convolution([0, 0, 0, 0, 0, 0, 1, 3, 5])
    fill_right = fill_right.std.CropAbs(width=y.width, height=1)

    # need shifts to see if the row is diagonal or not
    shift_left = row_2.std.Convolution([0, 0, 0, 1, 0, 0, 0, 0, 0])
    shift_left = shift_left.std.CropAbs(width=y.width, height=1)
    shift_right = row_2.std.Convolution([0, 0, 0, 0, 0, 1, 0, 0, 0])
    shift_right = shift_right.std.CropAbs(width=y.width, height=1)

    # find threshold by looking at difference of third row minus its blur
    blur_left = row_2.std.Convolution([0, 0, 0, 1, 0, 0, 0, 0, 0])
    blur_left = blur_left.std.Convolution([0, 0, 0, 1, 0, 0, 0, 0, 0])
    blur_left = blur_left.std.Convolution([0, 1, 0, 1, 0, 1, 0, 1, 0])

    threshold_left = core.std.Expr([blur_left, row_2], diff_expr)
    threshold_left = threshold_left.std.CropAbs(width=y.width, height=1, top=1)

    blur_right = row_2.std.Convolution([0, 0, 0, 0, 0, 1, 0, 0, 0])
    blur_right = blur_right.std.Convolution([0, 0, 0, 0, 0, 1, 0, 0, 0])
    blur_right = blur_right.std.Convolution([0, 1, 0, 1, 0, 1, 0, 1, 0])

    threshold_right = core.std.Expr([blur_right, row_2], diff_expr)
    threshold_right = threshold_right.std.CropAbs(y.width, 1, top=1)

    # evaluate whether blur results in a pixel being too different from its shift
    sl_fm_diff = core.std.Expr([shift_left, fill_mid], diff_expr)
    sr_fm_diff = core.std.Expr([shift_right, fill_mid], diff_expr)
    #                    x           y           z               a                b          c           d
    fix = core.std.Expr([sl_fm_diff, sr_fm_diff, threshold_left, threshold_right, fill_right, fill_left, fill_mid], f"x z {thr} * > y a {thr} * < and b y {thr} a * > x z {thr} * < and c d ? ?")

    s = core.std.StackVertical([fix, y.std.Crop(top=1)])
    return core.std.ShufflePlanes([s, clip], [0, 1, 2], vs.YUV) if clip.format.color_family == vs.YUV else s
Example #11
0
def do_filter() -> vs.VideoNode:
    """Vapoursynth filtering"""
    src = JPBD.src_cut
    out = src

    luma = get_y(out)
    rows = [
        core.std.CropAbs(luma, out.width, 1, top=out.height - 1),
        core.std.CropAbs(luma, out.width, 1, top=out.height - 2)
    ]
    diff = core.std.Expr(rows, 'x y - abs').std.PlaneStats()

    row_fix = vdf.merge_chroma(
        luma.fb.FillBorders(bottom=1, mode="fillmargins"),
        out.fb.FillBorders(bottom=2, mode="fillmargins"))

    fixrow = core.std.FrameEval(out,
                                partial(_select_row, clip=out,
                                        row_fix=row_fix),
                                prop_src=diff)
    out = fixrow

    out = depth(out, 16)

    # Denoising only the chroma
    pre = hvf.SMDegrain(out, tr=2, thSADC=300, plane=3)
    planes = split(out)
    planes[1], planes[2] = [
        mvf.BM3D(planes[i], 1.25, radius2=2, pre=plane(pre, i))
        for i in range(1, 3)
    ]
    out = join(planes)

    preden = core.dfttest.DFTTest(out, sbsize=16, sosize=12, tbsize=1)
    detail_mask = lvf.mask.detail_mask(preden, brz_a=2500, brz_b=1500)

    deband = vdf.dumb3kdb(preden, 16, threshold=[17, 17], grain=[24, 0])
    deband = core.std.MergeDiff(deband, out.std.MakeDiff(preden))
    deband = core.std.MaskedMerge(deband, out, detail_mask)
    out = deband

    decz = vdf.decsiz(out, min_in=128 << 8, max_in=192 << 8)
    out = decz

    ref = depth(src, 16)
    credit = out
    credit = lvf.rfs(out, ref, CREDITS)
    out = credit

    return depth(out, 10).std.Limiter(16 << 2, [235 << 2, 240 << 2], [0, 1, 2])
Example #12
0
    def prepare_hr(self, clip: vs.VideoNode,
                   dvd_clip: vs.VideoNode) -> vs.VideoNode:
        """
        Match with the DVD @640x480:
            bd.resize.Bicubic(720, 486, src_left=-0.75).std.Crop(top=3, bottom=3)
        """
        import havsfunc as haf
        import lvsfunc as lvf
        import muvsfunc as muf
        import rekt
        import vardefunc as vdf
        from awsmfunc import bbmod
        from vsutil import depth, join, plane
        from xvs import WarpFixChromaBlend

        rkt = rekt.rektlvls(clip, [0, 1079], [17, 16],
                            [0, 1, 2, 3] + [1917, 1918, 1919],
                            [16, 4, -2, 2] + [-2, 5, 14])
        ef = bbmod(rkt, left=4, right=3, y=False)

        clip = depth(ef, 32).std.AssumeFPS(fpsnum=24, fpsden=1)

        bd_descale = lvf.kernels.Bicubic().descale(plane(clip, 0), 1280, 720)
        bd_doubled = vdf.scale.nnedi3_upscale(bd_descale)
        bd_down = muf.SSIM_downsample(bd_doubled, dvd_clip.width * 2, 486 * 2)

        # Need to do some fuckery to make sure the chroma matches up perfectly
        bd_cshift = clip.resize.Bicubic(chromaloc_in=1,
                                        chromaloc=0,
                                        format=vs.YUV420P16)
        bd_cwarp = bd_cshift.warp.AWarpSharp2(thresh=88,
                                              blur=3,
                                              type=1,
                                              depth=6,
                                              planes=[1, 2])
        bd_chroma = bd_cwarp.resize.Bicubic(format=vs.YUV444P16,
                                            width=bd_down.width,
                                            height=bd_down.height)
        bd_i444 = core.std.ShufflePlanes([depth(bd_down, 16), bd_chroma])
        bd_shift = bd_i444.resize.Bicubic(src_left=-0.75).std.Crop(top=6,
                                                                   bottom=6)

        return bd_shift.resize.Bicubic(format=vs.RGB24, dither_type='error_diffusion') \
            .std.ShufflePlanes([1, 2, 0], vs.RGB)
Example #13
0
def prot_dpir(clip: vs.VideoNode, strength: int = 25,
              matrix: Optional[Union[Matrix, int]] = None,
              cuda: bool = True, device_index: int = 0,
              **dpir_args: Any) -> vs.VideoNode:
    """
    Protective DPIR function for the deblocking mode.
    Sometimes vs-dpir's deblocking mode will litter a random frame with a lot of red dots.
    This is obviously undesirable, so this function was written to combat that.
    Original code by Zewia, modified by LightArrowsEXE.
    Dependencies:
    * vs-dpir
    :param clip:            Input clip
    :param strength:        DPIR's deblocking strength
    :param matrix:          Enum for the matrix of the input clip. See ``types.Matrix`` for more info.
                            If `None`, gets matrix from the "_Matrix" prop of the clip
    :param cuda:            Device type used for deblocking. Uses CUDA if True, else CPU
    :param device_index:    The 'device_index' + 1Âş device of type device type in the system
    :dpir_args:             Additional args to pass onto DPIR
    :return:                Deblocked clip
    """
    from vsdpir import DPIR

    if clip.format is None:
        raise ValueError("prot_dpir: 'Variable-format clips not supported'")

    dpir_args |= dict(strength=strength, task='deblock',
                      device_type='cuda' if cuda else 'cpu',
                      device_index=device_index)

    clip_rgb = depth(clip, 32).std.SetFrameProp('_Matrix', intval=matrix)
    clip_rgb = core.resize.Bicubic(clip_rgb, format=vs.RGBS)

    debl = DPIR(clip_rgb, **dpir_args)
    rgb_planes = split(debl)

    # Grab the brigher parts of the R plane to avoid model fuckery
    # Everything below 5 (8 bit value) gets replaced with the ref's R plane
    rgb_planes[0] = core.std.Expr([rgb_planes[0], rgb_planes[1], plane(clip_rgb, 0)],
                                  'z x > y 5 255 / <= and z x ?')
    rgb_merge = join(rgb_planes, family=vs.RGB)

    return core.resize.Bicubic(rgb_merge, format=clip.format.id, matrix=matrix)
Example #14
0
File: aa.py Project: petzku/lvsfunc
def upscaled_sraa(clip: vs.VideoNode,
                  rfactor: float = 1.5,
                  rep: Optional[int] = None,
                  width: Optional[int] = None,
                  height: Optional[int] = None,
                  downscaler: Optional[
                      Callable[[vs.VideoNode, int, int],
                               vs.VideoNode]] = kernels.Spline36().scale,
                  opencl: bool = False,
                  nnedi3cl: Optional[bool] = None,
                  eedi3cl: Optional[bool] = None,
                  **eedi3_args: Any) -> vs.VideoNode:
    """
    A function that performs a supersampled single-rate AA to deal with heavy aliasing and broken-up lineart.
    Useful for Web rips, where the source quality is not good enough to descale,
    but you still want to deal with some bad aliasing and lineart.

    It works by supersampling the clip, performing AA, and then downscaling again.
    Downscaling can be disabled by setting `downscaler` to `None`, returning the supersampled luma clip.
    The dimensions of the downscaled clip can also be adjusted by setting `height` or `width`.
    Setting either `height` or `width` will also scale the chroma accordingly.

    Original function written by Zastin, heavily modified by LightArrowsEXE.

    Alias for this function is `lvsfunc.sraa`.

    Dependencies:
    * fmtconv
    * rgsf (optional: 32 bit clip),
    * vapoursynth-eedi3
    * vapoursynth-nnedi3
    * vapoursynth-nnedi3cl (optional: opencl)

    :param clip:            Input clip
    :param rfactor:         Image enlargement factor. 1.3..2 makes it comparable in strength to vsTAAmbk
                            It is not recommended to go below 1.3 (Default: 1.5)
    :param rep:             Repair mode (Default: None)
    :param width:           Target resolution width. If None, determined from `height`
    :param height:          Target resolution height (Default: ``clip.height``)
    :param downscaler:      Resizer used to downscale the AA'd clip
    :param opencl:          OpenCL acceleration (Default: False)
    :param nnedi3cl:        OpenCL acceleration for nnedi3 (Default: False)
    :param eedi3cl:         OpenCL acceleration for eedi3 (Default: False)
    :param eedi3_args:      Arguments passed to eedi3 (Default: alpha=0.2, beta=0.6, gamma=40, nrad=2, mdis=20)

    :return:                Antialiased and optionally rescaled clip
    """
    if clip.format is None:
        raise ValueError(
            "upscaled_sraa: 'Variable-format clips not supported'")

    luma = get_y(clip)

    nnargs: Dict[str, Any] = dict(nsize=0, nns=4, qual=2)
    # TAAmbk defaults are 0.5, 0.2, 20, 3, 30
    eeargs: Dict[str, Any] = dict(alpha=0.2,
                                  beta=0.6,
                                  gamma=40,
                                  nrad=2,
                                  mdis=20)
    eeargs.update(eedi3_args)

    if rfactor < 1:
        raise ValueError("upscaled_sraa: '\"rfactor\" must be above 1'")

    ssw = round(clip.width * rfactor)
    ssh = round(clip.height * rfactor)

    while ssw % 2:
        ssw += 1
    while ssh % 2:
        ssh += 1

    if height is None:
        height = clip.height
    if width is None:
        if height != clip.height:
            width = get_w(height, aspect_ratio=clip.width / clip.height)
        else:
            width = clip.width

    nnedi3cl = fallback(nnedi3cl, opencl)
    eedi3cl = fallback(eedi3cl, opencl)

    # there doesn't seem to be a cleaner way to do this that makes mypy happy
    def nnedi3(*args: Any, **kwargs: Any) -> vs.VideoNode:
        return core.nnedi3cl.NNEDI3CL(
            *args, **kwargs) if nnedi3cl else core.nnedi3.nnedi3(
                *args, **kwargs)

    def eedi3(*args: Any, **kwargs: Any) -> vs.VideoNode:
        return core.eedi3m.EEDI3CL(*args, **
                                   kwargs) if nnedi3cl else core.eedi3m.EEDI3(
                                       *args, **kwargs)

    # Nnedi3 upscale from source height to source height * rounding (Default 1.5)
    up_y = nnedi3(luma, 0, 1, 0, **nnargs)
    up_y = core.resize.Spline36(up_y, height=ssh, src_top=.5)
    up_y = core.std.Transpose(up_y)
    up_y = nnedi3(up_y, 0, 1, 0, **nnargs)
    up_y = core.resize.Spline36(up_y, height=ssw, src_top=.5)

    # Single-rate AA
    aa_y = eedi3(up_y,
                 0,
                 0,
                 0,
                 sclip=nnedi3(up_y, 0, 0, 0, **nnargs),
                 **eeargs)
    aa_y = core.std.Transpose(aa_y)
    aa_y = eedi3(aa_y,
                 0,
                 0,
                 0,
                 sclip=nnedi3(aa_y, 0, 0, 0, **nnargs),
                 **eeargs)

    scaled: vs.VideoNode

    # Back to source clip height or given height
    if downscaler is None:
        scaled = aa_y
    else:
        scaled = downscaler(aa_y, width, height)

    if rep:
        scaled = util.pick_repair(scaled)(scaled,
                                          luma.resize.Bicubic(width, height),
                                          mode=rep)

    if clip.format.num_planes == 1 or downscaler is None:
        return scaled
    if height is not clip.height or width is not clip.width:
        if height % 2:
            raise ValueError(
                "upscaled_sraa: '\"height\" must be an even number when not passing a GRAY clip'"
            )
        if width % 2:
            raise ValueError(
                "upscaled_sraa: '\"width\" must be an even number when not passing a GRAY clip'"
            )

        chr = kernels.Bicubic().scale(clip, width, height)
        return join([scaled, plane(chr, 1), plane(chr, 2)])
    return join([scaled, plane(clip, 1), plane(clip, 2)])
Example #15
0
def do_filter():
    """Vapoursynth filtering"""
    src = JPBD.src_cut

    # Variables
    opstart, opend = 2111, 4268
    edstart, edend = 31650, 33809
    full_zone = [(18727, 18774), (31590, 31649), (33990, src.num_frames - 1)
                 ]  # eyecatch, episode name and next episode
    shabc_zone = [(edstart + 15, edstart + 1215),
                  (edstart + 1882, edstart + 2126)]
    h = 720
    w = get_w(h)

    # Bicubic sharp parts don't have bad edges
    edges_a = core.edgefixer.ContinuityFixer(src, *[[2, 1, 1]] * 4)
    edges_b = awf.bbmod(src, left=6, thresh=32, blur=200)
    edges = lvf.rfs(edges_a, edges_b, [(edstart + 1275, edstart + 1757)])
    edges = lvf.rfs(edges, src, [(opstart, opend)] + full_zone)
    out = depth(edges, 32)

    # Denoise
    ref = hvf.SMDegrain(depth(get_y(out), 16), thSAD=450)
    denoise = hybrid_denoise(out, 0.35, 1.4, dict(a=2, d=1),
                             dict(ref=depth(ref, 32)))
    out = denoise
    # denoise = out

    # Descale
    luma = get_y(out)
    lineart = vdf.edge_detect(luma, 'FDOG', 0.055,
                              (1, 1)).std.Median().std.BoxBlur(0, 1, 1, 1, 1)

    descale_a = core.descale.Despline36(luma, w,
                                        h).std.SetFrameProp('descaleKernel',
                                                            data='spline36')
    descale_b = core.descale.Debicubic(luma, w, h, 0, 1).std.SetFrameProp(
        'descaleKernel', data='sharp_bicubic')
    descale = lvf.rfs(descale_a, descale_b, shabc_zone)

    # Chroma reconstruction
    # y_m is the assumed mangled luma.
    # Descale 1080p -> Bad conversion in 422 720p -> Regular 1080p 420
    radius = 2
    y, u, v = descale, plane(out, 1), plane(out, 2)
    y_m = core.resize.Point(y, 640, 720,
                            src_left=-1).resize.Bicubic(960,
                                                        540,
                                                        filter_param_a=1 / 3,
                                                        filter_param_b=1 / 3)

    # 0.25 for 444 and 0.25 for right shifting
    y_m, u, v = [
        c.resize.Bicubic(w,
                         h,
                         src_left=0.25 + 0.25,
                         filter_param_a=0,
                         filter_param_b=.5) for c in [y_m, u, v]
    ]

    y_fixup = core.std.MakeDiff(y, y_m)
    yu, yv = Regress(y_m, u, v, radius=radius, eps=1e-7)

    u_fixup = ReconstructMulti(y_fixup, yu, radius=radius)
    u_r = core.std.MergeDiff(u, u_fixup)

    v_fixup = ReconstructMulti(y_fixup, yv, radius=radius)
    v_r = core.std.MergeDiff(v, v_fixup)

    # -0.5 * 720/1080 = -1/3
    # -1/3 for the right shift
    # https://forum.doom9.org/showthread.php?p=1802716#post1802716
    u_r, v_r = [
        c.resize.Bicubic(960,
                         540,
                         src_left=-1 / 3,
                         filter_param_a=-.5,
                         filter_param_b=.25) for c in [u_r, v_r]
    ]

    upscale = vdf.fsrcnnx_upscale(
        descale,
        height=h * 2,
        shader_file=r'shaders\FSRCNNX_x2_56-16-4-1.glsl',
        upscaler_smooth=eedi3_upscale,
        profile='zastin')

    antialias = sraa_eedi3(upscale,
                           3,
                           alpha=0.2,
                           beta=0.4,
                           gamma=40,
                           nrad=3,
                           mdis=20)

    downscale = muvf.SSIM_downsample(antialias,
                                     src.width,
                                     src.height,
                                     filter_param_a=0,
                                     filter_param_b=0)
    downscale = core.std.MaskedMerge(luma, downscale, lineart)

    merged_a = join([downscale, u_r, v_r])
    merged_b = vdf.merge_chroma(downscale, denoise)
    merged = lvf.rfs(merged_a, merged_b, shabc_zone)
    out = depth(merged, 16)

    warp = xvs.WarpFixChromaBlend(out, 80, 2, depth=8)
    out = warp

    dering = gf.MaskedDHA(out,
                          rx=1.25,
                          ry=1.25,
                          darkstr=0.05,
                          brightstr=1.0,
                          maskpull=48,
                          maskpush=140)
    out = dering

    qtgmc = hvf.QTGMC(out, Preset="Slower", InputType=1, ProgSADMask=2.0)
    qtgmc = vdf.fade_filter(out, out, qtgmc, edstart + 1522,
                            edstart + 1522 + 24)
    qtgmc = lvf.rfs(out, qtgmc, [(edstart + 1522 + 25, edstart + 1757)])
    out = qtgmc

    out = lvf.rfs(out, depth(denoise, 16), [(opstart, opend)])

    detail_dark_mask = detail_dark_mask_func(get_y(out),
                                             brz_a=8000,
                                             brz_b=6000)
    detail_light_mask = lvf.denoise.detail_mask(out, brz_a=2500, brz_b=1200)
    detail_mask = core.std.Expr([detail_dark_mask, detail_light_mask],
                                'x y +').std.Median()
    detail_mask_grow = iterate(detail_mask, core.std.Maximum, 2)
    detail_mask_grow = iterate(detail_mask_grow, core.std.Inflate,
                               2).std.BoxBlur(0, 1, 1, 1, 1)

    detail_mask = core.std.Expr([get_y(out), detail_mask_grow, detail_mask],
                                f'x {32<<8} < y z ?')

    deband = dumb3kdb(out, 22, 30)
    deband = core.std.MaskedMerge(deband, out, detail_mask)
    out = deband

    ref = get_y(out).std.PlaneStats()
    adgmask_a = core.adg.Mask(ref, 30)
    adgmask_b = core.adg.Mask(ref, 12)

    stgrain = sizedgrn(out, 0.1, 0.05, 1.05, sharp=80)
    stgrain = core.std.MaskedMerge(out, stgrain, adgmask_b)
    stgrain = core.std.MaskedMerge(out, stgrain, adgmask_a.std.Invert())

    dygrain = sizedgrn(out, 0.2, 0.05, 1.15, sharp=80, static=False)
    dygrain = core.std.MaskedMerge(out, dygrain, adgmask_a)
    grain = core.std.MergeDiff(dygrain, out.std.MakeDiff(stgrain))
    out = grain

    ref = depth(edges, 16)
    credit = out
    rescale_mask = vdf.diff_rescale_mask(ref, h, b=0, c=1, mthr=40, sw=0, sh=0)
    rescale_mask = vdf.region_mask(rescale_mask, *[10] * 4)
    rescale_mask = hvf.mt_expand_multi(rescale_mask,
                                       mode='ellipse',
                                       sw=4,
                                       sh=4).std.BoxBlur(0, 1, 1, 1, 1)

    credit = lvf.rfs(credit, ref, full_zone)
    credit = lvf.rfs(credit, core.std.MaskedMerge(credit, ref, rescale_mask),
                     [(edstart, edend)])
    out = credit

    return depth(out, 10).std.Limiter(16 << 2, [235 << 2, 240 << 2])
Example #16
0
def smart_descale(clip: vs.VideoNode,
                  resolutions: List[int],
                  kernel: str = 'bicubic',
                  b: Union[float, Fraction] = Fraction(0),
                  c: Union[float, Fraction] = Fraction(1, 2),
                  taps: int = 4,
                  thr: float = 0.05,
                  rescale: bool = False) -> vs.VideoNode:
    """
    A function that descales a clip to multiple resolutions.

    This function will descale clips to multiple resolutions and return the descaled clip
    that is mostly likely to be the actual resolution of the clip. This is useful for shows
    like Shield Hero, Made in Abyss, or Symphogear that love jumping between multiple resolutions.

    The returned clip will be multiple resolutions, meaning most resamplers will break,
    as well as encoding it as-is. When handling it, please ensure you return the clip to
    a steady resolution before further processing.

    Setting rescaled will use `smart_rescaler` to reupscale the clip to its original resolution.
    This will return a proper YUV clip as well. If rescaled is set to False, the returned clip
    will be GRAY.

    Original written by kageru, and in part rewritten by me. Thanks Varde for helping me fix some bugs with it.

    Dependencies: vapoursynth-descale

    :param clip:             Input clip
    :param resolutions:      A list of resolutions to descale to
    :param kernel:           Kernel used to descale (see :py:func:`lvsfunc.util.get_scale_filter`, Default: bicubic)
    :param b:                B-param for bicubic kernel (Default: 0)
    :param c:                C-param for bicubic kernel (Default: 1 / 2)
    :param taps:             Taps param for lanczos kernel (Default: 4)
    :param thr:              Threshold for when a clip is discerned as "non-scaleable" (Default: 0.05)
    :param rescale:          Rescale the clip to the original resolution after descaling (Default: False)

    :return:                 Variable-resolution clip containing descaled frames
    """
    try:
        from descale import get_filter
    except ModuleNotFoundError:
        raise ModuleNotFoundError(
            "smart_descale: missing dependency 'descale'")

    b = float(b)
    c = float(c)

    Resolution = namedtuple('Resolution', ['width', 'height'])
    ScaleAttempt = namedtuple('ScaleAttempt',
                              ['descaled', 'rescaled', 'resolution', 'diff'])
    clip_c = clip

    clip = util.resampler((get_y(clip) if clip.format.num_planes != 1 else clip), 32) \
        .std.SetFrameProp('descaleResolution', intval=clip.height)

    def _perform_descale(height: int) -> ScaleAttempt:
        resolution = Resolution(get_w(height, clip.width / clip.height),
                                height)
        descaled = get_filter(b, c, taps, kernel)(clip, resolution.width, resolution.height) \
            .std.SetFrameProp('descaleResolution', intval=height)
        rescaled = util.get_scale_filter(kernel, b=b, c=c,
                                         taps=taps)(descaled, clip.width,
                                                    clip.height)
        diff = core.std.Expr([rescaled, clip], 'x y - abs').std.PlaneStats()
        return ScaleAttempt(descaled, rescaled, resolution, diff)

    clips_by_resolution = {
        c.resolution.height: c
        for c in map(_perform_descale, resolutions)
    }
    # If we pass a variable res clip as first argument to FrameEval, we’re also allowed to return one.
    variable_res_clip = core.std.Splice([
        core.std.BlankClip(clip, length=len(clip) - 1),
        core.std.BlankClip(clip, length=1, width=clip.width + 1)
    ],
                                        mismatch=True)

    def _select_descale(n: int, f: List[vs.VideoFrame]):
        best_res = max(
            f,
            key=lambda frame: math.log(
                clip.height - frame.props.descaleResolution, 2) * round(
                    1 / max(frame.props.PlaneStatsAverage, 1e-12))**0.2)

        best_attempt = clips_by_resolution.get(
            best_res.props.descaleResolution)
        if thr == 0:
            return best_attempt.descaled
        # No blending here because src and descaled have different resolutions.
        # The caller can use the frameProps to deal with that if they so desire.
        if best_res.props.PlaneStatsAverage > thr:
            return clip
        return best_attempt.descaled

    props = [c.diff for c in clips_by_resolution.values()]
    descaled = core.std.FrameEval(variable_res_clip,
                                  _select_descale,
                                  prop_src=props)

    if rescale:
        upscale = smart_reupscale(descaled,
                                  height=clip.height,
                                  kernel=kernel,
                                  b=b,
                                  c=c,
                                  taps=taps)
        if clip_c.format is vs.GRAY:
            return upscale
        return join([upscale, plane(clip_c, 1), plane(clip_c, 2)])
    return descaled
Example #17
0
def zzdeband(clip: vs.VideoNode,
             denoised: bool = False,
             mask: int = 0,
             f3kdb_args: Dict[str, Any] = {},
             placebo_args: Dict[str, Any] = {}) -> Union[vs.VideoNode, Any]:
    """
    Written by Zastin, *CAUTIOUSLY* modified by puny little me

    This is all pure black magic to me,
    so I'm just gonna pretend I didn't see anything.
    """
    import zzfunc as zzf

    plcbo_args: Dict[str, Any] = dict(iterations=3,
                                      threshold=5,
                                      radius=16,
                                      grain=0)
    plcbo_args.update(placebo_args)

    dumb3kdb_args: Dict[str, Any] = dict(radius=16, threshold=[30, 0], grain=0)
    dumb3kdb_args.update(f3kdb_args)

    brz = 256 if denoised else 384

    clip_depth = get_depth(clip)
    if clip_depth != 16:
        clip = depth(clip, 16)

    clip_y = plane(clip, 0)

    ymax = maxm(clip_y, sw=30, mode='ellipse')
    ymin = minm(clip_y, sw=30, mode='ellipse')

    # edge detection
    thr = 3.2 * 256
    ypw0 = clip_y.std.Prewitt()
    ypw = ypw0.std.Binarize(thr).rgvs.RemoveGrain(11)
    if mask == 1:
        return ypw

    # range masks (neighborhood max - min)
    rad, thr = 3, 2.5 * 256
    yrangesml = core.std.Expr([ymax[3], ymin[3]], 'x y - abs')
    yrangesml = yrangesml.std.Binarize(thr).std.BoxBlur(0, 2, 1, 2, 1)
    if mask == 2:
        return yrangesml

    rad, thr = 14, 6.5 * 256
    yrangebig0 = core.std.Expr([ymax[rad], ymin[rad]], 'x y - abs')
    yrangebig = yrangebig0.std.Binarize(thr)
    yrangebig = minm(yrangebig,
                     sw=rad * 3 // 4,
                     threshold=65536 // ((rad * 3 // 4) + 1),
                     mode='ellipse')[-1]
    yrangebig = yrangebig.std.BoxBlur(0, rad // 4, 1, rad // 4, 1)
    if mask == 3:
        return yrangebig

    # morphological masks (shapes)
    rad = 30
    ymph = core.std.Expr([
        clip_y,
        maxm(ymin[rad], sw=rad, mode='ellipse')[rad],
        minm(ymax[rad], sw=rad, mode='ellipse')[rad]
    ], 'x y - z x - max')
    ymph = ymph.std.Binarize(brz)
    ymph = ymph.std.Minimum().std.Maximum()
    ymph = ymph.std.BoxBlur(0, 4, 1, 4, 1)
    if mask == 4:
        return ymph

    grad_mask = zzf.combine([ymph, yrangesml, ypw])
    if mask == 5:
        return grad_mask

    ydebn_strong = clip_y.placebo.Deband(1, **plcbo_args)
    ydebn_normal = vdf.deband.dumb3kdb(clip_y, **dumb3kdb_args)
    ydebn = ydebn_strong.std.MaskedMerge(ydebn_normal, grad_mask)
    ydebn = ydebn.std.MaskedMerge(clip_y, yrangebig)

    merged = join([ydebn, plane(clip, 1), plane(clip, 2)])
    return merged if clip_depth == 16 else depth(merged, clip_depth)
Example #18
0
def faggotdb_mod(clip: vs.VideoNode,
                 thrY=40,
                 thrC=None,
                 radiusY=15,
                 radiusC=15,
                 CbY=44,
                 CrY=44,
                 CbC=44,
                 CrC=44,
                 grainY=32,
                 grainC=None,
                 grainCC=0,
                 sample_mode=2,
                 neo=True,
                 dynamic_grainY=False,
                 dynamic_grainC=False,
                 tv_range=True,
                 mask="retinex",
                 binarize=False,
                 binarize_thr=70,
                 grayscale=True,
                 bitresamp=False,
                 outbits=None,
                 blurmask=True,
                 horizblur=2,
                 vertblur=2) -> vs.VideoNode:

    funcName = "faggotdb_mod"  # name kept to be fallback compatible

    # Original Idea: Author who created Fag3kdb. Edited by AlucardSama04; additional modifications by l00t

    if not isinstance(clip, vs.VideoNode):
        raise TypeError(f"{funcName}: This is not a clip")

    if outbits is None:
        outbits = clip.format.bits_per_sample

    if bitresamp:
        if clip.format.bits_per_sample != outbits:
            clip = fvf.Depth(
                clip,
                bits=outbits)  # instead of error, auto convert to 16 bits
    elif clip.format.bits_per_sample != outbits:
        raise TypeError(f"{funcName}: Input-output bitdepth mismatch")

    # if not isinstance(mask, vs.VideoNode):
    # raise vs.Error(f"{funcName}: mask' only clip inputs")

    if mask in [-1]:  # more user friendly if we do the masking intentionally
        mask = clip
        blurmask = False
    elif mask in [0, "retinex"]:
        mask = kgf.retinex_edgemask(clip)
    elif mask in [1, "kirsch"]:
        mask = kgf.kirsch(clip)
    elif mask in [2, "Sobel"]:
        mask = core.std.Sobel(clip, scale=1)
    elif mask in [3, "Prewitt"]:
        mask = core.std.Prewitt(clip)
    elif mask in [4, "GF"]:
        mask = fvf.GradFun3(clip, mask=2, debug=1)
    else:
        raise ValueError(f"{funcName}: Unknown Mask Mode")

    if grayscale:
        mask = core.std.ShufflePlanes(mask, planes=0, colorfamily=vs.GRAY)

    if binarize:  # binarize threshold should be adjusted according to bitdepth
        mask = core.std.Binarize(mask, threshold=binarize_thr)

    if blurmask:
        mask = core.std.BoxBlur(mask, hradius=horizblur, vradius=vertblur)

    if thrC is None:
        thrC = int(round(thrY / 2))

    if grainC is None:
        grainC = int(round(grainY / 2))

    if grainCC is None:
        grainCC = 0

    f3kdb = core.neo_f3kdb.Deband if neo else core.f3kdb.Deband

    U = plane(clip, 1)
    V = plane(clip, 2)

    U = f3kdb(U,
              range=radiusC,
              y=thrC,
              cb=CbC,
              cr=CrC,
              grainy=grainC,
              grainc=0,
              sample_mode=sample_mode,
              dynamic_grain=dynamic_grainC,
              keep_tv_range=tv_range,
              output_depth=outbits)

    V = f3kdb(V,
              range=radiusC,
              y=thrC,
              cb=CbC,
              cr=CrC,
              grainy=grainC,
              grainc=0,
              sample_mode=sample_mode,
              dynamic_grain=dynamic_grainC,
              keep_tv_range=tv_range,
              output_depth=outbits)

    filtered = core.std.ShufflePlanes([clip, U, V], [0, 0, 0], vs.YUV)

    filtered = f3kdb(filtered,
                     range=radiusY,
                     y=thrY,
                     cb=CbY,
                     cr=CrY,
                     grainy=grainY,
                     grainc=grainCC,
                     sample_mode=sample_mode,
                     dynamic_grain=dynamic_grainY,
                     keep_tv_range=tv_range,
                     output_depth=outbits
                     )  # if grainCC > 0 UV planes will be debanded once again

    return core.std.MaskedMerge(filtered, clip, mask)
Example #19
0
def placebo_debander(clip: vs.VideoNode, grain: float = 2.0, deband_args: Mapping[str, Any] = {}) -> vs.VideoNode:
    return join([
        core.placebo.Deband(plane(clip, 0), grain=grain, **deband_args),
        core.placebo.Deband(plane(clip, 1), grain=0, **deband_args),
        core.placebo.Deband(plane(clip, 2), grain=0, **deband_args)
    ])
Example #20
0
def based_aa(clip: vs.VideoNode,
             shader_file: str = "FSRCNNX_x2_56-16-4-1.glsl",
             rfactor: float = 2.0,
             tff: bool = True,
             mask_thr: float = 60,
             show_mask: bool = False,
             lmask: vs.VideoNode | None = None,
             **eedi3_args: Any) -> vs.VideoNode:
    """
    As the name implies, this is a based anti-aliaser. Thank you, based Zastin.
    This relies on FSRCNNX being very sharp, and as such it very much acts like the main "AA" here.

    Original function by Zastin, modified by LightArrowsEXE.

    Dependencies:

    * vapoursynth-eedi3
    * vs-placebo

    :param clip:            Input clip
    :param shader_file:     Path to FSRCNNX shader file
    :param rfactor:         Image enlargement factor
    :param tff:             Top-Field-First if true, Bottom-Field-First if alse
    :param mask_thr:        Threshold for the edge mask binarisation.
                            Scaled internally to match bitdepth of clip.
    :param show_mask:       Output mask
    :param eedi3_args:      Additional args to pass to eedi3
    :param lmask:           Line mask clip to use for eedi3

    :return:                AA'd clip or mask clip
    """
    def _eedi3s(clip: vs.VideoNode,
                mclip: vs.VideoNode | None = None,
                **eedi3_kwargs: Any) -> vs.VideoNode:
        edi_args: Dict[str, Any] = {  # Eedi3 args for `eedi3s`
            'field': int(tff),
            'alpha': 0.125,
            'beta': 0.25,
            'gamma': 40,
            'nrad': 2,
            'mdis': 20,
            'vcheck': 2,
            'vthresh0': 12,
            'vthresh1': 24,
            'vthresh2': 4
        }
        edi_args |= eedi3_kwargs

        out = core.eedi3m.EEDI3(clip,
                                dh=False,
                                sclip=clip,
                                planes=0,
                                **edi_args)

        if mclip:
            return core.std.Expr([clip, out, mclip], 'z y x ?')
        return out

    def _resize_mclip(mclip: vs.VideoNode,
                      width: int | None = None,
                      height: int | None = None) -> vs.VideoNode:
        iw, ih = mclip.width, mclip.height
        ow, oh = fallback(width, iw), fallback(height, ih)

        if (ow > iw and ow / iw != ow // iw) or (oh > ih
                                                 and oh / ih != oh // ih):
            mclip = Point().scale(mclip, iw * ceil(ow / iw),
                                  ih * ceil(oh / ih))
        return core.fmtc.resample(mclip,
                                  ow,
                                  oh,
                                  kernel='box',
                                  fulls=1,
                                  fulld=1)

    check_variable(clip, "based_aa")
    assert clip.format

    aaw = (round(clip.width * rfactor) + 1) & ~1
    aah = (round(clip.height * rfactor) + 1) & ~1

    clip_y = get_y(clip)

    if not lmask:
        if mask_thr > 255:
            raise ValueError(
                f"based_aa: 'mask_thr must be equal to or lower than 255 (current: {mask_thr})'"
            )

        mask_thr = scale_value(mask_thr, 8, get_depth(clip))

        lmask = clip_y.std.Prewitt().std.Binarize(
            mask_thr).std.Maximum().std.BoxBlur(0, 1, 1, 1, 1)

    mclip_up = _resize_mclip(lmask, aaw, aah)

    if show_mask:
        return lmask

    aa = depth(clip_y, 16).std.Transpose()
    aa = join([aa] * 3).placebo.Shader(shader=shader_file,
                                       filter='box',
                                       width=aa.width * 2,
                                       height=aa.height * 2)
    aa = depth(aa, get_depth(clip_y))
    aa = ssim_downsample(get_y(aa), aah, aaw)
    aa = _eedi3s(aa, mclip=mclip_up.std.Transpose(),
                 **eedi3_args).std.Transpose()
    aa = ssim_downsample(_eedi3s(aa, mclip=mclip_up, **eedi3_args), clip.width,
                         clip.height)
    aa = depth(aa, get_depth(clip_y))

    aa_merge = core.std.MaskedMerge(clip_y, aa, lmask)

    if clip.format.num_planes == 1:
        return aa_merge
    return join([aa_merge, plane(clip, 1), plane(clip, 2)])
Example #21
0
def filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]:
    """Main filterchain"""
    import havsfunc as haf
    import lvsfunc as lvf
    import muvsfunc as muf
    import debandshit as dbs
    import vardefunc as vdf
    from vsutil import depth, get_y, insert_clip, join, plane

    src = JP_DVD.clip_cut.std.AssumeFPS(fpsnum=24000, fpsden=1001)
    src_ncop, src_nced = JP_BD_NCOP.clip_cut, JP_BD_NCED.clip_cut
    src_ncop = src_ncop + src_ncop[-1] * 11
    src_nced = src_nced + src_nced[-1]
    src_03 = JP_BD_03.clip_cut

    src_ncop = flt.rekt(src_ncop)
    src_nced = flt.rekt(src_nced)
    src_03 = flt.rekt(src_03)

    # Fixing an animation error in the NCOP
    sqmask_ncop = lvf.mask.BoundingBox((419, 827), (1500, 68))
    masked_ncop = core.std.MaskedMerge(src_ncop, src_03, sqmask_ncop.get_mask(src_ncop))
    masked_ncop = lvf.rfs(src_ncop, masked_ncop, [(opstart+2064, opstart+2107)])

    op_mask = vdf.dcm(
        src_03, src_03[opstart:opstart+masked_ncop.num_frames-op_offset], masked_ncop[:-op_offset],
        start_frame=opstart, thr=85, prefilter=True) if opstart is not False \
        else get_y(core.std.BlankClip(src))
    op_mask = depth(core.resize.Bicubic(op_mask, 1296, 728).std.Crop(top=4, bottom=4), 16)

    # Stick credits ontop of NC because it looks way better
    masked_ncop = src_ncop.resize.Bicubic(chromaloc_in=1, chromaloc=0)
    cwarp_ncop = masked_ncop.warp.AWarpSharp2(thresh=88, blur=3, type=1, depth=6, planes=[1, 2])
    descale_ncop = lvf.kernels.Bicubic().descale(plane(depth(src_ncop, 16), 0), 1280, 720)
    chroma_down_ncop = core.resize.Bicubic(cwarp_ncop, 1280, 720, vs.YUV444P16)
    scaled_ncop = join([descale_ncop, plane(chroma_down_ncop, 1), plane(chroma_down_ncop, 2)])
    scaled_ncop = scaled_ncop.resize.Bicubic(1296, 728).std.Crop(top=4, bottom=4)
    downscale_03 = src_03.resize.Bicubic(1296, 728, vs.YUV444P16).std.Crop(top=4, bottom=4)
    merge_credits_03 = core.std.MaskedMerge(scaled_ncop, downscale_03, op_mask)

    for prop, val in PROPS_DVD:
        src = src.std.SetFrameProp(prop, intval=val)

    dvd_i444 = vdf.scale.to_444(depth(src, 32), src.width, src.height, join_planes=True)
    dvd_rgb = dvd_i444.resize.Bicubic(format=vs.RGB24, dither_type='error_diffusion', matrix_in=6)

    upscale = vsgan.run(dvd_rgb)
    conv = core.resize.Bicubic(upscale, format=vs.YUV444P16, matrix=1)
    scaled = depth(muf.SSIM_downsample(conv, 1296, 720), 16)

    splice_op = lvf.rfs(scaled, merge_credits_03, [(0, 203), (263, 1740), (1836, 2018)])

    # Splicing for the ED. Halves of the ED have visuals, others are credits
    masked_nced = src_nced.resize.Bicubic(chromaloc_in=1, chromaloc=0)
    cwarp_nced = masked_nced.warp.AWarpSharp2(thresh=88, blur=3, type=1, depth=6, planes=[1, 2])
    descale_nced = lvf.kernels.Bicubic().descale(plane(depth(src_nced, 16), 0), 1280, 720)
    chroma_down_nced = core.resize.Bicubic(cwarp_nced, 1280, 720, vs.YUV444P16)
    scaled_nced = join([descale_nced, plane(chroma_down_nced, 1), plane(chroma_down_nced, 2)])
    scaled_nced = scaled_nced.resize.Bicubic(1296, 728).std.Crop(top=4, bottom=4)

    ed_nc = scaled_nced
    ed_dvd = splice_op[31888:]

    sq_top = lvf.mask.BoundingBox((0, 0), (ed_nc.width, ed_nc.height/2)).get_mask(ed_nc)
    sq_left = lvf.mask.BoundingBox((0, 0), (ed_nc.width/2, ed_nc.height)).get_mask(ed_nc)
    sq_right = sq_left.std.Invert()

    mask_top = core.std.MaskedMerge(ed_dvd, ed_nc, sq_top)
    mask_left = core.std.MaskedMerge(ed_dvd, ed_nc, sq_left)
    mask_right = core.std.MaskedMerge(ed_dvd, ed_nc, sq_right)

    ed_splice = lvf.rfs(ed_dvd, mask_top, [(0, 1213), (1613, 1673), (1794, None)])
    ed_splice = lvf.rfs(ed_splice, mask_left, [(1390, 1503)])
    ed_splice = lvf.rfs(ed_splice, mask_right, [(1214, 1389), (1504, 1612), (1674, 1793)])

    splice_ed = insert_clip(splice_op, ed_splice, edstart)

    # Back to more regular filtering
    stab = haf.GSMC(splice_ed, radius=2, planes=[0])
    decs = vdf.noise.decsiz(stab, sigmaS=8, min_in=208 << 8, max_in=232 << 8)

    detail_mask = flt.detail_mask(decs, brz=(1800, 3500))
    deband = dbs.debanders.dumb3kdb(decs, threshold=32, grain=16)
    deband_masked = core.std.MaskedMerge(deband, decs, detail_mask)

    grain = vdf.noise.Graigasm(  # Mostly stolen from Varde tbh
        thrs=[x << 8 for x in (32, 80, 128, 176)],
        strengths=[(0.25, 0.0), (0.2, 0.0), (0.15, 0.0), (0.0, 0.0)],
        sizes=(1.2, 1.15, 1.05, 1),
        sharps=(65, 50, 40, 40),
        grainers=[
            vdf.noise.AddGrain(seed=69420, constant=True),
            vdf.noise.AddGrain(seed=69420, constant=False),
            vdf.noise.AddGrain(seed=69420, constant=False)
        ]).graining(deband_masked)

    return grain
Example #22
0
def masked_deband(clip: vs.VideoNode,
                  denoised: bool = False,
                  deband_args: Dict[str, Any] = {}) -> vs.VideoNode:
    """
    Written by Zastin, *CAUTIOUSLY* modified by puny little me

    This is all pure black magic to me,
    so I'm just gonna pretend I didn't see anything.
    """
    import zzfunc as zzf

    placebo_args: Dict[str, Any] = dict(iterations=2,
                                        threshold=4.0,
                                        radius=12,
                                        grain=4.0)
    placebo_args |= deband_args

    brz = 256 if denoised else 384

    assert clip.format is not None

    clip_depth = clip.format.bits_per_sample
    if clip_depth != 16:
        clip = depth(clip, 16)

    clip_y = plane(clip, 0)
    stats = clip_y.std.PlaneStats()
    agm3 = core.adg.Mask(stats, 3)

    ymax = maxm(clip_y, sw=30, mode='ellipse')
    ymin = minm(clip_y, sw=30, mode='ellipse')

    # edge detection
    thr = 3.2 * 256
    ypw0 = clip_y.std.Prewitt()
    ypw = ypw0.std.Binarize(thr).rgvs.RemoveGrain(11)

    # range masks (neighborhood max - min)
    rad, thr = 3, 2.5 * 256
    yrangesml = core.std.Expr([ymax[3], ymin[3]], 'x y - abs')
    yrangesml = yrangesml.std.Binarize(thr).std.BoxBlur(0, 2, 1, 2, 1)

    rad, thr = 16, 4 * 256
    yrangebig0 = core.std.Expr([ymax[rad], ymin[rad]], 'x y - abs')
    yrangebig = yrangebig0.std.Binarize(thr)
    yrangebig = minm(yrangebig,
                     sw=rad * 3 // 4,
                     threshold=65536 // ((rad * 3 // 4) + 1),
                     mode='ellipse')[-1]
    yrangebig = yrangebig.std.BoxBlur(0, rad // 4, 1, rad // 4, 1)

    # morphological masks (shapes)
    rad, thr = 30, 1 * 256
    ymph = core.std.Expr([
        clip_y,
        maxm(ymin[rad], sw=rad, mode='ellipse')[rad],
        minm(ymax[rad], sw=rad, mode='ellipse')[rad]
    ], 'x y - z x - max')
    ymph = ymph.std.Binarize(brz)
    ymph = ymph.std.Minimum().std.Maximum()
    ymph = ymph.std.BoxBlur(0, 4, 1, 4, 1)

    grad_mask = zzf.combine([ymph, yrangesml, ypw])
    grain_mask = core.std.Expr([
        yrangebig, grad_mask,
        ypw0.std.Binarize(2000).std.Maximum().std.Maximum()
    ],
                               expr='65535 y - x min z -').std.BoxBlur(
                                   0, 16, 1, 16, 1)

    ydebn_strong = clip_y.placebo.Deband(1, **placebo_args)

    ydebn_normal = clip_y.f3kdb.Deband(16, 41, 0, 0, 0, 0, output_depth=16)
    ydebn = ydebn_strong.std.MaskedMerge(ydebn_normal, grad_mask)
    ydebn = ydebn.std.MaskedMerge(clip_y, yrangebig)

    strong_grain = ydebn_strong.grain.Add(0.25, constant=True, seed=69420)
    normal_grain = ydebn.std.MaskedMerge(
        ydebn.grain.Add(0.1, constant=True, seed=69420), agm3)
    y_final = normal_grain.std.MaskedMerge(strong_grain, grain_mask)
    merged = join([y_final, plane(clip, 1), plane(clip, 2)])
    return merged if clip_depth == 16 \
        else depth(merged, clip_depth)
Example #23
0
def pre_filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]:
    """
    Regular filtering to get the output images to stitch.

    This is preferable over handling it unfiltered, since it'll be faster than encoding it
    and reduces the amount of jitter caused by the upscale after.
    """
    import lvsfunc as lvf
    import rekt
    import vardefunc as vdf
    from awsmfunc import bbmod
    from muvsfunc import SSIM_downsample
    from vsutil import depth, get_y, join, plane

    src = JP_BD.clip_cut

    # Fixing animation f**k-ups
    if freeze_ranges:
        src = core.std.FreezeFrames(
            src,
            [s[0] for s in freeze_ranges],
            [e[1] for e in freeze_ranges],
            [f[2] for f in freeze_ranges]
        )

    # Edgefixing
    ef = rekt.rektlvls(
        src, prot_val=[16, 235], min=16, max=235,
        rownum=[0, src.height-1], rowval=[16, 16],
        colnum=[0, src.width-1], colval=[16, 16],
    )

    bb_y = bbmod(ef, left=1, top=1, right=1, bottom=1, thresh=32, y=True, u=False, v=False)
    bb_uv = bbmod(bb_y, left=2, top=2, right=2, bottom=2, y=False, u=True, v=True)

    cshift = flt.shift_chroma(bb_uv, left=0.6)
    cshift = lvf.rfs(bb_uv, cshift, cshift_left_ranges)

    bb32 = depth(cshift, 32)
    bb32_y = get_y(bb32)

    # Descaling + DPIR while it's at a lower res (so I can actually run it because >memory issues xd)
    descale = lvf.kernels.Catrom().descale(bb32_y, 1280, 720)
    downscale = lvf.kernels.Catrom(format=vs.YUV444PS).scale(bb32, 1280, 720)
    descale_444 = join([descale, plane(downscale, 1), plane(downscale, 2)])
    denoise_y = lvf.deblock.vsdpir(descale_444, strength=2.75, mode='deblock', matrix=1, i444=True, cuda=True)

    supersample = vdf.scale.fsrcnnx_upscale(get_y(denoise_y), shader_file=shader_file, downscaler=None)
    downscaled = SSIM_downsample(supersample, src.width, src.height, smooth=((3 ** 2 - 1) / 12) ** 0.5,
                                 sigmoid=True, filter_param_a=0, filter_param_b=0)

    # Create credit mask
    upscale = lvf.kernels.Catrom().scale(descale, src.width, src.height)
    credit_mask = lvf.scale.descale_detail_mask(bb32_y, upscale, threshold=0.055) \
        .std.Deflate().std.Deflate().std.Minimum()

    # Merge early for additional accuracy with DPIR
    merged = core.std.MaskedMerge(downscaled, bb32_y, credit_mask)

    down_y = lvf.kernels.Catrom().scale(merged, src.width/2, src.height/2)
    down_i444 = join([down_y, plane(bb32, 1), plane(bb32, 2)])
    deblock_down = lvf.deblock.vsdpir(down_i444, strength=3, mode='denoise', matrix=1, i444=True, cuda=True)

    scaled = depth(join([merged, plane(deblock_down, 1), plane(deblock_down, 2)]), 16)

    # Final bit of "denoising"
    dft = core.dfttest.DFTTest(scaled, sigma=2.0, tbsize=5, tosize=3, planes=[0])
    decs = vdf.noise.decsiz(dft, sigmaS=4, min_in=208 << 8, max_in=232 << 8)

    # AA
    baa = lvf.aa.based_aa(decs, str(shader_file))
    sraa = lvf.sraa(decs, rfactor=1.65)
    clmp = lvf.aa.clamp_aa(decs, baa, sraa, strength=1.3)

    dehalo = lvf.dehalo.masked_dha(clmp, rx=1.4, ry=1.4, brightstr=0.4)
    cwarp = core.warp.AWarpSharp2(dehalo, thresh=72, blur=3, type=1, depth=4, planes=[1, 2])

    # Merge credits (if applicable)
    merged = core.std.MaskedMerge(cwarp, depth(bb32, 16), depth(credit_mask, 16))

    deband = core.average.Mean([
        flt.masked_f3kdb(merged, rad=16, thr=[20, 24], grain=[24, 12]),
        flt.masked_f3kdb(merged, rad=20, thr=[28, 24], grain=[24, 12]),
        flt.masked_placebo(merged, rad=6, thr=2.5, itr=2, grain=4)
    ])

    no_flt = lvf.rfs(deband, depth(bb32, 16), no_filter)

    return no_flt
Example #24
0
def FaggotDB(clip: vs.VideoNode,
             thrY=40,
             thrC=None,
             radiusY=15,
             radiusC=15,
             CbY=44,
             CrY=44,
             CbC=44,
             CrC=44,
             grainY=20,
             grainC=None,
             sample_mode=2,
             neo=False,
             dynamic_grainY=False,
             dynamic_grainC=False,
             tv_range=True,
             mask=None) -> vs.VideoNode:

    funcName = "FaggotDB"

    # Original Idea: Author who created Fag3kdb. Edited by AlucardSama04

    if not isinstance(clip, vs.VideoNode):
        raise TypeError(f"{funcName}: This is not a clip")

    if clip.format.bits_per_sample != 16:
        raise TypeError(f"{funcName}: Only 16Bit clips are supported")

    if not isinstance(mask, vs.VideoNode):
        raise vs.Error(f"{funcName}: mask' only clip inputs")

    if thrC is None:
        thrC = int(round(thrY / 2))

    if grainC is None:
        grainC = int(round(grainY / 2))

    f3kdb = core.neo_f3kdb.Deband if neo else core.f3kdb.Deband

    U = plane(clip, 1)
    V = plane(clip, 2)
    U = f3kdb(U,
              range=radiusC,
              y=thrC,
              cb=CbC,
              cr=CrC,
              grainy=grainC,
              grainc=0,
              sample_mode=sample_mode,
              dynamic_grain=dynamic_grainC,
              keep_tv_range=tv_range,
              output_depth=16)

    V = f3kdb(V,
              range=radiusC,
              y=thrC,
              cb=CbC,
              cr=CrC,
              grainy=grainC,
              grainc=0,
              sample_mode=sample_mode,
              dynamic_grain=dynamic_grainC,
              keep_tv_range=tv_range,
              output_depth=16)

    filtered = core.std.ShufflePlanes([clip, U, V], [0, 0, 0], vs.YUV)
    filtered = f3kdb(filtered,
                     range=radiusY,
                     y=thrY,
                     cb=CbY,
                     cr=CrY,
                     grainy=grainY,
                     grainc=0,
                     sample_mode=sample_mode,
                     dynamic_grain=dynamic_grainY,
                     keep_tv_range=tv_range,
                     output_depth=16)

    return core.std.MaskedMerge(
        filtered, clip,
        mask)  #from vapoursynth import core, VideoNode, GRAY, YUV
Example #25
0
def descale(clip: vs.VideoNode,
            upscaler: Optional[Callable[[vs.VideoNode, int, int],
                                        vs.VideoNode]] = reupscale,
            width: Union[int, List[int], None] = None,
            height: Union[int, List[int]] = 720,
            kernel: kernels.Kernel = kernels.Bicubic(b=0, c=1 / 2),
            threshold: float = 0.0,
            mask: Optional[Callable[[vs.VideoNode, vs.VideoNode],
                                    vs.VideoNode]] = descale_detail_mask,
            src_left: float = 0.0,
            src_top: float = 0.0,
            show_mask: bool = False) -> vs.VideoNode:
    """
    A unified descaling function.
    Includes support for handling fractional resolutions (experimental),
    multiple resolutions, detail masking, and conditional scaling.

    If you want to descale to a fractional resolution,
    set src_left and src_top and round up the target height.

    If the source has multiple native resolutions, specify ``height``
    as a list.

    If you want to conditionally descale, specify a non-zero threshold.

    Dependencies: vapoursynth-descale, znedi3

    :param clip:                    Clip to descale
    :param upscaler:                Callable function with signature upscaler(clip, width, height)
                                    -> vs.VideoNode to be used for reupscaling.
                                    Must be capable of handling variable res clips
                                    for multiple heights and conditional scaling.
                                    If a single height is given and upscaler is None,
                                    a constant resolution GRAY clip will be returned instead.
                                    Note that if upscaler is None, no upscaling will be performed
                                    and neither detail masking nor proper fractional descaling can be preformed.
                                    (Default: :py:func:`lvsfunc.scale.reupscale`)
    :param width:                   Width to descale to (if None, auto-calculated)
    :param height:                  Height(s) to descale to. List indicates multiple resolutions,
                                    the function will determine the best. (Default: 720)
    :param kernel:                  Kernel used to descale (see :py:class:`lvsfunc.kernels.Kernel`,
                                    (Default: kernels.Bicubic(b=0, c=1/2))
    :param threshold:               Error threshold for conditional descaling (Default: 0.0, always descale)
    :param mask:                    Function used to mask detail. If ``None``, no masking.
                                    Function must accept a clip and a reupscaled clip and return a mask.
                                    (Default: :py:func:`lvsfunc.scale.descale_detail_mask`)
    :param src_left:                Horizontal shifting for fractional resolutions (Default: 0.0)
    :param src_top:                 Vertical shifting for fractional resolutions (Default: 0.0)
    :param show_mask:               Return detail mask

    :return:                       Descaled and re-upscaled clip with float bitdepth
    """
    if clip.format is None:
        raise ValueError("descale: 'Variable-format clips not supported'")

    if type(height) is int:
        height = [cast(int, height)]

    height = cast(List[int], height)

    if type(width) is int:
        width = [cast(int, width)]
    elif width is None:
        width = [
            get_w(h, aspect_ratio=clip.width / clip.height) for h in height
        ]

    width = cast(List[int], width)

    if len(width) != len(height):
        raise ValueError(
            "descale: Asymmetric number of heights and widths specified")

    resolutions = [Resolution(*r) for r in zip(width, height)]

    clip = depth(clip, 32)
    assert clip.format is not None  # clip was modified by depth, but that wont make it variable
    clip_y = get_y(clip) \
        .std.SetFrameProp('descaleResolution', intval=clip.height)

    variable_res_clip = core.std.Splice([
        core.std.BlankClip(clip_y, length=len(clip) - 1),
        core.std.BlankClip(clip_y, length=1, width=clip.width + 1)
    ],
                                        mismatch=True)

    descale_partial = partial(_perform_descale, clip=clip_y, kernel=kernel)
    clips_by_resolution = {
        c.resolution.height: c
        for c in map(descale_partial, resolutions)
    }

    props = [c.diff for c in clips_by_resolution.values()]
    select_partial = partial(_select_descale,
                             threshold=threshold,
                             clip=clip_y,
                             clips_by_resolution=clips_by_resolution)

    descaled = core.std.FrameEval(variable_res_clip,
                                  select_partial,
                                  prop_src=props)

    if src_left != 0 or src_top != 0:
        descaled = core.resize.Bicubic(descaled,
                                       src_left=src_left,
                                       src_top=src_top)

    if upscaler is None:
        upscaled = descaled
        if len(height) == 1:
            upscaled = core.resize.Point(upscaled, width[0], height[0])
        else:
            return upscaled
    else:
        upscaled = upscaler(descaled, clip.width, clip.height)

    if src_left != 0 or src_top != 0:
        upscaled = core.resize.Bicubic(descaled,
                                       src_left=-src_left,
                                       src_top=-src_top)

    if upscaled.format is None:
        raise RuntimeError(
            "descale: 'Upscaler cannot return variable-format clips'")

    if mask:
        clip_y = clip_y.resize.Point(format=upscaled.format.id)
        rescaled = kernel.scale(descaled, clip.width, clip.height,
                                (src_left, src_top))
        rescaled = rescaled.resize.Point(format=clip.format.id)
        dmask = mask(clip_y, rescaled)

        if upscaler is None:
            dmask = core.resize.Spline36(dmask, upscaled.width,
                                         upscaled.height)
            clip_y = core.resize.Spline36(clip_y, upscaled.width,
                                          upscaled.height)

        if show_mask:
            return dmask

        upscaled = core.std.MaskedMerge(upscaled, clip_y, dmask)

    upscaled = depth(upscaled, get_depth(clip))

    if clip.format.num_planes == 1 or upscaler is None:
        return upscaled
    return join([upscaled, plane(clip, 1), plane(clip, 2)])
Example #26
0
 def test_plane(self):
     y = vs.core.std.BlankClip(format=vs.GRAY8)
     # This should be a no-op, and even the clip reference shouldn’t change
     self.assertEqual(y, vsutil.plane(y, 0))