Exemple #1
0
def scaled_grain(clip: vs.VideoNode,
                 var: float = 0.25,
                 uvar: float = 0,
                 grain_h: Optional[int] = None,
                 grain_w: Optional[int] = None,
                 static: bool = True,
                 adaptive: bool = False,
                 luma_scaling: int = 12,
                 kernel: str = 'bicubic',
                 b: float = 0,
                 c: float = 1 / 2,
                 taps: int = 3) -> vs.VideoNode:
    """Grains a clip in the given dimensions and merges it with the source clip.

    This is useful for making larger grain patterns when
    using a grain resolution smaller than the source clip's resolution.
    It supports static and dynamic grain with optional adaptive brightness
    masking to grain darker areas more than brighter areas.

    Args:
        clip: The source clip. Assumes YUV format.
        var: Luma grain variance (strength).
        uvar: Chroma grain variance (strength).
        grain_h: Height of the grained clip.
        grain_w: Width of the grained clip.
        static: Determines whether static (constant) or dynamic grain is used.
        adaptive: Determines whether adaptive brightness masking is used.
        luma_scaling: The scaling factor for adaptive brightness masking.
            Lower values increase the graining of brighter areas.
        kernel: The scaling kernel used to scale the grain clip to
            the source clip's dimensions.
        b, c, taps: Parameters for tweaking the kernel.
    """
    grain_h = fallback(grain_h, clip.height / 2)
    grain_w = fallback(grain_w, get_w(grain_h, clip.width / clip.height))

    blank_value = (1 << get_depth(clip)) / 2 if is_integer(clip) else 0
    blank_clip = core.std.BlankClip(
        clip,
        width=grain_w,
        height=grain_h,
        color=[blank_value, blank_value, blank_value])

    grained = core.grain.Add(blank_clip, var=var, uvar=uvar, constant=static)
    grained = fvf.Resize(grained,
                         clip.width,
                         clip.height,
                         kernel=kernel,
                         a1=b,
                         a2=c,
                         taps=taps)

    if adaptive:
        src_res_blank = core.resize.Point(blank_clip, clip.width, clip.height)
        adaptive_mask = core.adg.Mask(core.std.PlaneStats(clip), luma_scaling)
        grained = core.std.MaskedMerge(src_res_blank, grained, adaptive_mask)

    merged = core.std.MergeDiff(clip, grained)

    return clamp(merged)
Exemple #2
0
def inverse_scale(source: vs.VideoNode, width: int = None, height: int = 0, kernel: str = 'bilinear', taps: int = 4,
                  b: float = 1 / 3, c: float = 1 / 3, mask_detail: bool = False, descale_mask_zones: str = '',
                  denoise: bool = False, bm3d_sigma: float = 1, knl_strength: float = 0.4, use_gpu: bool = True) \
        -> vs.VideoNode:
    """
    Use descale to reverse the scaling on a given input clip.
    width, height, kernel, taps, a1, a2 are parameters for resizing.
    descale_mask_zones can be used to only mask certain zones to improve performance; uses rfs syntax.
    denoise, bm3d_sigma, knl_strength, use_gpu are parameters for denoising; denoise = False to disable
    use_gpu = True -> chroma will be denoised with KNLMeansCL (faster)
    """
    if not height:
        raise ValueError(
            'inverse_scale: you need to specify a value for the output height')

    only_luma = source.format.num_planes == 1

    if get_depth(source) != 32:
        source = source.resize.Point(format=source.format.replace(
            bits_per_sample=32, sample_type=vs.FLOAT))
    width = fallback(width, getw(height, source.width / source.height))

    # if we denoise luma and chroma separately, do the chroma here while it’s still 540p
    if denoise and use_gpu and not only_luma:
        source = core.knlm.KNLMeansCL(source,
                                      a=2,
                                      h=knl_strength,
                                      d=3,
                                      device_type='gpu',
                                      device_id=0,
                                      channels='UV')

    planes = split(source)
    planes[0] = _descale_luma(planes[0], width, height, kernel, taps, b, c)
    if only_luma:
        return planes[0]
    planes = _descale_chroma(planes, width, height)

    if mask_detail:
        upscaled = fvf.Resize(planes[0],
                              source.width,
                              source.height,
                              kernel=kernel,
                              taps=taps,
                              a1=b,
                              a2=c)
        planes[0] = mask_descale(get_y(source),
                                 planes[0],
                                 upscaled,
                                 zones=descale_mask_zones)
    scaled = join(planes)
    return mvf.BM3D(
        scaled, radius1=1, sigma=[bm3d_sigma, 0]
        if use_gpu else bm3d_sigma) if denoise else scaled
Exemple #3
0
def test_descale(clip: vs.VideoNode,
                 height: int,
                 kernel: str = 'bicubic',
                 b: float = 1 / 3, c: float = 1 / 3,
                 taps: int = 3,
                 show_error: bool = True) -> vs.VideoNode:
    funcname = "test_descale"
    """
    Generic function to test descales with.
    Descales and reupscales a given clip, allowing you to compare the two easily.

    When comparing, it is recommended to do atleast a 4x zoom using Nearest Neighbor.
    I also suggest using 'compare', as that will make comparison a lot easier.

    Some of this code was leveraged from DescaleAA, and it also uses functions
    available in fvsfunc.

    :param height: int:         Target descaled height.
    :param kernel: str:         Descale kernel - 'bicubic'(default), 'bilinear', 'lanczos', 'spline16', or 'spline36'
    :param b: float:            B-param for bicubic kernel. (Default value = 1 / 3)
    :param c: float:            C-param for bicubic kernel. (Default value = 1 / 3)
    :param taps: int:           Taps param for lanczos kernel. (Default value = 43)
    :param show_error: bool:    Show diff between the original clip and the reupscaled clip
    """

    clip_y = get_y(clip)

    desc = fvf.Resize(clip_y, get_w(height), height,
                      kernel=kernel, a1=b, a2=c, taps=taps,
                      invks=True)
    upsc = fvf.Resize(desc, clip.width, clip.height,
                      kernel=kernel, a1=b, a2=c, taps=taps)
    upsc = core.std.PlaneStats(clip_y, upsc)

    if clip is vs.GRAY:
        return core.text.FrameProps(upsc, "PlaneStatsDiff") if show_error else upsc
    merge = core.std.ShufflePlanes([upsc, clip], planes=[0, 1, 2], colorfamily=vs.YUV)
    return core.text.FrameProps(merge, "PlaneStatsDiff") if show_error else merge
Exemple #4
0
def generate_detail_mask(source,
                         downscaled,
                         kernel='bicubic',
                         taps=4,
                         a1=1 / 3,
                         a2=1 / 3,
                         threshold=0.05):
    upscaled = fvf.Resize(downscaled,
                          source.width,
                          source.height,
                          kernel=kernel,
                          taps=taps,
                          a1=a1,
                          a2=a2)
    mask = core.std.Expr([source, upscaled], 'x y - abs') \
        .resize.Bicubic(downscaled.width, downscaled.height).std.Binarize(threshold)
    mask = iterate(mask, core.std.Maximum, 2)
    return iterate(mask, core.std.Inflate, 2)
Exemple #5
0
    def perform_aa(src, aatype):
        sw = src.width
        sh = src.height

        if aatype == 'nnedi3':
            aa = core.znedi3.nnedi3(src,
                                    field=1,
                                    dh=True,
                                    nsize=nsize,
                                    nns=nns,
                                    qual=qual).std.Transpose()
            aa = core.znedi3.nnedi3(aa,
                                    field=1,
                                    dh=True,
                                    nsize=nsize,
                                    nns=nns,
                                    qual=qual).std.Transpose()
        elif aatype == 'eedi3':
            aa = core.eedi3m.EEDI3CL(src,
                                     field=1,
                                     dh=True,
                                     alpha=alpha,
                                     beta=beta,
                                     nrad=nrad,
                                     mdis=mdis).std.Transpose()
            aa = core.eedi3m.EEDI3CL(aa,
                                     field=1,
                                     dh=True,
                                     alpha=alpha,
                                     beta=beta,
                                     nrad=nrad,
                                     mdis=mdis).std.Transpose()
        elif aatype == 'combo':
            aa = core.eedi3m.EEDI3CL(src,
                                     field=1,
                                     dh=True,
                                     alpha=alpha,
                                     beta=beta,
                                     nrad=nrad,
                                     mdis=mdis)
            aa = core.znedi3.nnedi3(aa,
                                    field=0,
                                    dh=True,
                                    nsize=nsize,
                                    nns=nns,
                                    qual=qual).std.Transpose()
            aa = core.eedi3m.EEDI3CL(aa,
                                     field=1,
                                     dh=True,
                                     alpha=alpha,
                                     beta=beta,
                                     nrad=nrad,
                                     mdis=mdis)
            aa = core.znedi3.nnedi3(aa,
                                    field=0,
                                    dh=True,
                                    nsize=nsize,
                                    nns=nns,
                                    qual=qual).std.Transpose()

        return fvf.Resize(aa, w=sw, h=sh, sx=-0.5, sy=-0.5, kernel=kernel)
Exemple #6
0
def rescale(src,
            w=None,
            h=None,
            mask_detail=False,
            mask=None,
            thr=10,
            expand=2,
            inflate=2,
            descale_kernel='bicubic',
            b=1 / 3,
            c=1 / 3,
            descale_taps=3,
            kernel='spline16',
            taps=None,
            invks=False,
            invkstaps=3,
            a1=None,
            a2=None,
            nsize=4,
            nns=4,
            f=None,
            show_mask=False):
    """
    Descale and re-upscale a clip

    This descales a clip's luma, nnedi3_resamples it back to its original resolution,
    and merges back in the original chroma if applicable. It can also mask detail that is
    greater than the 'native' resolution and merge it back into the final rescaled clip.

    Parameters:
    -----------
    w:                           source clip's native width to descale to
    h:                           source clip's native height to descale to
    mask_detail (False):         mask higher-than-native-resolution detail
    mask:                        external mask clip to use instead of built-in masking
    thr (10):                    threshold of detail to include in built-in mask
    expand (2):                  number of times to expand built-in mask
    inflate (2):                 number of times to inflate built-in mask
    descale_kernel ('bicubic'):  kernel for descale
    b (1/3):                     b value for descale
    c (1/3):                     c value for descale
    descale_taps (3):            taps value for descale
    kernel ('spline16'):         kernel for nnedi3_resample rescale
    taps:                        taps value for nnedi3_resample rescale
    invks (False):               invks for nnedi3_resample rescale
    invkstaps (3):               invkstaps for nnedi3_resample
    a1:                          a1 for nnedi3_resample
    a2:                          a2 for nnedi3_resample
    nsize (4):                   nsize for nnedi3_resample
    nns (4):                     nns for nnedi3_resample
    f:                           function to perform on descaled luma before upscaling
    show_mask (False):           output detail mask

    """
    name = 'rescale'

    if not isinstance(src, vs.VideoNode):
        raise TypeError(name + ": 'src' must be a clip")
    if mask is not None and not isinstance(mask, vs.VideoNode):
        raise TypeError(name + ": 'mask' must be a clip")
    if h is None:
        raise TypeError(name + ": native height 'h' must be given")
    if show_mask and not mask_detail:
        raise TypeError(name +
                        ": 'show_mask' can only be used with mask_detail=True")

    sw = src.width
    sh = src.height
    src_bits = src.format.bits_per_sample
    is_gray = src.format.color_family == vs.GRAY

    if w is None:
        w = h / sh * sw
    if mask is not None and mask.format.bits_per_sample != src_bits:
        mask = fvf.Depth(mask, src_bits, dither_type='none')

    y = src if is_gray else get_y(src)
    descaled = fvf.Resize(y,
                          w,
                          h,
                          kernel=descale_kernel,
                          a1=b,
                          a2=c,
                          taps=descale_taps,
                          invks=True)

    # Built-in diff mask generation (from fvsfunc's DescaleM)
    if mask_detail and mask is None:
        peak = (1 << src_bits) - 1
        thr = hvf.scale(thr, peak)

        up = fvf.Resize(descaled,
                        sw,
                        sh,
                        kernel=descale_kernel,
                        a1=b,
                        a2=c,
                        taps=descale_taps)

        diff_mask = core.std.Expr([y, up], 'x y - abs')
        diff_mask = fvf.Resize(diff_mask, w, h, kernel='bilinear')
        diff_mask = core.std.Binarize(diff_mask, threshold=thr)

        diff_mask = kgf.iterate(diff_mask, core.std.Maximum, expand)
        diff_mask = kgf.iterate(diff_mask, core.std.Inflate, inflate)

        diff_mask = core.resize.Spline36(diff_mask, sw, sh)
    elif mask_detail:
        diff_mask = mask

    if show_mask:
        return diff_mask

    if f is not None:
        descaled = f(descaled)

    rescaled = nnedi3_resample(descaled,
                               sw,
                               sh,
                               nsize=nsize,
                               nns=nns,
                               kernel=kernel,
                               a1=a1,
                               a2=a2,
                               taps=taps,
                               invks=invks,
                               invkstaps=invkstaps)

    if mask_detail:
        rescaled = core.std.MaskedMerge(rescaled, y, diff_mask)

    if is_gray:
        return rescaled

    return merge_chroma(rescaled, src)
Exemple #7
0
 def _get_error(clip, height, kernel, b, c, taps):
     descale = fvf.Resize(clip, get_w(height), height,  kernel=kernel, a1=b, a2=c, taps=taps, invks=True)
     upscale = fvf.Resize(clip, clip.width, clip.height,  kernel=kernel, a1=b, a2=c, taps=taps)
     diff = core.std.PlaneStats(upscale, clip)
     return descale, diff
Exemple #8
0
def Descale444ToTarget(clip: vs.VideoNode,
                       descale_masked: bool = True,
                       nnedi3_rpow2: bool = False,
                       *,
                       native_kernel: str = 'bicubic',
                       native_width: int,
                       native_height: int,
                       target_kernel: str = 'spline36',
                       target_width: int,
                       target_height: int,
                       **kwargs):
    y = clip.std.ShufflePlanes(planes=0, colorfamily=vs.GRAY)
    u = clip.std.ShufflePlanes(planes=1, colorfamily=vs.GRAY)
    v = clip.std.ShufflePlanes(planes=2, colorfamily=vs.GRAY)

    if descale_masked:
        y = fvf.DescaleM(y,
                         descale_kernel=native_kernel,
                         w=native_width,
                         h=native_height,
                         **kwargs)
    else:
        y = fvf.Resize(y,
                       kernel=native_kernel,
                       w=native_width,
                       h=native_height,
                       invks=True,
                       **kwargs)

    if y.height < target_height and y.width < target_width and y.height / y.width == target_height / target_width and nnedi3_rpow2:
        y = rpow2.nnedi3_rpow2(y,
                               correct_shift=True,
                               kernel=target_kernel,
                               width=target_width,
                               height=target_height)
    elif (native_height, native_width) != (target_height,
                                           target_width) or not nnedi3_rpow2:
        y = fvf.Resize(y,
                       w=target_width,
                       h=target_height,
                       kernel=target_kernel)

    if u.height < target_height and u.width < target_width and u.height / u.width == target_height / target_width:
        u = rpow2.nnedi3_rpow2(u,
                               correct_shift=True,
                               kernel=target_kernel,
                               width=y.width,
                               height=y.height)
        v = rpow2.nnedi3_rpow2(v,
                               correct_shift=True,
                               kernel=target_kernel,
                               width=y.width,
                               height=y.height)
    elif (u.height, u.width) != (target_height, target_width):
        u = fvf.Resize(u, w=y.width, h=y.height, kernel=target_kernel, sx=0.25)
        v = fvf.Resize(v, w=y.width, h=y.height, kernel=target_kernel, sx=0.25)

    clip = core.std.ShufflePlanes(clips=[y, u, v],
                                  planes=[0, 0, 0],
                                  colorfamily=vs.YUV)

    return clip
Exemple #9
0
def DescaleAAMod(src: vs.VideoNode,
                 w: Optional[int] = None,
                 h: int = 720,
                 thr: int = 10,
                 kernel: str = 'bicubic',
                 b: Union[float, Fraction] = Fraction(0),
                 c: Union[float, Fraction] = Fraction(1, 2),
                 taps: int = 4,
                 expand: int = 3,
                 inflate: int = 3,
                 showmask: bool = False) -> vs.VideoNode:
    """
    Mod of DescaleAA to use nnedi3_resample, which produces sharper results than nnedi3 rpow2.

    Original script by Frechdachs

    Original Summary:
        Downscale only lineart with an inverted kernel and interpolate
        it back to its original resolution with NNEDI3.

        Parts of higher resolution like credits are protected by a mask.

        Basic idea stolen from a script made by Daiz.

    :param src:         Source clip
    :type src:          VideoNode
    :param w:           Downscale resolution width, defaults to 1280
    :type w:            int, optional
    :param h:           Downscale resolution height, defaults to 720
    :type h:            int
    :param thr:         Threshhold used in masking, defaults to 10
    :type thr:          int
    :param kernel:      Downscaling kernel, defaults to 'bilinear'
    :type kernel:       str
    :param b:           Downscaling parameter used in fvf.Resize, defaults to 0
    :type b:            var
    :param c:           Downscaling parameter used in fvf.Resize, defaults to 1/2
    :type c:            var
    :param taps:        Downscaling parameter used in fvf.Resize, defaults to 4
    :type taps:         int
    :param expand:      Number of times to expand the difference mask, defaults to 3
    :type expand:       int
    :param inflate:     Number of times to inflate the difference mask, defaults to 3
    :type inflate:      int
    :param showmask:    Return mask created, defaults to False
    :type showmask:     bool

    :return:            The filtered video
    :rtype:             VideoNode
    """
    import fvsfunc as fvf
    from nnedi3_resample import nnedi3_resample

    if kernel.lower().startswith('de'):
        kernel = kernel[2:]

    ow = src.width
    oh = src.height

    if w is None:
        w = get_w(h, src.width / src.height)

    bits = src.format.bits_per_sample
    sample_type = src.format.sample_type

    if sample_type == vs.INTEGER:
        maxvalue = (1 << bits) - 1
        thr = thr * maxvalue // 0xFF
    else:
        maxvalue = 1
        thr /= (235 - 16)

    # Fix lineart
    src_y = core.std.ShufflePlanes(src, planes=0, colorfamily=vs.GRAY)
    deb = fvf.Resize(src_y,
                     w,
                     h,
                     kernel=kernel,
                     a1=b,
                     a2=c,
                     taps=taps,
                     invks=True)
    sharp = nnedi3_resample(deb,
                            ow,
                            oh,
                            invks=True,
                            invkstaps=2,
                            kernel="bicubic",
                            a1=0.70,
                            a2=0,
                            nns=4,
                            qual=2,
                            pscrn=4)
    edgemask = core.std.Prewitt(sharp, planes=0)

    if kernel == "bicubic" and c >= 0.7:
        edgemask = core.std.Maximum(edgemask, planes=0)
    sharp = core.resize.Point(sharp, format=src.format.id)

    # Restore true 1080p
    deb_upscale = fvf.Resize(deb, ow, oh, kernel=kernel, a1=b, a2=c, taps=taps)
    diffmask = core.std.Expr([src_y, deb_upscale], 'x y - abs')
    for _ in range(expand):
        diffmask = core.std.Maximum(diffmask, planes=0)
    for _ in range(inflate):
        diffmask = core.std.Inflate(diffmask, planes=0)

    mask = core.std.Expr([diffmask, edgemask],
                         'x {thr} >= 0 y ?'.format(thr=thr))
    mask = mask.std.Inflate().std.Deflate()
    out_y = core.std.MaskedMerge(src, sharp, mask, planes=0)

    #scale chroma
    new_uv = nnedi3_resample(src,
                             ow,
                             oh,
                             invks=True,
                             invkstaps=2,
                             kernel="gauss",
                             a1=30,
                             nns=4,
                             qual=2,
                             pscrn=4,
                             chromak_down="gauss",
                             chromak_down_invks=True,
                             chromak_down_invkstaps=2,
                             chromak_down_taps=1,
                             chromak_down_a1=16)
    edgemask = core.std.Prewitt(new_uv, planes=0)
    edgemask_uv = core.std.Invert(edgemask, planes=[0])

    # Restore true 1080p
    deb_upscale = fvf.Resize(src, ow, oh, kernel=kernel, a1=b, a2=c, taps=taps)
    diffmask = core.std.Expr([src, deb_upscale], 'x y - abs')
    for _ in range(expand):
        diffmask = core.std.Maximum(diffmask, planes=0)
    for _ in range(inflate):
        diffmask = core.std.Inflate(diffmask, planes=0)

    mask_uv = core.std.Expr([diffmask, edgemask_uv],
                            'x {thr} >= 0 y ?'.format(thr=thr))
    mask_uv = mask_uv.std.Inflate().std.Deflate()
    out_uv = core.std.MaskedMerge(src, new_uv, mask_uv, planes=[1, 2])

    out = core.std.ShufflePlanes([out_y, out_uv, out_uv],
                                 planes=[0, 1, 2],
                                 colorfamily=vs.YUV)

    if showmask:
        out = mask
    return out