def detail_mask( clip: vs.VideoNode, sigma: float = 1.0, detail_brz: int = 2500, lines_brz: int = 4500, blur_func: Callable[ [vs.VideoNode, vs.VideoNode, float], vs.VideoNode] = core.bilateral.Bilateral, # type: ignore edgemask_func: Callable[[vs.VideoNode], vs.VideoNode] = core.std.Prewitt, rg_mode: int = 17) -> vs.VideoNode: """ A detail mask aimed at preserving as much detail as possible within darker areas, even if it winds up being mostly noise. Currently still in the beta stage. Please report any problems or feedback in the IEW Discord (link in the README). :param clip: Input clip :param sigma: Sigma for the detail mask. Higher means more detail and noise will be caught. :param detail_brz: Binarizing for the detail mask. Default values assume a 16bit clip, so you may need to adjust it yourself. Will not binarize if set to 0. :param lines_brz: Binarizing for the prewitt mask. Default values assume a 16bit clip, so you may need to adjust it yourself. Will not binarize if set to 0. :param blur_func: Blurring function used for the detail detection. Must accept the following parameters: ``clip``, ``ref_clip``, ``sigma``. :param edgemask_func: Edgemasking function used for the edge detection :param rg_mode: Removegrain mode performed on the final output """ import lvsfunc as lvf from vsutil import get_y, iterate if clip.format is None: raise ValueError("detail_mask: 'Variable-format clips not supported'") clip_y = get_y(clip) blur_pf = core.bilateral.Gaussian(clip_y, sigma=sigma / 4 * 3) blur_pref = blur_func(clip_y, blur_pf, sigma) blur_pref_diff = core.std.Expr([blur_pref, clip_y], "x y -").std.Deflate() blur_pref = iterate(blur_pref_diff, core.std.Inflate, 4) prew_mask = edgemask_func(clip_y).std.Deflate().std.Inflate() if detail_brz > 0: blur_pref = blur_pref.std.Binarize(detail_brz) if lines_brz > 0: prew_mask = prew_mask.std.Binarize(lines_brz) merged = core.std.Expr([blur_pref, prew_mask], "x y +") rm_grain = lvf.util.pick_removegrain(merged)(merged, rg_mode) return depth(rm_grain, clip.format.bits_per_sample)
def linemask(clip: vs.VideoNode, strength: int = 200, protection: int = 2, luma_cap: int = 224, threshold: float = 3) -> Tuple[vs.VideoNode, vs.VideoNode]: """ Lineart mask from havsfunc.FastLineDarkenMod, using the very same syntax. Furthermore, it checks the overall planestatsaverage of the frame to determine if it's a super grainy scene or not. """ import math from functools import partial from typing import List from lvsfunc.misc import get_prop from vsutil import depth, get_depth, get_y def _reduce_grain(n: int, f: vs.VideoFrame, clips: List[vs.VideoNode]) -> vs.VideoNode: return clips[1] if get_prop(f, 'PlaneStatsAverage', float) > 0.032 else clips[0] def _cround(x: float) -> float: return math.floor(x + 0.5) if x > 0 else math.ceil(x - 0.5) assert clip.format clip_y = get_y(depth(clip, 8)) bits = clip.format.bits_per_sample peak = (1 << get_depth(clip_y)) - 1 strngth = strength / 128 lum = _cround(luma_cap * peak / 255) if peak != 1 else luma_cap / 255 thr = _cround(threshold * peak / 255) if peak != 1 else threshold / 255 maxed = clip_y.std.Maximum(threshold=peak / (protection + 1)).std.Minimum() dark = core.std.Expr( [clip_y, maxed], expr= f'y {lum} < y {lum} ? x {thr} + > x y {lum} < y {lum} ? - 0 ? {strngth} * x +' ) extr = core.std.Lut2(clip_y, dark, function=lambda x, y: 255 if abs(x - y) else 0) dedot = extr.rgvs.RemoveGrain(6) blur = dedot.std.Convolution(matrix=[1, 2, 1, 2, 0, 2, 1, 2, 1]) degrain = core.std.FrameEval(dedot, partial(_reduce_grain, clips=[dedot, blur]), dedot.std.PlaneStats()) return dark, depth(degrain, bits)
def _fsrcnnx(clip: vs.VideoNode) -> vs.VideoNode: blank = core.std.BlankClip(clip, format=vs.GRAY16, color=128 << 8) clip = join([clip, blank, blank]) # The chroma is upscaled with box AKA nearest but we don't care since we only need the luma. # It's especially faster and speed is the key :^) clip = core.placebo.Shader(clip, 'Shaders/FSRCNNX_x2_56-16-4-1.glsl', clip.width * 2, clip.height * 2, filter='box') return get_y(clip)
def detail_mask(clip: vs.VideoNode, pre_denoise: Optional[float] = None, rad: int = 3, radc: int = 2, brz_a: float = 0.005, brz_b: float = 0.005) -> vs.VideoNode: """ A wrapper for creating a detail mask to be used during denoising and/or debanding. The detail mask is created using debandshit's rangemask, and is then merged with Prewitt to catch lines it may have missed. Dependencies: knlmeans (optional: pre_denoise), debandshit :param clip: Input clip :param pre_denoise: Denoise the clip before creating the mask (Default: False) :brz_a: Binarizing for the detail mask (Default: 0.05) :brz_b: Binarizing for the edge mask (Default: 0.05) :return: Detail mask """ try: from debandshit import rangemask except ModuleNotFoundError: raise ModuleNotFoundError( "detail_mask: missing dependency 'debandshit'") if pre_denoise is not None: clip = core.knlm.KNLMeansCL(clip, d=2, a=3, h=pre_denoise) mask_a = util.resampler( get_y(clip), 16) if clip.format.bits_per_sample < 32 else get_y(clip) mask_a = rangemask(mask_a, rad=rad, radc=radc) mask_a = util.resampler(mask_a, clip.format.bits_per_sample) mask_a = core.std.Binarize(mask_a, brz_a) mask_b = core.std.Prewitt(get_y(clip)) mask_b = core.std.Binarize(mask_b, brz_b) mask = core.std.Expr([mask_a, mask_b], 'x y max') mask = util.pick_removegrain(mask)(mask, 22) return util.pick_removegrain(mask)(mask, 11)
def transpose_aa(clip: vs.VideoNode, eedi3: bool = False, rep: int = 13) -> vs.VideoNode: """ Function that performs anti-aliasing over a clip by using nnedi3/eedi3 and transposing multiple times. This results in overall stronger anti-aliasing. Useful for shows like Yuru Camp with bad lineart problems. Original function written by Zastin, modified by LightArrowsEXE. Dependencies: rgsf (optional: 32 bit clip), vapoursynth-eedi3, vapoursynth-nnedi3, znedi3 :param clip: Input clip :param eedi3: Use eedi3 for the interpolation (Default: False) :param rep: Repair mode. Pass it 0 to not repair (Default: 13) :return: Antialiased clip """ if clip.format is None: raise ValueError("transpose_aa: 'Variable-format clips not supported'") clip_y = get_y(clip) if eedi3: def _aa(clip_y: vs.VideoNode) -> vs.VideoNode: clip_y = clip_y.std.Transpose() clip_y = clip_y.eedi3m.EEDI3(0, 1, 0, 0.5, 0.2) clip_y = clip_y.znedi3.nnedi3(1, 0, 0, 3, 4, 2) clip_y = clip_y.resize.Spline36(clip.height, clip.width, src_top=.5) clip_y = clip_y.std.Transpose() clip_y = clip_y.eedi3m.EEDI3(0, 1, 0, 0.5, 0.2) clip_y = clip_y.znedi3.nnedi3(1, 0, 0, 3, 4, 2) return clip_y.resize.Spline36(clip.width, clip.height, src_top=.5) else: def _aa(clip_y: vs.VideoNode) -> vs.VideoNode: clip_y = clip_y.std.Transpose() clip_y = clip_y.nnedi3.nnedi3(0, 1, 0, 3, 3, 2) clip_y = clip_y.nnedi3.nnedi3(1, 0, 0, 3, 3, 2) clip_y = clip_y.resize.Spline36(clip.height, clip.width, src_top=.5) clip_y = clip_y.std.Transpose() clip_y = clip_y.nnedi3.nnedi3(0, 1, 0, 3, 3, 2) clip_y = clip_y.nnedi3.nnedi3(1, 0, 0, 3, 3, 2) return clip_y.resize.Spline36(clip.width, clip.height, src_top=.5) def _csharp(flt: vs.VideoNode, clip: vs.VideoNode) -> vs.VideoNode: blur = core.std.Convolution(flt, [1] * 9) return core.std.Expr([flt, clip, blur], 'x y < x x + z - x max y min x x + z - x min y max ?') aaclip = _aa(clip_y) aaclip = _csharp(aaclip, clip_y) aaclip = util.pick_repair(clip_y)(aaclip, clip_y, rep) return aaclip if clip.format.color_family is vs.GRAY else core.std.ShufflePlanes([aaclip, clip], [0, 1, 2], vs.YUV)
def do_filter(): """Vapoursynth filtering""" src = JPBD.src_cut src = depth(src, 32) ed = (30089, 32247) denoise = kgf.hybriddenoise(src, 0.45, 2) out = denoise h = 720 w = get_w(h) b, c = vdf.get_bicubic_params('mitchell') luma = get_y(out) line_mask = shf.edge_mask_simple(luma, 'FDOG', 0.08, (1, 1)) descale = core.descale.Debicubic(luma, w, h, b, c) upscale = shf.fsrcnnx_upscale(descale, src.height, 'shaders/FSRCNNX_x2_56-16-4-1.glsl', partial(SSIM_downsample, kernel='Bicubic')) rescale = core.std.MaskedMerge(luma, upscale, line_mask) merged = vdf.merge_chroma(rescale, denoise) out = depth(merged, 16) mask = shf.detail_mask(out, (10000, 4000), (12000, 3500), [(2, 2), (2, 2)], sigma=[50, 250, 400], upper_thr=0.005) deband = dbs.f3kpf(out, 17, 42, 48, thrc=0.4) deband = core.std.MaskedMerge(deband, out, mask) deband_b = placebo.deband(out, 27, 8, 3, 0) deband = lvf.rfs(deband, deband_b, [(3404, 3450)]) deband_c = shf.deband_stonks(out, 20, 8, 3, shf.edge_mask_simple(out, 'prewitt', 2500, (8, 1))) deband = lvf.rfs(deband, deband_c, [(5642, 5784), (6222, 6479), (7798, 8073), (8133, 8256), (9699, 9817)]) deband_d = placebo.deband(out, 17, 7.5, 1, 0) deband_d = core.std.MaskedMerge(deband_d, out, mask) deband = lvf.rfs(deband, deband_d, [(8074, 8132), (8711, 8766), (12267, 12433), (28468, 28507)]) grain = core.neo_f3kdb.Deband(deband, preset='depth', grainy=24, grainc=24) out = grain grain = adptvgrnMod(out, 0.3, size=4/3, sharp=55, luma_scaling=14, grain_chroma=False) out = grain ending = shinyori_ed01.filtering(src, *ed) final = lvf.rfs(out, ending, [ed]) return depth(final, 10)
def filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]: """Regular VapourSynth filterchain""" import havsfunc as haf import lvsfunc as lvf import vardefunc as vdf from adptvgrnMod import adptvgrnMod from muvsfunc import SSIM_downsample from vsutil import depth, get_y, iterate src = pre_freeze() src = depth(src, 16) # TO-DO: Figure out how they post-sharpened it. Probably some form of unsharpening? src_y = depth(get_y(src), 32) descale = lvf.kernels.Bicubic(b=0, c=3 / 4).descale(src_y, 1440, 810) double = vdf.scale.nnedi3cl_double(descale, pscrn=1) rescale = depth(SSIM_downsample(double, 1920, 1080), 16) scaled = vdf.misc.merge_chroma(rescale, src) denoise = core.knlm.KNLMeansCL(scaled, d=1, a=3, s=4, h=0.3, channels='Y') decs = vdf.noise.decsiz(denoise, sigmaS=4, min_in=208 << 8, max_in=232 << 8) dehalo = haf.YAHR(decs, blur=2, depth=28) halo_mask = lvf.mask.halo_mask(decs, rad=3, brz=0.3, thma=0.42) dehalo_masked = core.std.MaskedMerge(decs, dehalo, halo_mask) aa = lvf.aa.nneedi3_clamp(dehalo_masked, strength=1.5) # Some scenes have super strong aliasing that I really don't wanna scenefilter until BDs. Thanks, Silver Link! aa_strong = lvf.sraa(dehalo_masked, rfactor=1.35) aa_spliced = lvf.rfs(aa, aa_strong, [()]) upscale = lvf.kernels.Bicubic(b=0, c=3 / 4).scale(descale, 1920, 1080) credit_mask = lvf.scale.descale_detail_mask(src_y, upscale, threshold=0.08) credit_mask = iterate(credit_mask, core.std.Deflate, 3) credit_mask = iterate(credit_mask, core.std.Inflate, 3) credit_mask = iterate(credit_mask, core.std.Maximum, 2) merge_credits = core.std.MaskedMerge(aa_spliced, src, depth(credit_mask, 16)) deband = flt.masked_f3kdb(merge_credits, rad=18, thr=32, grain=[24, 0]) grain: vs.VideoNode = adptvgrnMod(deband, seed=42069, strength=0.15, luma_scaling=10, size=1.25, sharp=80, static=True, grain_chroma=False) return grain
def mask_nc(clip: vs.VideoNode, src: vs.VideoNode, ncop: FileInfo, nced: FileInfo, OP: Optional[int] = None, ED: Optional[int] = None) -> vs.VideoNode: mask_op = vdf.dcm(src, src[OP:OP + ncop.clip_cut.num_frames], ncop.clip_cut, OP, thr=128, prefilter=True) if OP else get_y(core.std.BlankClip(src)) mask_ed = vdf.dcm(src, src[ED:ED + nced.clip_cut.num_frames], nced.clip_cut, ED, thr=128, prefilter=True) if ED else get_y(core.std.BlankClip(src)) credit_mask = depth(core.std.Expr([mask_op, mask_ed], expr='x y +'), 16) merge = core.std.MaskedMerge(depth(clip, 16), depth(src, 16), credit_mask) return merge
def _vdf_fsrcnnx(clip: vs.VideoNode, width: int, height: int) -> vs.VideoNode: clip = core.std.ShufflePlanes([ vsutil.depth(clip.resize.Point(vsutil.get_w(864), 864), 16), src.resize.Bicubic(vsutil.get_w(864), 864) ], planes=[0, 1, 2], colorfamily=vs.YUV) return vsutil.get_y( vsutil.depth(vdf.fsrcnnx_upscale(clip, width, height, FSRCNNX), 32))
def filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]: """Main filterchain""" import lvsfunc as lvf import rekt import vardefunc as vdf from muvsfunc import SSIM_downsample from vsutil import depth, get_y src = JP_BD.clip_cut rkt = rekt.rektlvls(src, [0, -1], [15, 15], [0, -1], [15, 15]) cloc = depth(rkt, 32).resize.Bicubic(chromaloc_in=1, chromaloc=0) descale = lvf.kernels.Bilinear().descale(get_y(cloc), 1280, 720) supersample = vdf.scale.nnedi3_upscale(descale) 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) scaled = vdf.misc.merge_chroma(downscaled, cloc) scaled = depth(scaled, 16) dft = core.dfttest.DFTTest(scaled, sigma=0.6, sbsize=8, sosize=6, tbsize=3, tosize=1) decs = vdf.noise.decsiz(dft, sigmaS=4, min_in=200 << 8, max_in=232 << 8) baa = lvf.aa.based_aa(decs, str(shader_file)) sraa = lvf.sraa(decs, rfactor=1.5) clmp = lvf.aa.clamp_aa(decs, baa, sraa, strength=1.5) deband = flt.masked_f3kdb(clmp, rad=16, thr=[20, 16]) grain = vdf.noise.Graigasm(thrs=[x << 8 for x in (32, 80, 128, 176)], strengths=[(0.25, 0.0), (0.20, 0.0), (0.15, 0.0), (0.0, 0.0)], sizes=(1.20, 1.15, 1.10, 1), sharps=(80, 70, 60, 50), grainers=[ vdf.noise.AddGrain(seed=69420, constant=False), vdf.noise.AddGrain(seed=69420, constant=False), vdf.noise.AddGrain(seed=69420, constant=True) ]).graining(deband) return grain
def _nneedi3_clamp(clip: vs.VideoNode, strength: int = 1): bits = clip.format.bits_per_sample - 8 thr = strength * (1 >> bits) luma = get_y(clip) strong = TAAmbk(luma, aatype='Eedi3', alpha=0.4, beta=0.4) weak = TAAmbk(luma, aatype='Nnedi3') expr = 'x z - y z - * 0 < y x y {0} + min y {0} - max ?'.format(thr) clip_aa = core.std.Expr([strong, weak, luma], expr) return core.std.ShufflePlanes([clip_aa, clip], [0, 1, 2], vs.YUV)
def do_filter(): """Vapoursynth filtering""" def _nnedi3(clip: vs.VideoNode, factor: float, args: dict)-> vs.VideoNode: upscale = clip.std.Transpose().nnedi3.nnedi3(0, True, **args) \ .std.Transpose().nnedi3.nnedi3(0, True, **args) sraa = _sraa(upscale, dict(nsize=3, nns=3, qual=1, pscrn=1)) return core.resize.Bicubic(sraa, clip.width*factor, clip.height*factor, src_top=.5, src_left=.5, filter_param_a=0, filter_param_b=0.25) # My GPU’s dying on it def _sraa(clip: vs.VideoNode, nnargs: dict)-> vs.VideoNode: clip = clip.nnedi3cl.NNEDI3CL(0, False, **nnargs).std.Transpose() clip = clip.nnedi3cl.NNEDI3CL(0, False, **nnargs).std.Transpose() return clip # Fun part / Cutting src_funi, src_wkn = [depth(c, 16) for c in CLIPS_SRC] src_funi, src_wkn = src_funi[744:], src_wkn[0] + src_wkn # Dehardsubbing # comp = lvf.comparison.stack_compare(src_funi, src_wkn, make_diff=True) src = core.std.MaskedMerge(src_wkn, src_funi, kgf.hardsubmask(src_wkn, src_funi)) hardsub_rem = core.std.MaskedMerge(src, src_funi, kgf.hardsubmask_fades(src, src_funi, 8, 2000)) src = lvf.rfs(src, hardsub_rem, [(3917, 4024)]) # Regular filterchain op, ed = (17238, 19468), (31889, 34045) h = 846 w = get_w(h) b, c = vdf.get_bicubic_params('robidoux') luma = get_y(src) thr = 8000 line_mask = gf.EdgeDetect(luma, 'FDOG').std.Median().std.Expr(f'x {thr} < x x 3 * ?') descale = core.descale.Debicubic(depth(luma, 32), w, h, b, c) rescaled = _nnedi3(depth(descale, 16), src.height/h, dict(nsize=0, nns=4, qual=2, pscrn=2)) rescaled = core.std.MaskedMerge(luma, rescaled, line_mask) merged = vdf.merge_chroma(rescaled, src) out = merged cred_m = vdf.drm(src, h, b=b, c=c, mthr=80, mode='ellipse') credit = lvf.rfs(out, core.std.MaskedMerge(out, src, cred_m), [op]) credit = lvf.rfs(credit, src, [ed]) out = credit deband = dbs.f3kpf(out, 17, 36, 36) deband = core.std.MaskedMerge(deband, out, line_mask) grain = core.grain.Add(deband, 0.25) return depth(grain, 10)
def filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]: """Main VapourSynth filterchain""" import EoEfunc as eoe import lvsfunc as lvf import vardefunc as vdf from ccd import ccd from finedehalo import fine_dehalo from vsutil import depth, get_y src, ep = JP_BD.clip_cut, JP_EP.clip_cut src = lvf.rfs(src, ep, [(811, 859)]) src = depth(src, 16) # This noise can burn in hell. denoise_pre = get_y(src.dfttest.DFTTest(sigma=1.0)) denoise_ret = core.retinex.MSRCP(denoise_pre, sigma=[50, 200, 350], upper_thr=0.005) denoise_mask = flt.detail_mask(denoise_ret, sigma=2.0, lines_brz=3000).rgvs.RemoveGrain(4) denoise_pre = core.dfttest.DFTTest(src, sigma=4.0) denoise_smd = eoe.dn.CMDegrain(src, tr=5, thSAD=275, freq_merge=True, prefilter=denoise_pre) denoise_masked = core.std.MaskedMerge(denoise_smd, src, denoise_mask) denoise_uv = ccd(denoise_masked, threshold=6) decs = vdf.noise.decsiz(denoise_uv, sigmaS=8, min_in=200 << 8, max_in=235 << 8) # F**K THIS SHOW'S LINEART HOLY SHIT baa = lvf.aa.based_aa(decs.std.Transpose(), str(shader_file), gamma=120) baa = lvf.aa.based_aa(baa.std.Transpose(), str(shader_file), gamma=120) sraa = lvf.sraa(decs, rfactor=1.4) clmp_aa = lvf.aa.clamp_aa(decs, baa, sraa, strength=1.65) # AAing introduces some haloing (zzzzzz) restr_edges = fine_dehalo(clmp_aa, decs) restr_dark = core.std.Expr([clmp_aa, restr_edges], "x y min") deband = core.average.Mean([ flt.masked_f3kdb(restr_dark, rad=17, thr=[28, 20], grain=[12, 8]), flt.masked_f3kdb(restr_dark, rad=21, thr=[36, 32], grain=[24, 12]), flt.masked_placebo(restr_dark, rad=6.5, thr=2.5, itr=2, grain=4) ]) grain = vdf.noise.Graigasm( thrs=[x << 8 for x in (32, 80, 128, 176)], strengths=[(0.25, 0.0), (0.20, 0.0), (0.15, 0.0), (0.0, 0.0)], sizes=(1.20, 1.15, 1.10, 1), sharps=(80, 70, 60, 50), grainers=[ vdf.noise.AddGrain(seed=69420, constant=True), vdf.noise.AddGrain(seed=69420, constant=False), vdf.noise.AddGrain(seed=69420, constant=False) ]).graining(deband) return grain
def _perform_motion_mask(clip: vs.VideoNode, brz: int)-> vs.VideoNode: clip = depth(clip, 8) sup = core.hqdn3d.Hqdn3d(clip).neo_fft3d.FFT3D().mv.Super(sharp=1) fv1 = core.mv.Analyse(sup, isb=False, delta=1, truemotion=False, dct=2) fv2 = core.mv.Analyse(sup, isb=True, delta=1, truemotion=True, dct=2) momask1 = core.mv.Mask(clip, fv1, ml=2, kind=1) momask2 = core.mv.Mask(clip, fv2, ml=3, kind=1) momask = core.std.Merge(momask1, momask2).rgvs.RemoveGrain(3).std.Binarize(brz) momask = momask.std.Minimum().std.Minimum() return depth(get_y(momask), 16)
def _nnedi3_supersample(clip: vs.VideoNode, width: int, height: int, opencl: bool = True) -> vs.VideoNode: nnargs: Dict[str, Any] = dict(field=0, dh=True, nsize=0, nns=4, qual=2) _nnedi3 = nnedi3(opencl=opencl, **nnargs) up_y = _nnedi3(get_y(clip)) up_y = up_y.resize.Spline36(height=height, src_top=0.5).std.Transpose() up_y = _nnedi3(up_y) up_y = up_y.resize.Spline36(height=width, src_top=0.5) return up_y
def do_filter(): """Vapoursynth filtering""" src = JPBD.src_cut src = depth(src, 16) out = src + src[-1] denoise = mvf.BM3D(out, [2.5, 1.5], radius1=1) diff = core.std.MakeDiff(out, denoise) out = denoise luma = get_y(out) dehalo = gf.MaskedDHA(luma, rx=2.5, ry=2.5, darkstr=0.15, brightstr=1.2, maskpull=48, maskpush=140) out = dehalo dering = gf.HQDeringmod(out, sharp=3, drrep=24, thr=24, darkthr=0) out = dering antialias_mask = gf.EdgeDetect(out, 'FDOG') antialias = lvf.sraa(out, 1.5, 13, gamma=100, downscaler=core.resize.Spline64) out = core.std.MaskedMerge(out, antialias, antialias_mask) out = vdf.merge_chroma(out, denoise) warp = xvs.WarpFixChromaBlend(out, 64, depth=8) out = warp deband_mask = lvf.denoise.detail_mask(out, brz_a=2250, brz_b=1500).std.Median() deband = dbs.f3kpf(out, 17, 36, 36) deband = core.std.MaskedMerge(deband, out, deband_mask) out = deband grain_original = core.std.MergeDiff(out, diff) grain_new = core.grain.Add(out, 0.15, 0, constant=True) grain_mask = core.adg.Mask(out.std.PlaneStats(), 30).std.Expr(f'x x {96<<8} - 0.25 * +') grain = core.std.MaskedMerge(grain_new, grain_original, grain_mask) out = grain ending = lvf.rfs(out, src, [(31241, src.num_frames - 1)]) out = ending return depth(out, 10)
def descale_detail_mask(clip: vs.VideoNode, rescaled_clip: vs.VideoNode, threshold: float = 0.05) -> vs.VideoNode: """ Generate a detail mask given a clip and a clip rescaled with the same kernel. Function is curried to allow parameter tuning when passing to :py:func:`lvsfunc.scale.descale` :param clip: Original clip :param rescaled_clip: Clip downscaled and reupscaled using the same kernel :param threshold: Binarization threshold for mask (Default: 0.05) :return: Mask of lost detail """ check_variable(clip, "descale_detail_mask") check_variable(rescaled_clip, "descale_detail_mask") mask = core.std.Expr([get_y(clip), get_y(rescaled_clip)], 'x y - abs') \ .std.Binarize(threshold) mask = iterate(mask, core.std.Maximum, 4) return iterate(mask, core.std.Inflate, 2)
def do_filter(): """Vapoursynth filtering""" def _fsrcnnx(clip: vs.VideoNode) -> vs.VideoNode: blank = core.std.BlankClip(clip, format=vs.GRAY16, color=128 << 8) clip = join([clip, blank, blank]) # The chroma is upscaled with box AKA nearest but we don't care since we only need the luma. # It's especially faster and speed is the key :^) clip = core.placebo.Shader(clip, 'Shaders/FSRCNNX_x2_56-16-4-1.glsl', clip.width*2, clip.height*2, filter='box') return get_y(clip) def _nnedi(clip: vs.VideoNode) -> vs.VideoNode: args = dict(nsize=4, nns=4, qual=2, pscrn=2) clip = clip.std.Transpose().nnedi3.nnedi3(0, True, **args) \ .std.Transpose().nnedi3.nnedi3(0, True, **args) return core.resize.Spline36(clip, src_top=.5, src_left=.5) def _rescale(clip: vs.VideoNode, thr: int, downscaler: Callable[[vs.VideoNode], vs.VideoNode]) -> vs.VideoNode: return downscaler(core.std.Merge(_nnedi(clip), _fsrcnnx(clip), thr)) src = SRC_CUT fixedges = lvf.ef(src, [1, 1, 1]) denoise = CoolDegrain(depth(fixedges, 16), tr=1, thsad=48, thsadc=84, blksize=8, overlap=4, plane=4) w, h = 1280, 720 b, c = vdf.get_bicubic_params('robidoux') luma = get_y(denoise) descale = depth(core.descale.Debicubic(depth(luma, 32), w, h, b, c), 16) rescale = _rescale(descale, 0.55, lambda c: core.resize.Bicubic(c, src.width, src.height, filter_param_a=0, filter_param_b=0)) line_mask = lvf.denoise.detail_mask(rescale, brz_a=7000, brz_b=2000) rescale = core.std.MaskedMerge(luma, rescale, line_mask) credit_mask = vdf.drm(luma, b=b, c=c, mode='ellipse', sw=3, sh=3) credit = core.std.MaskedMerge(rescale, luma, credit_mask) merged = vdf.merge_chroma(credit, denoise) deband_mask = lvf.denoise.detail_mask(merged, brz_a=3000, brz_b=1500) deband = dbs.f3kpf(merged, 17, 36, 36) deband = core.std.MaskedMerge(deband, merged, deband_mask) grain = core.neo_f3kdb.Deband(deband, preset='depth', grainy=32, output_depth=10, dither_algo=3, keep_tv_range=True) return grain
def _eedi3_singlerate(clip: vs.VideoNode) -> vs.VideoNode: check_variable(clip, "_eedi3_singlerate") eeargs: Dict[str, Any] = dict(field=0, dh=False, alpha=0.2, beta=0.6, gamma=40, nrad=2, mdis=20) nnargs: Dict[str, Any] = dict(field=0, dh=False, nsize=0, nns=4, qual=2) y = get_y(clip) return eedi3(sclip=nnedi3(**nnargs)(y), **eeargs)(y)
def _retinex_edgemask(src: vs.VideoNode, sigma: int = 1) -> vs.VideoNode: """ Use retinex to greatly improve the accuracy of the edge detection in dark scenes. sigma is the sigma of tcanny From kagefunc.py, moved here so I don't need to import it. """ luma = get_y(src) max_value = 1 if src.format.sample_type == vs.FLOAT else ( 1 << get_depth(src)) - 1 ret = core.retinex.MSRCP(luma, sigma=[50, 200, 350], upper_thr=0.005) tcanny = ret.tcanny.TCanny( mode=1, sigma=sigma).std.Minimum(coordinates=[1, 0, 1, 0, 0, 1, 0, 1]) return core.std.Expr([_kirsch(luma), tcanny], f"x y + {max_value} min")
def test_descale(clip: vs.VideoNode, width: Optional[int] = None, height: int = 720, kernel: kernels.Kernel = kernels.Bicubic(b=0, c=1 / 2), show_error: bool = True) -> Tuple[vs.VideoNode, ScaleAttempt]: """ Generic function to test descales with; descales and reupscales a given clip, allowing you to compare the two easily. Also returns a :py:class:`lvsfunc.scale.ScaleAttempt` with additional information. When comparing, it is recommended to do atleast a 4x zoom using Nearest Neighbor. I also suggest using 'compare' (:py:func:`lvsfunc.comparison.compare`), as that will make comparing the output with the source clip a lot easier. Some of this code was leveraged from DescaleAA found in fvsfunc. Dependencies: * vapoursynth-descale :param clip: Input clip :param width: Target descale width. If None, determine from `height` :param height: Target descale height (Default: 720) :param kernel: Kernel used to descale (see :py:class:`lvsfunc.kernels.Kernel`, Default: kernels.Bicubic(b=0, c=1/2)) :param show_error: Render PlaneStatsDiff on the reupscaled frame (Default: True) :return: A tuple containing a clip re-upscaled with the same kernel and a ScaleAttempt tuple. """ if clip.format is None: raise ValueError("test_descale: 'Variable-format clips not supported'") width = width or get_w(height, clip.width / clip.height) clip_y = depth(get_y(clip), 32) descale = _perform_descale(Resolution(width, height), clip_y, kernel) rescaled = depth(core.std.PlaneStats(descale.rescaled, clip_y), get_depth(clip)) if clip.format.num_planes == 1: rescaled = core.text.FrameProps( rescaled, "PlaneStatsDiff") if show_error else rescaled else: merge = core.std.ShufflePlanes([rescaled, clip], [0, 1, 2], vs.YUV) rescaled = core.text.FrameProps( merge, "PlaneStatsDiff") if show_error else merge return rescaled, descale
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])
def _aa_extra(clip: vs.VideoNode) -> vs.VideoNode: downscaler_fun = lambda x, wid, hei: \ core.fmtc.resample(x, wid, hei, kernel='gauss', invks=True, invkstaps=2, taps=1, a1=32) luma = get_y(clip) antiaa = lvf.sraa(luma, 2, height=720) antiaa = TAAmbk(antiaa, 'Eedi3SangNom', mtype=2, alpha=0.75, beta=0.2, gamma=40, aa=96) antiaa = lvf.sraa(antiaa, 2, height=clip.height, downscaler=downscaler_fun) return vdf.merge_chroma(antiaa, clip)
def descale720(clip: vs.VideoNode, src: vs.VideoNode, ranges: List[Range] = []) -> vs.VideoNode: y = ldescale(vsutil.get_y(src), upscaler=_sraa_reupscale, height=720, kernel=Bicubic(b=1 / 3, c=1 / 3), threshold=0, mask=_inverse_mask) scaled = core.std.ShufflePlanes([y, src], planes=[0, 1, 2], colorfamily=vs.YUV) scaled = replace_ranges(clip, scaled, ranges) return scaled
def fineline_mask(clip: vs.VideoNode, thresh: int = 95) -> vs.VideoNode: """ Generates a very fine mask for lineart protection. Not perfect yet The generated mask is GRAY8, keep this in mind for conversions. :param clip: The clip to generate the mask for. :param thresh: The threshold for the binarization step. """ prew = core.std.Prewitt(clip, planes=[0]) thin = core.std.Minimum(prew) yp, yt = get_y(prew), get_y(thin) maska = core.std.Expr([yp, yt], ["x y < y x ?"]) bin_mask = core.std.Binarize(maska, threshold=thresh) bin_mask = depth(bin_mask, 8) redo = int(floor(thresh / 2.5) * 2) return core.std.Expr([bin_mask, maska], [f"x y < y x ? {redo} < 0 255 ?"])
def transpose_aa(clip: vs.VideoNode, eedi3: bool = False, rep: int = 13) -> vs.VideoNode: """ Function that performs anti-aliasing over a clip by using nnedi3/eedi3 and transposing multiple times. This results in overall stronger anti-aliasing. Useful for shows like Yuru Camp with bad lineart problems. Original function written by Zastin, modified by LightArrowsEXE. Dependencies: * RGSF (optional: 32 bit clip) * vapoursynth-EEDI3 * vapoursynth-nnedi3 * znedi3 :param clip: Input clip :param eedi3: Use eedi3 for the interpolation (Default: False) :param rep: Repair mode. Pass it 0 to not repair (Default: 13) :return: Antialiased clip """ check_variable(clip, "transpose_aa") assert clip.format clip_y = get_y(clip) def _aafun(clip: vs.VideoNode) -> vs.VideoNode: return clip.eedi3m.EEDI3(0, 1, 0, 0.5, 0.2).znedi3.nnedi3(1, 0, 0, 3, 4, 2) if eedi3 \ else clip.nnedi3.nnedi3(0, 1, 0, 3, 3, 2).nnedi3.nnedi3(1, 0, 0, 3, 3, 2) def _taa(clip: vs.VideoNode) -> vs.VideoNode: aa = _aafun(clip.std.Transpose()) aa = Catrom().scale(aa, clip.height, clip.width, shift=(0.5, 0)) aa = _aafun(aa.std.Transpose()) return Catrom().scale(aa, clip.width, clip.height, shift=(0.5, 0)) def _csharp(flt: vs.VideoNode, clip: vs.VideoNode) -> vs.VideoNode: blur = core.std.Convolution(flt, [1] * 9) return core.std.Expr( [flt, clip, blur], 'x y < x x + z - x max y min x x + z - x min y max ?') aaclip = _taa(clip_y) aaclip = _csharp(aaclip, clip_y) aaclip = pick_repair(clip_y)(aaclip, clip_y, rep) return aaclip if clip.format.color_family is vs.GRAY else core.std.ShufflePlanes( [aaclip, clip], [0, 1, 2], vs.YUV)
def do_filter(): """Vapoursynth filtering""" src = JPBD.src_cut src = depth(src, 16) out = src ref = hvf.SMDegrain(out, thSAD=300) denoise = mvf.BM3D(out, [1.5, 1.25], radius1=1, ref=ref) out = denoise y = get_y(out) lineart = gf.EdgeDetect(y, 'scharr').morpho.Dilate(2, 2).std.Inflate() fkrescale = fake_rescale(y, 882, 0, 1, deringer=lambda x: gf.MaskedDHA(x, rx=1.85, ry=1.85, darkstr=0.25, brightstr=1.0, maskpull=100, maskpush=200), antialiser=lambda c: lvf.sraa( c, 2, 13, downscaler=core.resize.Bicubic)) merged = core.std.MaskedMerge(y, fkrescale, lineart) out = vdf.merge_chroma(merged, out) dering = hvf.EdgeCleaner(out, 17, smode=1, hot=True) out = dering detail_mask = lvf.mask.detail_mask(out, brz_a=2250, brz_b=1000) deband = vdf.dumb3kdb(out, 15, threshold=17, grain=(24, 0)) deband = core.std.MaskedMerge(deband, out, detail_mask) out = deband grain = adptvgrnMod(out, 0.3, static=True, grain_chroma=False, hi=[128, 240], seed=333) out = grain decz = vdf.decsiz(out, min_in=128 << 8, max_in=200 << 8) out = decz return depth(out, 10).std.Limiter(16 << 2, [235 << 2, 240 << 2], [0, 1, 2])
def filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]: """Main filterchain""" import havsfunc as haf import lvsfunc as lvf import rekt import vardefunc as vdf from adptvgrnMod import adptvgrnMod from ccd import ccd from vsutil import depth, get_w, get_y src = JP_BD.clip_cut rkt = rekt.rektlvls(src, [0, 1079], [7, 7], [0, 1919], [7, 7], prot_val=None) rkt = depth(rkt, 16) denoise_uv = ccd(rkt, threshold=7, matrix='709') stab = haf.GSMC(denoise_uv, radius=1, thSAD=200, planes=[0]) decs = vdf.noise.decsiz(stab, sigmaS=8, min_in=208 << 8, max_in=232 << 8) l_mask = vdf.mask.FDOG().get_mask(get_y(decs), lthr=0.065, hthr=0.065).std.Maximum().std.Minimum() l_mask = l_mask.std.Median().std.Convolution([1] * 9) aa_weak = lvf.aa.taa(decs, lvf.aa.nnedi3(opencl=True)) aa_strong = lvf.aa.upscaled_sraa(decs, downscaler=lvf.kernels.Bicubic(b=-1 / 2, c=1 / 4).scale) aa_clamp = lvf.aa.clamp_aa(decs, aa_weak, aa_strong, strength=1.5) aa_masked = core.std.MaskedMerge(decs, aa_clamp, l_mask) dehalo = haf.FineDehalo(aa_masked, rx=1.6, ry=1.6, darkstr=0, brightstr=1.25) darken = flt.line_darkening(dehalo, 0.275).warp.AWarpSharp2(depth=2) deband = flt.masked_f3kdb(darken, rad=18, thr=32, grain=[32, 12]) grain: vs.VideoNode = adptvgrnMod(deband, seed=42069, strength=0.35, luma_scaling=8, size=1.05, sharp=80, grain_chroma=False) return grain
def eedi3_singlerate_custom(clip: vs.VideoNode) -> vs.VideoNode: import lvsfunc as lvf from vsutil import get_y eeargs: Dict[str, Any] = dict(field=0, dh=False, alpha=0.4, beta=0.6, gamma=30, nrad=2, mdis=30) nnargs: Dict[str, Any] = dict(field=0, dh=False, nsize=0, nns=4, qual=2) y = get_y(clip) return lvf.aa.eedi3(sclip=lvf.aa.nnedi3(**nnargs)(y), **eeargs)(y)
def morpho_mask(clip: vs.VideoNode): # y, u, v = split(clip) y = get_y(clip) # stats = y.std.PlaneStats() # agm3 = core.adg.Mask(stats, 3) # agm6 = core.adg.Mask(stats, 6) ymax = maxm(y, sw=40, mode='ellipse') ymin = minm(y, sw=40, mode='ellipse') # umax = maxm(u, sw=16, mode='ellipse') # umin = minm(u, sw=16, mode='ellipse') # vmax = maxm(v, sw=12, mode='ellipse') # vmin = minm(v, sw=12, mode='ellipse') thr = 3.2 * 256 ypw0 = y.std.Prewitt() ypw = ypw0.std.Binarize(thr).rgvs.RemoveGrain(11) rad = 3 thr = 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 = 16 thr = 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) rad = 30 thr = 1 * 256 ymph = core.std.Expr([ 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(384) 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()], '65535 y - x min z -').std.BoxBlur(0,16,1,16,1) return grad_mask, yrangebig