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)
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)
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)
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
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)
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)
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
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)
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
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)
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
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
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)
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)
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)
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)])