def rescaler(clip: vs.VideoNode, height: int) -> vs.VideoNode: """ Basic rescaling function using nnedi3. """ from lvsfunc.kernels import Bicubic, BicubicSharp from vardefunc.mask import FDOG from vardefunc.scale import nnedi3_upscale from vsutil import Range, depth, get_w, get_y clip = depth(clip, 32) clip_y = get_y(clip) descale = BicubicSharp().descale(clip_y, get_w(height, clip.width / clip.height), height) rescale = Bicubic(b=-1 / 2, c=1 / 4).scale(nnedi3_upscale(descale, pscrn=1), clip.width, clip.height) l_mask = FDOG().get_mask(clip_y, lthr=0.065, hthr=0.065).std.Maximum().std.Minimum() l_mask = l_mask.std.Median().std.Convolution([1] * 9) # stolen from varde xd masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask) scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)]) return depth(scaled, 16)
def rescaler(clip: vs.VideoNode, height: int) -> Tuple[vs.VideoNode, vs.VideoNode]: """ Basic rescaling and mask generating function using nnedi3. """ from lvsfunc.kernels import Bicubic from lvsfunc.scale import descale_detail_mask from vardefunc.mask import FDOG from vardefunc.scale import nnedi3_upscale from vsutil import Range, get_w, join, plane bits, clip = _get_bits(clip, expected_depth=32) clip_y = get_y(clip) descale = Bicubic().descale(clip_y, get_w(height, clip.width / clip.height), height) rescale = Bicubic().scale(nnedi3_upscale(descale, pscrn=1), clip.width, clip.height) l_mask = FDOG().get_mask(clip_y, lthr=0.065, hthr=0.065).std.Maximum().std.Minimum() l_mask = l_mask.std.Median().std.Convolution([1] * 9) # stolen from varde xd masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask) scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)]) upscale = Bicubic().scale(descale, 1920, 1080) detail_mask = descale_detail_mask(clip_y, upscale, threshold=0.055) scaled_down = scaled if bits == 32 else depth(scaled, bits) mask_down = detail_mask if bits == 32 else depth( detail_mask, 16, range_in=Range.FULL, range=Range.LIMITED) return scaled_down, mask_down
def debander(clip: vs.VideoNode, luma_grain: float = 4.0, **kwargs: Any) -> vs.VideoNode: """ A quick 'n dirty generic debanding function. To be more specific, it would appear that it's faster to deband every plane separately (don't ask me why). To abuse this, we split up the clip into planes beforehand, and then join them back together again at the end. Although the vast, vast majority of video will be YUV, a sanity check for plane amount is done as well, just to be safe. :param clip: Input clip :param luma_grain: Grain added to the luma plane :param kwargs: Additional parameters passed to placebo.Deband :return: Debanded clip """ if clip.format.num_planes == 0: return core.placebo.Deband(clip, grain=luma_grain, **kwargs) return join([ core.placebo.Deband(plane(clip, 0), grain=luma_grain, **kwargs), core.placebo.Deband(plane(clip, 1), grain=0, **kwargs), core.placebo.Deband(plane(clip, 2), grain=0, **kwargs) ])
def placebo_debander(clip: vs.VideoNode, grain: int = 4, **deband_args: Any) -> vs.VideoNode: return join( [ # Still not sure why splitting it up into planes is faster, but hey! core.placebo.Deband(plane(clip, 0), grain=grain, **deband_args), core.placebo.Deband(plane(clip, 1), grain=0, **deband_args), core.placebo.Deband(plane(clip, 2), grain=0, **deband_args) ])
def placebo_debander(clip: vs.VideoNode, grain: float = 4.0, placebo_args: Dict[str, Any] = {}) -> vs.VideoNode: from vsutil import join, plane return join([ core.placebo.Deband(plane(clip, 0), grain=grain, **placebo_args), core.placebo.Deband(plane(clip, 1), grain=0, **placebo_args), core.placebo.Deband(plane(clip, 2), grain=0, **placebo_args) ])
def placebo_debander(clip: vs.VideoNode, grain: float = 4.0, deband_args: Dict[str, Any] = {}) -> vs.VideoNode: placebo_args: Dict[str, Any] = dict() placebo_args |= deband_args return join([ core.placebo.Deband(plane(clip, 0), grain=grain, **placebo_args), core.placebo.Deband(plane(clip, 1), grain=0, **placebo_args), core.placebo.Deband(plane(clip, 2), grain=0, **placebo_args) ])
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 fixedge_a = awf.bbmod(out, 1, 1, 1, 1, 20, blur=700, u=False, v=False) fixedge = out fixedge = lvf.rfs(fixedge, fixedge_a, [(EDSTART + 309, EDEND)]) out = fixedge out = depth(out, 16) dehalo = gf.MaskedDHA(out, rx=1.4, ry=1.4, darkstr=0.02, brightstr=1) dehalo = lvf.rfs(out, dehalo, [(EDEND + 1, src.num_frames - 1)]) out = dehalo resize = core.std.Crop(out, right=12, bottom=8).resize.Bicubic(1920, 1080) resize = lvf.rfs(out, resize, [(27005, 27076)]) out = resize # 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 return depth(out, 10).std.Limiter(16 << 2, [235 << 2, 240 << 2], [0, 1, 2])
def rescaler(clip: vs.VideoNode, height: int, shader_file: Optional[str] = None ) -> Tuple[vs.VideoNode, vs.VideoNode]: """ Basic rescaling function using nnedi3. """ from lvsfunc.kernels import Bicubic, Lanczos from lvsfunc.scale import descale_detail_mask from vardefunc.mask import FDOG from vardefunc.scale import fsrcnnx_upscale, nnedi3_upscale from vsutil import Range, depth, get_w, get_y, iterate, join, plane clip = depth(clip, 32) clip_y = get_y(clip) descale = Lanczos(taps=5).descale(clip_y, get_w(height, clip.width / clip.height), height) if shader_file: rescale = fsrcnnx_upscale(descale, shader_file=shader_file, downscaler=Bicubic(b=-1 / 2, c=1 / 4).scale) else: rescale = Bicubic(b=-1 / 2, c=1 / 4).scale(nnedi3_upscale(descale, pscrn=1), clip.width, clip.height) l_mask = FDOG().get_mask(clip_y, lthr=0.065, hthr=0.065).std.Maximum().std.Minimum() l_mask = l_mask.std.Median().std.Convolution([1] * 9) # stolen from varde xd masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask) scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)]) upscale = Lanczos(taps=5).scale(descale, 1920, 1080) detail_mask = descale_detail_mask(clip_y, upscale, threshold=0.035) detail_mask = iterate(detail_mask, core.std.Inflate, 2) detail_mask = iterate(detail_mask, core.std.Maximum, 2) return depth(scaled, 16), depth(detail_mask, 16, range_in=Range.FULL, range=Range.LIMITED)
def rescaler(clip: vs.VideoNode, height: int, shader_file: Optional[str] = None, **kwargs: Any ) -> Tuple[vs.VideoNode, vs.VideoNode]: """ Multi-descaling + reupscaling function. Compares multiple descales and takes darkest/brightest pixels from clips as necessary """ import lvsfunc as lvf import muvsfunc as muf from vardefunc.mask import FDOG from vardefunc.scale import fsrcnnx_upscale, nnedi3_upscale bits = get_depth(clip) clip = depth(clip, 32) clip_y = get_y(clip) scalers: List[Callable[[vs.VideoNode, int, int], vs.VideoNode]] = [ lvf.kernels.Spline36().descale, lvf.kernels.Catrom().descale, lvf.kernels.BicubicSharp().descale, lvf.kernels.Catrom().scale ] descale_clips = [scaler(clip_y, get_w(height), height) for scaler in scalers] descale_clip = core.std.Expr(descale_clips, 'x y z a min max min y z a max min max z a min max') if shader_file: rescale = fsrcnnx_upscale(descale_clip, shader_file=shader_file, downscaler=None) else: rescale = nnedi3_upscale(descale_clip) rescale = muf.SSIM_downsample(rescale, clip.width, clip.height, smooth=((3 ** 2 - 1) / 12) ** 0.5, sigmoid=True, filter_param_a=0, filter_param_b=0) l_mask = FDOG().get_mask(clip_y, lthr=0.065, hthr=0.065).std.Maximum().std.Minimum() l_mask = l_mask.std.Median().std.Convolution([1] * 9) # stolen from varde xd masked_rescale = core.std.MaskedMerge(clip_y, rescale, l_mask) scaled = join([masked_rescale, plane(clip, 1), plane(clip, 2)]) upscale = lvf.kernels.Spline36().scale(descale_clips[0], clip.width, clip.height) detail_mask = lvf.scale.descale_detail_mask(clip_y, upscale, threshold=0.04) scaled_down = scaled if bits == 32 else depth(scaled, bits) mask_down = detail_mask if bits == 32 else depth(detail_mask, 16, range_in=Range.FULL, range=Range.LIMITED) return scaled_down, mask_down
def fixbrdr(clip, thr=3): import vsutil as vsu y = vsu.plane(clip, 0) diff_expr = "x y - abs" # prepare rows with enough size for convolutions row_1 = y.std.Crop(top=0, bottom=y.height - 1 - 3) # row_1_2 = y.std.Crop(top=0, bottom=y.height - 2) row_2 = y.std.Crop(top=1, bottom=y.height - 2 - 3) # row_2_3 = y.std.Crop(top=1, bottom=y.height - 3) # row_2_3_4 = y.std.Crop(top=2, bottom=y.height - 4) # FillMargins clip fill_mid = row_1.std.Convolution([0, 0, 0, 0, 0, 0, 3, 2, 3]) fill_mid = fill_mid.std.CropAbs(width=y.width, height=1) # no idea what convolutions work best but these are the diagonal fills fill_left = row_1.std.Convolution([0, 0, 0, 0, 0, 0, 5, 3, 1]) fill_left = fill_left.std.CropAbs(width=y.width, height=1) fill_right = row_1.std.Convolution([0, 0, 0, 0, 0, 0, 1, 3, 5]) fill_right = fill_right.std.CropAbs(width=y.width, height=1) # need shifts to see if the row is diagonal or not shift_left = row_2.std.Convolution([0, 0, 0, 1, 0, 0, 0, 0, 0]) shift_left = shift_left.std.CropAbs(width=y.width, height=1) shift_right = row_2.std.Convolution([0, 0, 0, 0, 0, 1, 0, 0, 0]) shift_right = shift_right.std.CropAbs(width=y.width, height=1) # find threshold by looking at difference of third row minus its blur blur_left = row_2.std.Convolution([0, 0, 0, 1, 0, 0, 0, 0, 0]) blur_left = blur_left.std.Convolution([0, 0, 0, 1, 0, 0, 0, 0, 0]) blur_left = blur_left.std.Convolution([0, 1, 0, 1, 0, 1, 0, 1, 0]) threshold_left = core.std.Expr([blur_left, row_2], diff_expr) threshold_left = threshold_left.std.CropAbs(width=y.width, height=1, top=1) blur_right = row_2.std.Convolution([0, 0, 0, 0, 0, 1, 0, 0, 0]) blur_right = blur_right.std.Convolution([0, 0, 0, 0, 0, 1, 0, 0, 0]) blur_right = blur_right.std.Convolution([0, 1, 0, 1, 0, 1, 0, 1, 0]) threshold_right = core.std.Expr([blur_right, row_2], diff_expr) threshold_right = threshold_right.std.CropAbs(y.width, 1, top=1) # evaluate whether blur results in a pixel being too different from its shift sl_fm_diff = core.std.Expr([shift_left, fill_mid], diff_expr) sr_fm_diff = core.std.Expr([shift_right, fill_mid], diff_expr) # x y z a b c d fix = core.std.Expr([sl_fm_diff, sr_fm_diff, threshold_left, threshold_right, fill_right, fill_left, fill_mid], f"x z {thr} * > y a {thr} * < and b y {thr} a * > x z {thr} * < and c d ? ?") s = core.std.StackVertical([fix, y.std.Crop(top=1)]) return core.std.ShufflePlanes([s, clip], [0, 1, 2], vs.YUV) if clip.format.color_family == vs.YUV else s
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 prepare_hr(self, clip: vs.VideoNode, dvd_clip: vs.VideoNode) -> vs.VideoNode: """ Match with the DVD @640x480: bd.resize.Bicubic(720, 486, src_left=-0.75).std.Crop(top=3, bottom=3) """ import havsfunc as haf import lvsfunc as lvf import muvsfunc as muf import rekt import vardefunc as vdf from awsmfunc import bbmod from vsutil import depth, join, plane from xvs import WarpFixChromaBlend rkt = rekt.rektlvls(clip, [0, 1079], [17, 16], [0, 1, 2, 3] + [1917, 1918, 1919], [16, 4, -2, 2] + [-2, 5, 14]) ef = bbmod(rkt, left=4, right=3, y=False) clip = depth(ef, 32).std.AssumeFPS(fpsnum=24, fpsden=1) bd_descale = lvf.kernels.Bicubic().descale(plane(clip, 0), 1280, 720) bd_doubled = vdf.scale.nnedi3_upscale(bd_descale) bd_down = muf.SSIM_downsample(bd_doubled, dvd_clip.width * 2, 486 * 2) # Need to do some fuckery to make sure the chroma matches up perfectly bd_cshift = clip.resize.Bicubic(chromaloc_in=1, chromaloc=0, format=vs.YUV420P16) bd_cwarp = bd_cshift.warp.AWarpSharp2(thresh=88, blur=3, type=1, depth=6, planes=[1, 2]) bd_chroma = bd_cwarp.resize.Bicubic(format=vs.YUV444P16, width=bd_down.width, height=bd_down.height) bd_i444 = core.std.ShufflePlanes([depth(bd_down, 16), bd_chroma]) bd_shift = bd_i444.resize.Bicubic(src_left=-0.75).std.Crop(top=6, bottom=6) return bd_shift.resize.Bicubic(format=vs.RGB24, dither_type='error_diffusion') \ .std.ShufflePlanes([1, 2, 0], vs.RGB)
def prot_dpir(clip: vs.VideoNode, strength: int = 25, matrix: Optional[Union[Matrix, int]] = None, cuda: bool = True, device_index: int = 0, **dpir_args: Any) -> vs.VideoNode: """ Protective DPIR function for the deblocking mode. Sometimes vs-dpir's deblocking mode will litter a random frame with a lot of red dots. This is obviously undesirable, so this function was written to combat that. Original code by Zewia, modified by LightArrowsEXE. Dependencies: * vs-dpir :param clip: Input clip :param strength: DPIR's deblocking strength :param matrix: Enum for the matrix of the input clip. See ``types.Matrix`` for more info. If `None`, gets matrix from the "_Matrix" prop of the clip :param cuda: Device type used for deblocking. Uses CUDA if True, else CPU :param device_index: The 'device_index' + 1º device of type device type in the system :dpir_args: Additional args to pass onto DPIR :return: Deblocked clip """ from vsdpir import DPIR if clip.format is None: raise ValueError("prot_dpir: 'Variable-format clips not supported'") dpir_args |= dict(strength=strength, task='deblock', device_type='cuda' if cuda else 'cpu', device_index=device_index) clip_rgb = depth(clip, 32).std.SetFrameProp('_Matrix', intval=matrix) clip_rgb = core.resize.Bicubic(clip_rgb, format=vs.RGBS) debl = DPIR(clip_rgb, **dpir_args) rgb_planes = split(debl) # Grab the brigher parts of the R plane to avoid model fuckery # Everything below 5 (8 bit value) gets replaced with the ref's R plane rgb_planes[0] = core.std.Expr([rgb_planes[0], rgb_planes[1], plane(clip_rgb, 0)], 'z x > y 5 255 / <= and z x ?') rgb_merge = join(rgb_planes, family=vs.RGB) return core.resize.Bicubic(rgb_merge, format=clip.format.id, matrix=matrix)
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)])
def do_filter(): """Vapoursynth filtering""" src = JPBD.src_cut # Variables opstart, opend = 2111, 4268 edstart, edend = 31650, 33809 full_zone = [(18727, 18774), (31590, 31649), (33990, src.num_frames - 1) ] # eyecatch, episode name and next episode shabc_zone = [(edstart + 15, edstart + 1215), (edstart + 1882, edstart + 2126)] h = 720 w = get_w(h) # Bicubic sharp parts don't have bad edges edges_a = core.edgefixer.ContinuityFixer(src, *[[2, 1, 1]] * 4) edges_b = awf.bbmod(src, left=6, thresh=32, blur=200) edges = lvf.rfs(edges_a, edges_b, [(edstart + 1275, edstart + 1757)]) edges = lvf.rfs(edges, src, [(opstart, opend)] + full_zone) out = depth(edges, 32) # Denoise ref = hvf.SMDegrain(depth(get_y(out), 16), thSAD=450) denoise = hybrid_denoise(out, 0.35, 1.4, dict(a=2, d=1), dict(ref=depth(ref, 32))) out = denoise # denoise = out # Descale luma = get_y(out) lineart = vdf.edge_detect(luma, 'FDOG', 0.055, (1, 1)).std.Median().std.BoxBlur(0, 1, 1, 1, 1) descale_a = core.descale.Despline36(luma, w, h).std.SetFrameProp('descaleKernel', data='spline36') descale_b = core.descale.Debicubic(luma, w, h, 0, 1).std.SetFrameProp( 'descaleKernel', data='sharp_bicubic') descale = lvf.rfs(descale_a, descale_b, shabc_zone) # Chroma reconstruction # y_m is the assumed mangled luma. # Descale 1080p -> Bad conversion in 422 720p -> Regular 1080p 420 radius = 2 y, u, v = descale, plane(out, 1), plane(out, 2) y_m = core.resize.Point(y, 640, 720, src_left=-1).resize.Bicubic(960, 540, filter_param_a=1 / 3, filter_param_b=1 / 3) # 0.25 for 444 and 0.25 for right shifting y_m, u, v = [ c.resize.Bicubic(w, h, src_left=0.25 + 0.25, filter_param_a=0, filter_param_b=.5) for c in [y_m, u, v] ] y_fixup = core.std.MakeDiff(y, y_m) yu, yv = Regress(y_m, u, v, radius=radius, eps=1e-7) u_fixup = ReconstructMulti(y_fixup, yu, radius=radius) u_r = core.std.MergeDiff(u, u_fixup) v_fixup = ReconstructMulti(y_fixup, yv, radius=radius) v_r = core.std.MergeDiff(v, v_fixup) # -0.5 * 720/1080 = -1/3 # -1/3 for the right shift # https://forum.doom9.org/showthread.php?p=1802716#post1802716 u_r, v_r = [ c.resize.Bicubic(960, 540, src_left=-1 / 3, filter_param_a=-.5, filter_param_b=.25) for c in [u_r, v_r] ] upscale = vdf.fsrcnnx_upscale( descale, height=h * 2, shader_file=r'shaders\FSRCNNX_x2_56-16-4-1.glsl', upscaler_smooth=eedi3_upscale, profile='zastin') antialias = sraa_eedi3(upscale, 3, alpha=0.2, beta=0.4, gamma=40, nrad=3, mdis=20) downscale = muvf.SSIM_downsample(antialias, src.width, src.height, filter_param_a=0, filter_param_b=0) downscale = core.std.MaskedMerge(luma, downscale, lineart) merged_a = join([downscale, u_r, v_r]) merged_b = vdf.merge_chroma(downscale, denoise) merged = lvf.rfs(merged_a, merged_b, shabc_zone) out = depth(merged, 16) warp = xvs.WarpFixChromaBlend(out, 80, 2, depth=8) out = warp dering = gf.MaskedDHA(out, rx=1.25, ry=1.25, darkstr=0.05, brightstr=1.0, maskpull=48, maskpush=140) out = dering qtgmc = hvf.QTGMC(out, Preset="Slower", InputType=1, ProgSADMask=2.0) qtgmc = vdf.fade_filter(out, out, qtgmc, edstart + 1522, edstart + 1522 + 24) qtgmc = lvf.rfs(out, qtgmc, [(edstart + 1522 + 25, edstart + 1757)]) out = qtgmc out = lvf.rfs(out, depth(denoise, 16), [(opstart, opend)]) detail_dark_mask = detail_dark_mask_func(get_y(out), brz_a=8000, brz_b=6000) detail_light_mask = lvf.denoise.detail_mask(out, brz_a=2500, brz_b=1200) detail_mask = core.std.Expr([detail_dark_mask, detail_light_mask], 'x y +').std.Median() detail_mask_grow = iterate(detail_mask, core.std.Maximum, 2) detail_mask_grow = iterate(detail_mask_grow, core.std.Inflate, 2).std.BoxBlur(0, 1, 1, 1, 1) detail_mask = core.std.Expr([get_y(out), detail_mask_grow, detail_mask], f'x {32<<8} < y z ?') deband = dumb3kdb(out, 22, 30) deband = core.std.MaskedMerge(deband, out, detail_mask) out = deband ref = get_y(out).std.PlaneStats() adgmask_a = core.adg.Mask(ref, 30) adgmask_b = core.adg.Mask(ref, 12) stgrain = sizedgrn(out, 0.1, 0.05, 1.05, sharp=80) stgrain = core.std.MaskedMerge(out, stgrain, adgmask_b) stgrain = core.std.MaskedMerge(out, stgrain, adgmask_a.std.Invert()) dygrain = sizedgrn(out, 0.2, 0.05, 1.15, sharp=80, static=False) dygrain = core.std.MaskedMerge(out, dygrain, adgmask_a) grain = core.std.MergeDiff(dygrain, out.std.MakeDiff(stgrain)) out = grain ref = depth(edges, 16) credit = out rescale_mask = vdf.diff_rescale_mask(ref, h, b=0, c=1, mthr=40, sw=0, sh=0) rescale_mask = vdf.region_mask(rescale_mask, *[10] * 4) rescale_mask = hvf.mt_expand_multi(rescale_mask, mode='ellipse', sw=4, sh=4).std.BoxBlur(0, 1, 1, 1, 1) credit = lvf.rfs(credit, ref, full_zone) credit = lvf.rfs(credit, core.std.MaskedMerge(credit, ref, rescale_mask), [(edstart, edend)]) out = credit return depth(out, 10).std.Limiter(16 << 2, [235 << 2, 240 << 2])
def smart_descale(clip: vs.VideoNode, resolutions: List[int], kernel: str = 'bicubic', b: Union[float, Fraction] = Fraction(0), c: Union[float, Fraction] = Fraction(1, 2), taps: int = 4, thr: float = 0.05, rescale: bool = False) -> vs.VideoNode: """ A function that descales a clip to multiple resolutions. This function will descale clips to multiple resolutions and return the descaled clip that is mostly likely to be the actual resolution of the clip. This is useful for shows like Shield Hero, Made in Abyss, or Symphogear that love jumping between multiple resolutions. The returned clip will be multiple resolutions, meaning most resamplers will break, as well as encoding it as-is. When handling it, please ensure you return the clip to a steady resolution before further processing. Setting rescaled will use `smart_rescaler` to reupscale the clip to its original resolution. This will return a proper YUV clip as well. If rescaled is set to False, the returned clip will be GRAY. Original written by kageru, and in part rewritten by me. Thanks Varde for helping me fix some bugs with it. Dependencies: vapoursynth-descale :param clip: Input clip :param resolutions: A list of resolutions to descale to :param kernel: Kernel used to descale (see :py:func:`lvsfunc.util.get_scale_filter`, Default: bicubic) :param b: B-param for bicubic kernel (Default: 0) :param c: C-param for bicubic kernel (Default: 1 / 2) :param taps: Taps param for lanczos kernel (Default: 4) :param thr: Threshold for when a clip is discerned as "non-scaleable" (Default: 0.05) :param rescale: Rescale the clip to the original resolution after descaling (Default: False) :return: Variable-resolution clip containing descaled frames """ try: from descale import get_filter except ModuleNotFoundError: raise ModuleNotFoundError( "smart_descale: missing dependency 'descale'") b = float(b) c = float(c) Resolution = namedtuple('Resolution', ['width', 'height']) ScaleAttempt = namedtuple('ScaleAttempt', ['descaled', 'rescaled', 'resolution', 'diff']) clip_c = clip clip = util.resampler((get_y(clip) if clip.format.num_planes != 1 else clip), 32) \ .std.SetFrameProp('descaleResolution', intval=clip.height) def _perform_descale(height: int) -> ScaleAttempt: resolution = Resolution(get_w(height, clip.width / clip.height), height) descaled = get_filter(b, c, taps, kernel)(clip, resolution.width, resolution.height) \ .std.SetFrameProp('descaleResolution', intval=height) rescaled = util.get_scale_filter(kernel, b=b, c=c, taps=taps)(descaled, clip.width, clip.height) diff = core.std.Expr([rescaled, clip], 'x y - abs').std.PlaneStats() return ScaleAttempt(descaled, rescaled, resolution, diff) clips_by_resolution = { c.resolution.height: c for c in map(_perform_descale, resolutions) } # If we pass a variable res clip as first argument to FrameEval, we’re also allowed to return one. variable_res_clip = core.std.Splice([ core.std.BlankClip(clip, length=len(clip) - 1), core.std.BlankClip(clip, length=1, width=clip.width + 1) ], mismatch=True) def _select_descale(n: int, f: List[vs.VideoFrame]): best_res = max( f, key=lambda frame: math.log( clip.height - frame.props.descaleResolution, 2) * round( 1 / max(frame.props.PlaneStatsAverage, 1e-12))**0.2) best_attempt = clips_by_resolution.get( best_res.props.descaleResolution) if thr == 0: return best_attempt.descaled # No blending here because src and descaled have different resolutions. # The caller can use the frameProps to deal with that if they so desire. if best_res.props.PlaneStatsAverage > thr: return clip return best_attempt.descaled props = [c.diff for c in clips_by_resolution.values()] descaled = core.std.FrameEval(variable_res_clip, _select_descale, prop_src=props) if rescale: upscale = smart_reupscale(descaled, height=clip.height, kernel=kernel, b=b, c=c, taps=taps) if clip_c.format is vs.GRAY: return upscale return join([upscale, plane(clip_c, 1), plane(clip_c, 2)]) return descaled
def zzdeband(clip: vs.VideoNode, denoised: bool = False, mask: int = 0, f3kdb_args: Dict[str, Any] = {}, placebo_args: Dict[str, Any] = {}) -> Union[vs.VideoNode, Any]: """ Written by Zastin, *CAUTIOUSLY* modified by puny little me This is all pure black magic to me, so I'm just gonna pretend I didn't see anything. """ import zzfunc as zzf plcbo_args: Dict[str, Any] = dict(iterations=3, threshold=5, radius=16, grain=0) plcbo_args.update(placebo_args) dumb3kdb_args: Dict[str, Any] = dict(radius=16, threshold=[30, 0], grain=0) dumb3kdb_args.update(f3kdb_args) brz = 256 if denoised else 384 clip_depth = get_depth(clip) if clip_depth != 16: clip = depth(clip, 16) clip_y = plane(clip, 0) ymax = maxm(clip_y, sw=30, mode='ellipse') ymin = minm(clip_y, sw=30, mode='ellipse') # edge detection thr = 3.2 * 256 ypw0 = clip_y.std.Prewitt() ypw = ypw0.std.Binarize(thr).rgvs.RemoveGrain(11) if mask == 1: return ypw # range masks (neighborhood max - min) rad, thr = 3, 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) if mask == 2: return yrangesml rad, thr = 14, 6.5 * 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) if mask == 3: return yrangebig # morphological masks (shapes) rad = 30 ymph = core.std.Expr([ clip_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(brz) ymph = ymph.std.Minimum().std.Maximum() ymph = ymph.std.BoxBlur(0, 4, 1, 4, 1) if mask == 4: return ymph grad_mask = zzf.combine([ymph, yrangesml, ypw]) if mask == 5: return grad_mask ydebn_strong = clip_y.placebo.Deband(1, **plcbo_args) ydebn_normal = vdf.deband.dumb3kdb(clip_y, **dumb3kdb_args) ydebn = ydebn_strong.std.MaskedMerge(ydebn_normal, grad_mask) ydebn = ydebn.std.MaskedMerge(clip_y, yrangebig) merged = join([ydebn, plane(clip, 1), plane(clip, 2)]) return merged if clip_depth == 16 else depth(merged, clip_depth)
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)
def placebo_debander(clip: vs.VideoNode, grain: float = 2.0, deband_args: Mapping[str, Any] = {}) -> vs.VideoNode: return join([ core.placebo.Deband(plane(clip, 0), grain=grain, **deband_args), core.placebo.Deband(plane(clip, 1), grain=0, **deband_args), core.placebo.Deband(plane(clip, 2), grain=0, **deband_args) ])
def based_aa(clip: vs.VideoNode, shader_file: str = "FSRCNNX_x2_56-16-4-1.glsl", rfactor: float = 2.0, tff: bool = True, mask_thr: float = 60, show_mask: bool = False, lmask: vs.VideoNode | None = None, **eedi3_args: Any) -> vs.VideoNode: """ As the name implies, this is a based anti-aliaser. Thank you, based Zastin. This relies on FSRCNNX being very sharp, and as such it very much acts like the main "AA" here. Original function by Zastin, modified by LightArrowsEXE. Dependencies: * vapoursynth-eedi3 * vs-placebo :param clip: Input clip :param shader_file: Path to FSRCNNX shader file :param rfactor: Image enlargement factor :param tff: Top-Field-First if true, Bottom-Field-First if alse :param mask_thr: Threshold for the edge mask binarisation. Scaled internally to match bitdepth of clip. :param show_mask: Output mask :param eedi3_args: Additional args to pass to eedi3 :param lmask: Line mask clip to use for eedi3 :return: AA'd clip or mask clip """ def _eedi3s(clip: vs.VideoNode, mclip: vs.VideoNode | None = None, **eedi3_kwargs: Any) -> vs.VideoNode: edi_args: Dict[str, Any] = { # Eedi3 args for `eedi3s` 'field': int(tff), 'alpha': 0.125, 'beta': 0.25, 'gamma': 40, 'nrad': 2, 'mdis': 20, 'vcheck': 2, 'vthresh0': 12, 'vthresh1': 24, 'vthresh2': 4 } edi_args |= eedi3_kwargs out = core.eedi3m.EEDI3(clip, dh=False, sclip=clip, planes=0, **edi_args) if mclip: return core.std.Expr([clip, out, mclip], 'z y x ?') return out 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) check_variable(clip, "based_aa") assert clip.format aaw = (round(clip.width * rfactor) + 1) & ~1 aah = (round(clip.height * rfactor) + 1) & ~1 clip_y = get_y(clip) if not lmask: if mask_thr > 255: raise ValueError( f"based_aa: 'mask_thr must be equal to or lower than 255 (current: {mask_thr})'" ) mask_thr = scale_value(mask_thr, 8, get_depth(clip)) lmask = clip_y.std.Prewitt().std.Binarize( mask_thr).std.Maximum().std.BoxBlur(0, 1, 1, 1, 1) mclip_up = _resize_mclip(lmask, aaw, aah) if show_mask: return lmask aa = depth(clip_y, 16).std.Transpose() aa = join([aa] * 3).placebo.Shader(shader=shader_file, filter='box', width=aa.width * 2, height=aa.height * 2) aa = depth(aa, get_depth(clip_y)) aa = ssim_downsample(get_y(aa), aah, aaw) aa = _eedi3s(aa, mclip=mclip_up.std.Transpose(), **eedi3_args).std.Transpose() aa = ssim_downsample(_eedi3s(aa, mclip=mclip_up, **eedi3_args), clip.width, clip.height) aa = depth(aa, get_depth(clip_y)) aa_merge = core.std.MaskedMerge(clip_y, aa, lmask) if clip.format.num_planes == 1: return aa_merge return join([aa_merge, plane(clip, 1), plane(clip, 2)])
def filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]: """Main filterchain""" import havsfunc as haf import lvsfunc as lvf import muvsfunc as muf import debandshit as dbs import vardefunc as vdf from vsutil import depth, get_y, insert_clip, join, plane src = JP_DVD.clip_cut.std.AssumeFPS(fpsnum=24000, fpsden=1001) src_ncop, src_nced = JP_BD_NCOP.clip_cut, JP_BD_NCED.clip_cut src_ncop = src_ncop + src_ncop[-1] * 11 src_nced = src_nced + src_nced[-1] src_03 = JP_BD_03.clip_cut src_ncop = flt.rekt(src_ncop) src_nced = flt.rekt(src_nced) src_03 = flt.rekt(src_03) # Fixing an animation error in the NCOP sqmask_ncop = lvf.mask.BoundingBox((419, 827), (1500, 68)) masked_ncop = core.std.MaskedMerge(src_ncop, src_03, sqmask_ncop.get_mask(src_ncop)) masked_ncop = lvf.rfs(src_ncop, masked_ncop, [(opstart+2064, opstart+2107)]) op_mask = vdf.dcm( src_03, src_03[opstart:opstart+masked_ncop.num_frames-op_offset], masked_ncop[:-op_offset], start_frame=opstart, thr=85, prefilter=True) if opstart is not False \ else get_y(core.std.BlankClip(src)) op_mask = depth(core.resize.Bicubic(op_mask, 1296, 728).std.Crop(top=4, bottom=4), 16) # Stick credits ontop of NC because it looks way better masked_ncop = src_ncop.resize.Bicubic(chromaloc_in=1, chromaloc=0) cwarp_ncop = masked_ncop.warp.AWarpSharp2(thresh=88, blur=3, type=1, depth=6, planes=[1, 2]) descale_ncop = lvf.kernels.Bicubic().descale(plane(depth(src_ncop, 16), 0), 1280, 720) chroma_down_ncop = core.resize.Bicubic(cwarp_ncop, 1280, 720, vs.YUV444P16) scaled_ncop = join([descale_ncop, plane(chroma_down_ncop, 1), plane(chroma_down_ncop, 2)]) scaled_ncop = scaled_ncop.resize.Bicubic(1296, 728).std.Crop(top=4, bottom=4) downscale_03 = src_03.resize.Bicubic(1296, 728, vs.YUV444P16).std.Crop(top=4, bottom=4) merge_credits_03 = core.std.MaskedMerge(scaled_ncop, downscale_03, op_mask) for prop, val in PROPS_DVD: src = src.std.SetFrameProp(prop, intval=val) dvd_i444 = vdf.scale.to_444(depth(src, 32), src.width, src.height, join_planes=True) dvd_rgb = dvd_i444.resize.Bicubic(format=vs.RGB24, dither_type='error_diffusion', matrix_in=6) upscale = vsgan.run(dvd_rgb) conv = core.resize.Bicubic(upscale, format=vs.YUV444P16, matrix=1) scaled = depth(muf.SSIM_downsample(conv, 1296, 720), 16) splice_op = lvf.rfs(scaled, merge_credits_03, [(0, 203), (263, 1740), (1836, 2018)]) # Splicing for the ED. Halves of the ED have visuals, others are credits masked_nced = src_nced.resize.Bicubic(chromaloc_in=1, chromaloc=0) cwarp_nced = masked_nced.warp.AWarpSharp2(thresh=88, blur=3, type=1, depth=6, planes=[1, 2]) descale_nced = lvf.kernels.Bicubic().descale(plane(depth(src_nced, 16), 0), 1280, 720) chroma_down_nced = core.resize.Bicubic(cwarp_nced, 1280, 720, vs.YUV444P16) scaled_nced = join([descale_nced, plane(chroma_down_nced, 1), plane(chroma_down_nced, 2)]) scaled_nced = scaled_nced.resize.Bicubic(1296, 728).std.Crop(top=4, bottom=4) ed_nc = scaled_nced ed_dvd = splice_op[31888:] sq_top = lvf.mask.BoundingBox((0, 0), (ed_nc.width, ed_nc.height/2)).get_mask(ed_nc) sq_left = lvf.mask.BoundingBox((0, 0), (ed_nc.width/2, ed_nc.height)).get_mask(ed_nc) sq_right = sq_left.std.Invert() mask_top = core.std.MaskedMerge(ed_dvd, ed_nc, sq_top) mask_left = core.std.MaskedMerge(ed_dvd, ed_nc, sq_left) mask_right = core.std.MaskedMerge(ed_dvd, ed_nc, sq_right) ed_splice = lvf.rfs(ed_dvd, mask_top, [(0, 1213), (1613, 1673), (1794, None)]) ed_splice = lvf.rfs(ed_splice, mask_left, [(1390, 1503)]) ed_splice = lvf.rfs(ed_splice, mask_right, [(1214, 1389), (1504, 1612), (1674, 1793)]) splice_ed = insert_clip(splice_op, ed_splice, edstart) # Back to more regular filtering stab = haf.GSMC(splice_ed, radius=2, planes=[0]) decs = vdf.noise.decsiz(stab, sigmaS=8, min_in=208 << 8, max_in=232 << 8) detail_mask = flt.detail_mask(decs, brz=(1800, 3500)) deband = dbs.debanders.dumb3kdb(decs, threshold=32, grain=16) deband_masked = core.std.MaskedMerge(deband, decs, detail_mask) grain = vdf.noise.Graigasm( # Mostly stolen from Varde tbh thrs=[x << 8 for x in (32, 80, 128, 176)], strengths=[(0.25, 0.0), (0.2, 0.0), (0.15, 0.0), (0.0, 0.0)], sizes=(1.2, 1.15, 1.05, 1), sharps=(65, 50, 40, 40), grainers=[ vdf.noise.AddGrain(seed=69420, constant=True), vdf.noise.AddGrain(seed=69420, constant=False), vdf.noise.AddGrain(seed=69420, constant=False) ]).graining(deband_masked) return grain
def masked_deband(clip: vs.VideoNode, denoised: bool = False, deband_args: Dict[str, Any] = {}) -> vs.VideoNode: """ Written by Zastin, *CAUTIOUSLY* modified by puny little me This is all pure black magic to me, so I'm just gonna pretend I didn't see anything. """ import zzfunc as zzf placebo_args: Dict[str, Any] = dict(iterations=2, threshold=4.0, radius=12, grain=4.0) placebo_args |= deband_args brz = 256 if denoised else 384 assert clip.format is not None clip_depth = clip.format.bits_per_sample if clip_depth != 16: clip = depth(clip, 16) clip_y = plane(clip, 0) stats = clip_y.std.PlaneStats() agm3 = core.adg.Mask(stats, 3) ymax = maxm(clip_y, sw=30, mode='ellipse') ymin = minm(clip_y, sw=30, mode='ellipse') # edge detection thr = 3.2 * 256 ypw0 = clip_y.std.Prewitt() ypw = ypw0.std.Binarize(thr).rgvs.RemoveGrain(11) # range masks (neighborhood max - min) rad, thr = 3, 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, thr = 16, 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) # morphological masks (shapes) rad, thr = 30, 1 * 256 ymph = core.std.Expr([ clip_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(brz) 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() ], expr='65535 y - x min z -').std.BoxBlur( 0, 16, 1, 16, 1) ydebn_strong = clip_y.placebo.Deband(1, **placebo_args) ydebn_normal = clip_y.f3kdb.Deband(16, 41, 0, 0, 0, 0, output_depth=16) ydebn = ydebn_strong.std.MaskedMerge(ydebn_normal, grad_mask) ydebn = ydebn.std.MaskedMerge(clip_y, yrangebig) strong_grain = ydebn_strong.grain.Add(0.25, constant=True, seed=69420) normal_grain = ydebn.std.MaskedMerge( ydebn.grain.Add(0.1, constant=True, seed=69420), agm3) y_final = normal_grain.std.MaskedMerge(strong_grain, grain_mask) merged = join([y_final, plane(clip, 1), plane(clip, 2)]) return merged if clip_depth == 16 \ else depth(merged, clip_depth)
def pre_filterchain() -> Union[vs.VideoNode, Tuple[vs.VideoNode, ...]]: """ Regular filtering to get the output images to stitch. This is preferable over handling it unfiltered, since it'll be faster than encoding it and reduces the amount of jitter caused by the upscale after. """ import lvsfunc as lvf import rekt import vardefunc as vdf from awsmfunc import bbmod from muvsfunc import SSIM_downsample from vsutil import depth, get_y, join, plane src = JP_BD.clip_cut # Fixing animation f**k-ups if freeze_ranges: src = core.std.FreezeFrames( src, [s[0] for s in freeze_ranges], [e[1] for e in freeze_ranges], [f[2] for f in freeze_ranges] ) # Edgefixing ef = rekt.rektlvls( src, prot_val=[16, 235], min=16, max=235, rownum=[0, src.height-1], rowval=[16, 16], colnum=[0, src.width-1], colval=[16, 16], ) bb_y = bbmod(ef, left=1, top=1, right=1, bottom=1, thresh=32, y=True, u=False, v=False) bb_uv = bbmod(bb_y, left=2, top=2, right=2, bottom=2, y=False, u=True, v=True) cshift = flt.shift_chroma(bb_uv, left=0.6) cshift = lvf.rfs(bb_uv, cshift, cshift_left_ranges) bb32 = depth(cshift, 32) bb32_y = get_y(bb32) # Descaling + DPIR while it's at a lower res (so I can actually run it because >memory issues xd) descale = lvf.kernels.Catrom().descale(bb32_y, 1280, 720) downscale = lvf.kernels.Catrom(format=vs.YUV444PS).scale(bb32, 1280, 720) descale_444 = join([descale, plane(downscale, 1), plane(downscale, 2)]) denoise_y = lvf.deblock.vsdpir(descale_444, strength=2.75, mode='deblock', matrix=1, i444=True, cuda=True) supersample = vdf.scale.fsrcnnx_upscale(get_y(denoise_y), shader_file=shader_file, downscaler=None) 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) # Create credit mask upscale = lvf.kernels.Catrom().scale(descale, src.width, src.height) credit_mask = lvf.scale.descale_detail_mask(bb32_y, upscale, threshold=0.055) \ .std.Deflate().std.Deflate().std.Minimum() # Merge early for additional accuracy with DPIR merged = core.std.MaskedMerge(downscaled, bb32_y, credit_mask) down_y = lvf.kernels.Catrom().scale(merged, src.width/2, src.height/2) down_i444 = join([down_y, plane(bb32, 1), plane(bb32, 2)]) deblock_down = lvf.deblock.vsdpir(down_i444, strength=3, mode='denoise', matrix=1, i444=True, cuda=True) scaled = depth(join([merged, plane(deblock_down, 1), plane(deblock_down, 2)]), 16) # Final bit of "denoising" dft = core.dfttest.DFTTest(scaled, sigma=2.0, tbsize=5, tosize=3, planes=[0]) decs = vdf.noise.decsiz(dft, sigmaS=4, min_in=208 << 8, max_in=232 << 8) # AA baa = lvf.aa.based_aa(decs, str(shader_file)) sraa = lvf.sraa(decs, rfactor=1.65) clmp = lvf.aa.clamp_aa(decs, baa, sraa, strength=1.3) dehalo = lvf.dehalo.masked_dha(clmp, rx=1.4, ry=1.4, brightstr=0.4) cwarp = core.warp.AWarpSharp2(dehalo, thresh=72, blur=3, type=1, depth=4, planes=[1, 2]) # Merge credits (if applicable) merged = core.std.MaskedMerge(cwarp, depth(bb32, 16), depth(credit_mask, 16)) deband = core.average.Mean([ flt.masked_f3kdb(merged, rad=16, thr=[20, 24], grain=[24, 12]), flt.masked_f3kdb(merged, rad=20, thr=[28, 24], grain=[24, 12]), flt.masked_placebo(merged, rad=6, thr=2.5, itr=2, grain=4) ]) no_flt = lvf.rfs(deband, depth(bb32, 16), no_filter) return no_flt
def FaggotDB(clip: vs.VideoNode, thrY=40, thrC=None, radiusY=15, radiusC=15, CbY=44, CrY=44, CbC=44, CrC=44, grainY=20, grainC=None, sample_mode=2, neo=False, dynamic_grainY=False, dynamic_grainC=False, tv_range=True, mask=None) -> vs.VideoNode: funcName = "FaggotDB" # Original Idea: Author who created Fag3kdb. Edited by AlucardSama04 if not isinstance(clip, vs.VideoNode): raise TypeError(f"{funcName}: This is not a clip") if clip.format.bits_per_sample != 16: raise TypeError(f"{funcName}: Only 16Bit clips are supported") if not isinstance(mask, vs.VideoNode): raise vs.Error(f"{funcName}: mask' only clip inputs") if thrC is None: thrC = int(round(thrY / 2)) if grainC is None: grainC = int(round(grainY / 2)) 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=16) 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=16) 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=0, sample_mode=sample_mode, dynamic_grain=dynamic_grainY, keep_tv_range=tv_range, output_depth=16) return core.std.MaskedMerge( filtered, clip, mask) #from vapoursynth import core, VideoNode, GRAY, YUV
def descale(clip: vs.VideoNode, upscaler: Optional[Callable[[vs.VideoNode, int, int], vs.VideoNode]] = reupscale, width: Union[int, List[int], None] = None, height: Union[int, List[int]] = 720, kernel: kernels.Kernel = kernels.Bicubic(b=0, c=1 / 2), threshold: float = 0.0, mask: Optional[Callable[[vs.VideoNode, vs.VideoNode], vs.VideoNode]] = descale_detail_mask, src_left: float = 0.0, src_top: float = 0.0, show_mask: bool = False) -> vs.VideoNode: """ A unified descaling function. Includes support for handling fractional resolutions (experimental), multiple resolutions, detail masking, and conditional scaling. If you want to descale to a fractional resolution, set src_left and src_top and round up the target height. If the source has multiple native resolutions, specify ``height`` as a list. If you want to conditionally descale, specify a non-zero threshold. Dependencies: vapoursynth-descale, znedi3 :param clip: Clip to descale :param upscaler: Callable function with signature upscaler(clip, width, height) -> vs.VideoNode to be used for reupscaling. Must be capable of handling variable res clips for multiple heights and conditional scaling. If a single height is given and upscaler is None, a constant resolution GRAY clip will be returned instead. Note that if upscaler is None, no upscaling will be performed and neither detail masking nor proper fractional descaling can be preformed. (Default: :py:func:`lvsfunc.scale.reupscale`) :param width: Width to descale to (if None, auto-calculated) :param height: Height(s) to descale to. List indicates multiple resolutions, the function will determine the best. (Default: 720) :param kernel: Kernel used to descale (see :py:class:`lvsfunc.kernels.Kernel`, (Default: kernels.Bicubic(b=0, c=1/2)) :param threshold: Error threshold for conditional descaling (Default: 0.0, always descale) :param mask: Function used to mask detail. If ``None``, no masking. Function must accept a clip and a reupscaled clip and return a mask. (Default: :py:func:`lvsfunc.scale.descale_detail_mask`) :param src_left: Horizontal shifting for fractional resolutions (Default: 0.0) :param src_top: Vertical shifting for fractional resolutions (Default: 0.0) :param show_mask: Return detail mask :return: Descaled and re-upscaled clip with float bitdepth """ if clip.format is None: raise ValueError("descale: 'Variable-format clips not supported'") if type(height) is int: height = [cast(int, height)] height = cast(List[int], height) if type(width) is int: width = [cast(int, width)] elif width is None: width = [ get_w(h, aspect_ratio=clip.width / clip.height) for h in height ] width = cast(List[int], width) if len(width) != len(height): raise ValueError( "descale: Asymmetric number of heights and widths specified") resolutions = [Resolution(*r) for r in zip(width, height)] clip = depth(clip, 32) assert clip.format is not None # clip was modified by depth, but that wont make it variable clip_y = get_y(clip) \ .std.SetFrameProp('descaleResolution', intval=clip.height) variable_res_clip = core.std.Splice([ core.std.BlankClip(clip_y, length=len(clip) - 1), core.std.BlankClip(clip_y, length=1, width=clip.width + 1) ], mismatch=True) descale_partial = partial(_perform_descale, clip=clip_y, kernel=kernel) clips_by_resolution = { c.resolution.height: c for c in map(descale_partial, resolutions) } props = [c.diff for c in clips_by_resolution.values()] select_partial = partial(_select_descale, threshold=threshold, clip=clip_y, clips_by_resolution=clips_by_resolution) descaled = core.std.FrameEval(variable_res_clip, select_partial, prop_src=props) if src_left != 0 or src_top != 0: descaled = core.resize.Bicubic(descaled, src_left=src_left, src_top=src_top) if upscaler is None: upscaled = descaled if len(height) == 1: upscaled = core.resize.Point(upscaled, width[0], height[0]) else: return upscaled else: upscaled = upscaler(descaled, clip.width, clip.height) if src_left != 0 or src_top != 0: upscaled = core.resize.Bicubic(descaled, src_left=-src_left, src_top=-src_top) if upscaled.format is None: raise RuntimeError( "descale: 'Upscaler cannot return variable-format clips'") if mask: clip_y = clip_y.resize.Point(format=upscaled.format.id) rescaled = kernel.scale(descaled, clip.width, clip.height, (src_left, src_top)) rescaled = rescaled.resize.Point(format=clip.format.id) dmask = mask(clip_y, rescaled) if upscaler is None: dmask = core.resize.Spline36(dmask, upscaled.width, upscaled.height) clip_y = core.resize.Spline36(clip_y, upscaled.width, upscaled.height) if show_mask: return dmask upscaled = core.std.MaskedMerge(upscaled, clip_y, dmask) upscaled = depth(upscaled, get_depth(clip)) if clip.format.num_planes == 1 or upscaler is None: return upscaled return join([upscaled, plane(clip, 1), plane(clip, 2)])
def test_plane(self): y = vs.core.std.BlankClip(format=vs.GRAY8) # This should be a no-op, and even the clip reference shouldn’t change self.assertEqual(y, vsutil.plane(y, 0))