예제 #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)
def RemoveGrainM(clip, mode=2, modeu=None, modev=None, iter=None, planes=None):
    if not isinstance(clip, vs.VideoNode):
        raise TypeError('RemoveGrainM: This is not a clip')

    numplanes = clip.format.num_planes
    planes = getplanes(planes, numplanes, 'RemoveGrainM')

    mode = [mode] if isinstance(mode, int) else mode
    modeu = [0] if numplanes < 2 else fallback(
        [modeu] if isinstance(modeu, int) else modeu, mode)
    modev = [0] if numplanes < 3 else fallback(
        [modev] if isinstance(modev, int) else modev, modeu)

    iter = fallback(iter, [len(x) for x in (mode, modeu, modev)])
    iter = append_params(iter, 3)
    iter = [iter[x] if x in planes else 0 for x in range(3)]

    mode = [0] if iter[0] is 0 else append_params(mode, iter[0])[:iter[0]]
    modeu = [0] if iter[1] is 0 else append_params(modeu, iter[1])[:iter[1]]
    modev = [0] if iter[2] is 0 else append_params(modev, iter[2])[:iter[2]]

    iterall = max(iter[:numplanes])

    while len(mode) < iterall:
        mode.append(0)
    while len(modeu) < iterall:
        modeu.append(0)
    while len(modev) < iterall:
        modev.append(0)

    for x in range(iterall):
        clip = RemoveGrain(clip, [mode[x], modeu[x], modev[x]])

    return clip
def Clense(clip, previous=None, next=None, planes=None, grey=False):
    if not isinstance(clip, vs.VideoNode):
        raise TypeError('Clense: This is not a clip')
    if next is not None:
        if not isinstance(next, vs.VideoNode):
            raise TypeError('Clense: This is not a clip')
    if previous is not None:
        if not isinstance(previous, vs.VideoNode):
            raise TypeError('Clense: This is not a clip')

    if grey:
        planes = 0

###################################################################
### Use std.Expr for integer clips ################################
    if clip.format.bits_per_sample < 32:
        previous = fallback(previous, clip)
        previous = previous[0] + previous[:-1]
        next = fallback(next, clip)
        next = next[1:] + next[-1]
        return MedianClip([clip, previous, next], planes=planes)


###################################################################

    return getnamespace(clip).Clense(clip, next, previous, planes)
def MinFilter(clip, filtera, filterb=None, mode=None, planes=None):
    mode = fallback(mode, 2 if filterb is None else 1)
    filterb = fallback(filterb, filtera)
    if mode == 1:
        return MedianClip([clip, filtera(clip), filterb(clip)], planes=planes)
    core = vs.core
    filtered = filtera(clip)
    diffa = core.std.MakeDiff(clip, filtered, planes=planes)
    diffb = filterb(diffa)
    return MedianDiff(clip, diffa, diffb, planes=planes)
예제 #5
0
def bidehalo(
    clip: vs.VideoNode,
    ref: Optional[vs.VideoNode] = None,
    sigmaS: float = 1.5,
    sigmaR: float = 5 / 255,
    sigmaS_final: Optional[float] = None,
    sigmaR_final: Optional[float] = None,
    bilateral_args: Dict[str, Any] = {},
    bm3d_args: Dict[str, Any] = {},
) -> vs.VideoNode:
    """
    A simple dehaloing function using bilateral and BM3D to remove bright haloing around edges.
    If a ref clip is passed, that will be masked onto the clip instead of a blurred clip.

    :param clip:                Source clip
    :param ref:                 Ref clip
    :param sigmaS:              Bilateral's spatial weight sigma
    :param sigmaR:              Bilateral's range weight sigma
    :param sigmaS_final:        Final bilateral call's spatial weight sigma.
                                You'll want this to be much weaker than the initial `sigmaS`.
                                If `None`, 1/3rd of `sigmaS`.
    :param sigmaR_final:        Bilateral's range weight sigma.
                                if `None`, same as `sigmaR`
    :param bilateral_args:      Additional parameters to pass to bilateral
    :param bm3d_args:           Additional parameters to pass to :py:class:`lvsfunc.denoise.bm3d`

    :return:                    Dehalo'd clip
    """
    bm3ddh_args: Dict[str, Any] = dict(sigma=8, radius=1, pre=clip)
    bm3ddh_args.update(bm3d_args)

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

    sigmaS_final = fallback(sigmaS_final, sigmaS / 3)
    sigmaR_final = fallback(sigmaR_final, sigmaR)

    if ref is None:
        den = depth(denoise.bm3d(clip, **bm3ddh_args), 16)

        ref = den.bilateral.Bilateral(sigmaS=sigmaS,
                                      sigmaR=sigmaR,
                                      **bilateral_args)
        bidh = den.bilateral.Bilateral(ref=ref,
                                       sigmaS=sigmaS_final,
                                       sigmaR=sigmaR_final,
                                       **bilateral_args)
        bidh = depth(bidh, clip.format.bits_per_sample)
    else:
        bidh = depth(ref, clip.format.bits_per_sample)

    restore_dark = core.std.Expr([clip, bidh], "x y < x y ?")
    return restore_dark
def Median(clip,
           radius=None,
           planes=None,
           mode='s',
           vcmode=1,
           range_in=None,
           memsize=1048576,
           opt=0,
           r=1):
    if not isinstance(clip, vs.VideoNode):
        raise TypeError('Median: This is not a clip')

    f = clip.format
    bits = f.bits_per_sample
    numplanes = f.num_planes
    range_in = fallback(range_in, f.color_family is vs.RGB)

    planes = getplanes(planes, numplanes, 'Median')

    radius = fallback(radius, r)

    radius = append_params(radius, numplanes)[:numplanes]
    mode = append_params(mode, numplanes)[:numplanes]
    vcmode = append_params(vcmode, numplanes)[:numplanes]

    radius = [radius[x] if x in planes else 0 for x in range(numplanes)]
    vcmode = [vcmode[x] if x in planes else 0 for x in range(numplanes)]
    aplanes = [3 if radius[x] > 0 else 1 for x in range(numplanes)]

    pr = [radius[x] for x in planes]
    pm = [mode[x] for x in planes]

    if max(radius) < 1:
        return clip

    mixproc = ('h' in pm or 'v' in pm) and max(pr) > 1 and numplanes - len(
        planes) != 0  # no planes parameter in average.Median
    mixproc = mixproc or any(len(set(p)) > 1 for p in (pr, pm))

    if mixproc:
        clips = split(clip)
        return join([
            Median_internal(clips[x], radius[x], [3], mode[x], vcmode[x],
                            range_in, [0, 1, 1][x], memsize, opt)
            for x in range(numplanes)
        ])

    return Median_internal(clip, pr[0], aplanes, pm[0], vcmode, range_in, 0,
                           memsize, opt)
예제 #7
0
def mt_xxpand_multi(clip: vs.VideoNode,
                    sw: int = 1,
                    sh: Optional[int] = None,
                    mode: str = 'square',
                    planes: Optional[List[int]] = None,
                    start: int = 0,
                    M__imum: Callable[[vs.VideoNode, Any, Any],
                                      vs.VideoNode] = core.std.Maximum,
                    **params: Any) -> List[vs.VideoNode]:
    sh = fallback(sh, sw)

    if mode == 'ellipse':
        coordinates = [[1] * 8, [0, 1, 0, 1, 1, 0, 1, 0],
                       [0, 1, 0, 1, 1, 0, 1, 0]]
    elif mode == 'losange':
        coordinates = [[0, 1, 0, 1, 1, 0, 1, 0]] * 3
    else:
        coordinates = [[1] * 8] * 3

    clips = [clip]
    end = min(sw, sh) + start

    for x in range(start, end):
        clips += [
            M__imum(clips[-1], coordinates=coordinates[x % 3], **params)
        ]  # type: ignore[call-arg]  # I can't be assed to sort this one out. Zastin pls  # noqa
    for x in range(end, end + sw - sh):
        clips += [
            M__imum(clips[-1], coordinates=[0, 0, 0, 1, 1, 0, 0, 0], **params)
        ]  # type: ignore[call-arg]  # I can't be assed to sort this one out. Zastin pls  # noqa
    for x in range(end, end + sh - sw):
        clips += [
            M__imum(clips[-1], coordinates=[0, 1, 0, 0, 0, 0, 1, 0], **params)
        ]  # type: ignore[call-arg]  # I can't be assed to sort this one out. Zastin pls  # noqa
    return clips
def sbr(clip, radius=None, planes=None, mode='s', blur='gauss', r=1):
    if not isinstance(clip, vs.VideoNode):
        raise TypeError('sbr: This is not a clip')

    numplanes = clip.format.num_planes

    planes = getplanes(planes, numplanes, 'sbr')

    radius = fallback(radius, r)

    radius = append_params(radius, numplanes)[:numplanes]
    mode = append_params(mode, numplanes)[:numplanes]
    blur = append_params(blur, numplanes)[:numplanes]

    radius = [radius[x] if x in planes else -1 for x in range(numplanes)]
    aplanes = [3 if radius[x] >= 0 else 1 for x in range(numplanes)]

    pr = [radius[x] for x in planes]
    pm = [mode[x] for x in planes]
    pb = [blur[x] for x in planes]

    if max(radius) < 1:
        return clip

    mixproc = numplanes - len(planes) > 1
    mixproc = mixproc or any(len(set(p)) > 1 for p in (pr, pm, pb))

    if mixproc:
        clips = split(clip)
        return join([
            sbr_internal(clips[x], radius[x], [3], mode[x], blur[x])
            for x in range(numplanes)
        ])

    return sbr_internal(clip, pr[0], aplanes, pm[0], pb[0])
def MinBlur(clip,
            radius=None,
            planes=None,
            mode='s',
            blur='gauss',
            range_in=None,
            memsize=1048576,
            opt=0,
            r=1):
    if not isinstance(clip, vs.VideoNode):
        raise TypeError('MinBlur: This is not a clip')

    numplanes = clip.format.num_planes

    planes = getplanes(planes, numplanes, 'MinBlur')

    radius = fallback(radius, r)
    radius = append_params(radius, numplanes)[:numplanes]
    radius = [radius[x] if x in planes else 0 for x in range(numplanes)]

    aplanes = [3 if radius[x] >= 0 else 1 for x in range(numplanes)]

    if max(radius) < 0:
        return clip

    sbr_radius = [1 if radius[x] is 0 else 0 for x in range(numplanes)]
    med_radius = [1 if radius[x] is 0 else radius[x] for x in range(numplanes)]

    RG11 = Blur(clip, radius, avstovs(aplanes), mode, blur)
    RG11 = sbr(RG11, sbr_radius, avstovs(aplanes), mode, blur)

    RG4 = Median(clip, med_radius, avstovs(aplanes), mode, 1, range_in,
                 memsize, opt)

    return MedianClip([clip, RG11, RG4], planes=planes)
예제 #10
0
def mt_xxpand_multi(clip: vs.VideoNode, sw: int = 1, sh: Optional[int] = None,
                    mode: XxpandModes = XxpandModes.SQUARE,
                    planes: Union[int, List[int], None] = None, start: int = 0,
                    m__imum: Callable[..., vs.VideoNode] = core.std.Maximum, **params: Any) -> List[vs.VideoNode]:
    """
    blame zastin for this
    """
    sh = vsutil.fallback(sh, sw)
    assert clip.format is not None
    planes = list(range(clip.format.num_planes)) if planes is None else [planes] if isinstance(planes, int) else planes

    if mode == XxpandModes.ELLIPSE:
        coordinates = [[1]*8, [0, 1, 0, 1, 1, 0, 1, 0], [0, 1, 0, 1, 1, 0, 1, 0]]
    elif mode == XxpandModes.LOSANGE:
        coordinates = [[0, 1, 0, 1, 1, 0, 1, 0]] * 3
    else:
        coordinates = [[1]*8] * 3

    clips = [clip]

    end = min(sw, sh) + start

    for x in range(start, end):
        clips += [m__imum(clips[-1], coordinates=coordinates[x % 3], planes=planes, **params)]

    for x in range(end, end + sw - sh):
        clips += [m__imum(clips[-1], coordinates=[0, 0, 0, 1, 1, 0, 0, 0], planes=planes, **params)]

    for x in range(end, end + sh - sw):
        clips += [m__imum(clips[-1], coordinates=[0, 1, 0, 0, 0, 0, 1, 0], planes=planes, **params)]

    return clips
예제 #11
0
    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)
예제 #12
0
def Depth(clip, planes, range_in, cal, outfmt=None):
    core = vs.core
    fmt = clip.format
    numplanes = fmt.num_planes
    range_in = 1 if range_in else 0
    yexpr = ['x 56064 * 4096 +', 'x 65535 *'
             ][range_in] if outfmt is None else [
                 'x 4096 - 56064 /', 'x 65535 /'
             ][range_in]
    cexpr = ['x 57344 * 32768 +', 'x 65535 * 32768 +'
             ][range_in] if outfmt is None else [
                 'x 32768 - 57344 /', 'x 32768 - 65535 /'
             ][range_in]
    expr = [yexpr] * 3 if fmt.color_family == vs.RGB else [yexpr, cexpr, cexpr]
    outfmt = fallback(
        outfmt, clip.format.replace(bits_per_sample=16,
                                    sample_type=vs.INTEGER))
    if cal:
        return core.std.Expr(clip, expr[-1], outfmt)
    if len(planes) != numplanes:
        expr = [expr[x] if x in planes else '' for x in range(numplanes)]
        return core.std.Expr(clip, expr, outfmt.id)
    return core.resize.Point(clip,
                             range=range_in,
                             range_in=range_in,
                             format=outfmt.id)
예제 #13
0
def mt_xxpand_multi(clip, sw=1, sh=None, mode='square', planes=None, start=0, M__imum=core.std.Maximum, **params):
    sh = fallback(sh, sw)
    planes = list(range(clip.format.num_planes)) if planes is None else [planes] if isinstance(planes, int) else planes

    if mode == 'ellipse':
        coordinates = [[1]*8, [0, 1, 0, 1, 1, 0, 1, 0], [0, 1, 0, 1, 1, 0, 1, 0]]
    elif mode == 'losange':
        coordinates = [[0, 1, 0, 1, 1, 0, 1, 0]] * 3
    else:
        coordinates = [[1]*8] * 3

    clips = [clip]

    end = min(sw, sh) + start

    for x in range(start, end):
        clips += [M__imum(clips[-1], coordinates=coordinates[x % 3], planes=planes, **params)]

    for x in range(end, end + sw - sh):
        clips += [M__imum(clips[-1], coordinates=[0, 0, 0, 1, 1, 0, 0, 0], planes=planes, **params)]

    for x in range(end, end + sh - sw):
        clips += [M__imum(clips[-1], coordinates=[0, 1, 0, 0, 0, 0, 1, 0], planes=planes, **params)]

    return clips
예제 #14
0
def MinFilter(clip, filtera, filterb=None, mode=None, planes=None):
    if mode == 1 and filterb is None:
        raise ValueError('rgvs.MinFilter: filterb must be defined when mode=1')

    planes = parse_planes(planes, clip.format.num_planes, 'MinFilter')
    mode = fallback(mode, 2 if filterb is None else 1)

    filterb = fallback(filterb, filtera)

    if mode == 1:
        return MedianClip([clip, filtera(clip), filterb(clip)], planes=planes)

    filtered = filtera(clip)

    diffa = clip.std.MakeDiff(filtered, planes=planes)
    diffb = filterb(diffa)

    return MedianDiff(clip, diffa, diffb, planes=planes)
예제 #15
0
def parse_planes(planes, numplanes, name):
    planes = fallback(planes, list(range(numplanes)))
    if not isinstance(planes, (list, tuple, set, int)):
        raise TypeError(f'{name}: improper "planes" format')
    if isinstance(planes, int):
        planes = [planes]
    else:
        planes = list(set(planes))
    for x in planes:
        if x >= numplanes:
            planes = planes[:planes.index(x)]
            break
    return planes
예제 #16
0
def get_w(height, ar=None, even=None, ref=None):
    even = fallback(even, height % 2 == 0)
    if ar is None:
        if ref is not None:
            try:
                ar = ref.width / ref.height
            except ZeroDivisionError:
                raise TypeError('zz.w ref must have constant width/height')
        else:
            ar = 16 / 9
    width = height * ar
    if even:
        return round(width / 2) * 2
    return round(width)
예제 #17
0
def parse_planes(planes, numplanes=3, name='util.parse_planes'):
    planes = fallback(planes, list(range(numplanes)))
    if isinstance(planes, int):
        planes = [planes]
    if isinstance(planes, tuple):
        planes = list(planes)
    if not isinstance(planes, list):
        raise TypeError(f'zzfunc.{name}: improper "planes" format')
    planes = planes[:min(len(planes), numplanes)]
    for x in planes:
        if x >= numplanes:
            planes = planes[:planes.index(x)]
            break
    return planes
예제 #18
0
def mt_xxpand_multi(
        clip: vs.VideoNode,  # noqa
        sw: int = 1,
        sh: Optional[int] = None,
        mode: str = 'square',
        planes: Union[List[range], int, None] = None,
        start: int = 0,
        M__imum: Any = core.std.Maximum,
        **params: Any) -> List[vs.VideoNode]:
    sh = fallback(sh, sw)
    assert clip.format is not None
    planes = [range(clip.format.num_planes)] or planes

    if mode == 'ellipse':
        coordinates = [[1] * 8, [0, 1, 0, 1, 1, 0, 1, 0],
                       [0, 1, 0, 1, 1, 0, 1, 0]]
    elif mode == 'losange':
        coordinates = [[0, 1, 0, 1, 1, 0, 1, 0]] * 3
    else:
        coordinates = [[1] * 8] * 3

    clips = [clip]
    end = min(sw, sh) + start

    for x in range(start, end):
        clips += [
            M__imum(clips[-1],
                    coordinates=coordinates[x % 3],
                    planes=planes,
                    **params)
        ]
    for x in range(end, end + sw - sh):
        clips += [
            M__imum(clips[-1],
                    coordinates=[0, 0, 0, 1, 1, 0, 0, 0],
                    planes=planes,
                    **params)
        ]
    for x in range(end, end + sh - sw):
        clips += [
            M__imum(clips[-1],
                    coordinates=[0, 1, 0, 0, 0, 0, 1, 0],
                    planes=planes,
                    **params)
        ]
    return clips
예제 #19
0
def join(clipa, clipb=None, clipc=None, colorfamily=None):
    if isinstance(clipa, list):
        clips = clipa
    else:
        clips = [clipa]

    if clipb is not None:
        clips += [clipb]
    if clipc is not None:
        clips += [clipc]
    if len(clips) == 1:
        return clips[0]
    core = vs.core
    colorfamily = fallback(
        colorfamily,
        vs.RGB if clips[0].format.color_family == vs.RGB else vs.YUV)
    if len(clips) == 2:
        return core.std.ShufflePlanes(clips,
                                      planes=[0, 1, 2],
                                      colorfamily=colorfamily)
    return core.std.ShufflePlanes(clips,
                                  planes=[0] * 3,
                                  colorfamily=colorfamily)
예제 #20
0
파일: aa.py 프로젝트: petzku/lvsfunc
def nneedi3_clamp(clip: vs.VideoNode,
                  strength: float = 1,
                  mask: Optional[vs.VideoNode] = None,
                  ret_mask: bool = False,
                  mthr: float = 0.25,
                  show_mask: bool = False,
                  opencl: bool = False,
                  nnedi3cl: Optional[bool] = None,
                  eedi3cl: Optional[bool] = None) -> vs.VideoNode:
    """
    A function that clamps eedi3 to nnedi3 for the purpose of reducing eedi3 artifacts.
    This should fix every issue created by eedi3. For example: https://i.imgur.com/hYVhetS.jpg

    Original function written by Zastin, modified by LightArrowsEXE.

    Dependencies:
    * kagefunc (optional: retinex edgemask)
    * vapoursynth-retinex (optional: retinex edgemask)
    * vapoursynth-tcanny (optional: retinex edgemask)
    * vapoursynth-eedi3
    * vapoursynth-nnedi3
    * vapoursynth-nnedi3cl (optional: opencl)

    :param clip:                Input clip
    :param strength:            Set threshold strength for over/underflow value for clamping eedi3's result
                                to nnedi3 +/- strength * 256 scaled to 8 bit (Default: 1)
    :param mask:                Clip to use for custom mask (Default: None)
    :param ret_mask:            Replace default mask with a retinex edgemask (Default: False)
    :param mthr:                Binarize threshold for the mask, scaled to float (Default: 0.25)
    :param show_mask:           Return mask instead of clip (Default: False)
    :param opencl:              OpenCL acceleration (Default: False)
    :param nnedi3cl:            OpenCL acceleration for nnedi3 (Default: False)
    :param eedi3cl:             OpenCL acceleration for eedi3 (Default: False)

    :return:                    Antialiased clip
    """

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

    clip_y = get_y(clip)

    bits = clip.format.bits_per_sample
    sample_type = clip.format.sample_type
    shift = bits - 8
    thr = strength * (
        1 >> shift) if sample_type == vs.INTEGER else strength / 219
    expr = 'x z - y z - xor y x y {0} + min y {0} - max ?'.format(thr)

    if sample_type == vs.INTEGER:
        mthr = round(mthr * ((1 >> shift) - 1))

    if mask is None:
        try:
            from kagefunc import kirsch
        except ModuleNotFoundError:
            raise ModuleNotFoundError(
                "nnedi3_clamp: missing dependency 'kagefunc'")
        mask = kirsch(clip_y)
        if ret_mask:
            # workaround to support float input
            ret = depth(clip_y, min(16, bits))
            ret = core.retinex.MSRCP(ret,
                                     sigma=[50, 200, 350],
                                     upper_thr=0.005)
            ret = depth(ret, bits)
            tcanny = core.tcanny.TCanny(ret, mode=1, sigma=[1.0])
            tcanny = core.std.Minimum(tcanny,
                                      coordinates=[1, 0, 1, 0, 0, 1, 0, 1])
            # no clamping needed when binarizing
            mask = core.std.Expr([mask, tcanny], 'x y +')
        mask = mask.std.Binarize(thr).std.Maximum().std.Convolution([1] * 9)

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

    nnedi3_args: Dict[str, Any] = dict(nsize=3, nns=3, qual=1)
    eedi3_args: Dict[str, Any] = dict(alpha=0.25,
                                      beta=0.5,
                                      gamma=40,
                                      nrad=2,
                                      mdis=20)

    clip_tra = core.std.Transpose(clip_y)

    if eedi3cl:
        strong = core.eedi3m.EEDI3CL(clip_tra, 0, True, **eedi3_args)
        strong = core.resize.Spline36(strong, height=clip.width, src_top=0.5)
        strong = core.std.Transpose(strong)
        strong = core.eedi3m.EEDI3CL(strong, 0, True, **eedi3_args)
        strong = core.resize.Spline36(strong, height=clip.height, src_top=0.5)
    else:
        strong = core.eedi3m.EEDI3(clip_tra,
                                   0,
                                   True,
                                   mclip=mask.std.Transpose(),
                                   **eedi3_args)
        strong = core.resize.Spline36(strong, height=clip.width, src_top=0.5)
        strong = core.std.Transpose(strong)
        strong = core.eedi3m.EEDI3(strong, 0, True, mclip=mask, **eedi3_args)
        strong = core.resize.Spline36(strong, height=clip.height, src_top=0.5)

    nnedi3: Callable[..., vs.VideoNode] = core.nnedi3.nnedi3
    if nnedi3cl:
        nnedi3 = core.nnedi3cl.NNEDI3CL

    weak = nnedi3(clip_tra, 0, True, **nnedi3_args)
    weak = core.resize.Spline36(weak, height=clip.width, src_top=0.5)
    weak = core.std.Transpose(weak)
    weak = nnedi3(weak, 0, True, **nnedi3_args)
    weak = core.resize.Spline36(weak, height=clip.height, src_top=0.5)

    aa = core.std.Expr([strong, weak, clip_y], expr)

    merged = core.std.MaskedMerge(clip_y, aa, mask)

    if show_mask:
        return mask
    return merged if clip.format.color_family == vs.GRAY else core.std.ShufflePlanes(
        [merged, clip], [0, 1, 2], vs.YUV)
예제 #21
0
 def test_fallback(self):
     self.assertEqual(vsutil.fallback(None, 'a value'), 'a value')
     self.assertEqual(vsutil.fallback('a value', 'another value'), 'a value')
     self.assertEqual(vsutil.fallback(None, sum(range(5))), 10)
예제 #22
0
파일: aa.py 프로젝트: 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)])