def detail_mask(clip: vs.VideoNode, sigma: float = 1.0, rxsigma: List[int] = [50, 200, 350], pf_sigma: Optional[float] = 1.0, rad: int = 3, brz: Tuple[int, int] = (2500, 4500), rg_mode: int = 17, ) -> vs.VideoNode: """ A detail mask aimed at preserving as much detail as possible within darker areas, even if it contains mostly noise. """ from kagefunc import kirsch from vsutil import iterate bits, clip = _get_bits(clip) clip_y = get_y(clip) pf = core.bilateral.Gaussian(clip_y, sigma=pf_sigma) if pf_sigma else clip_y ret = core.retinex.MSRCP(pf, sigma=rxsigma, upper_thr=0.005) blur_ret = core.bilateral.Gaussian(ret, sigma=sigma) blur_ret_diff = core.std.Expr([blur_ret, ret], "x y -") blur_ret_dfl = core.std.Deflate(blur_ret_diff) blur_ret_ifl = iterate(blur_ret_dfl, core.std.Inflate, 4) blur_ret_brz = core.std.Binarize(blur_ret_ifl, brz[0]) # blur_ret_brz = core.morpho.Close(blur_ret_brz, size=8) # No longer in R55 kirsch_mask = kirsch(clip_y).std.Binarize(brz[1]) kirsch_ifl = kirsch_mask.std.Deflate().std.Inflate() kirsch_brz = core.std.Binarize(kirsch_ifl, brz[1]) # kirsch_brz = core.morpho.Close(kirsch_brz, size=4) # No longer in R55 merged = core.std.Expr([blur_ret_brz, kirsch_brz], "x y +") rm_grain = core.rgvs.RemoveGrain(merged, rg_mode) return rm_grain if bits == 16 else depth(rm_grain, bits)
def kirsch_aa_mask(clip: vs.VideoNode, mthr: float = 0.25) -> vs.VideoNode: """ Kirsh-based AA mask. Dependencies: * kagefunc :param clip: Input clip :param mthr: Mask threshold, scaled to clip range if between 0 and 1 (inclusive). (Default: 0.25) """ try: from kagefunc import kirsch except ModuleNotFoundError: raise ModuleNotFoundError( "kirsch_aa_mask: missing dependency 'kagefunc'") return kirsch(get_y(clip)).std.Binarize(scale_thresh( mthr, clip)).std.Maximum().std.Convolution([1] * 9)
def RainbowSmooth(clip, radius=3, lthresh=0, hthresh=220, mask="original"): core = vs.core if isinstance(mask, str): if mask == "original": mask = core.std.Expr(clips=[clip.std.Maximum(planes=0), clip.std.Minimum(planes=0)], expr=["x y - 90 > 255 x y - 255 90 / * ?", "", ""]) elif mask == "prewitt": mask = core.std.Prewitt(clip=clip, planes=0) elif mask == "sobel": mask = core.std.Sobel(clip=clip, planes=0) elif mask == "tcanny": mask = core.tcanny.TCanny(clip) elif mask == "fast_sobel": import kagefunc as kage mask = kage.fast_sobel(clip) elif mask == "kirsch": import kagefunc as kage mask = kage.kirsch(clip) elif mask == "retinex_edgemask": import kagefunc as kage mask = kage.retinex_edgemask(clip) lderain = clip if lthresh > 0: lderain = clip.smoothuv.SmoothUV(radius=radius, threshold=lthresh, interlaced=False) hderain = clip.smoothuv.SmoothUV(radius=radius, threshold=hthresh, interlaced=False) if hthresh > lthresh: return core.std.MaskedMerge(clipa=lderain, clipb=hderain, mask=mask, planes=[1, 2], first_plane=True) else: return lderain
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 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)