Last active
September 6, 2021 07:15
-
-
Save blaze077/ac896645913938591b45e7d65932b136 to your computer and use it in GitHub Desktop.
Collection of Meine Funktionen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import vapoursynth as vs | |
import functools | |
import numpy as np | |
core = vs.core | |
def extended_dimension(src, dimensions=[0, 0, 0, 0]): | |
""" | |
Extends the edges of the clip using the given extension parameters (like negative cropping in Avisynth) | |
src is the source clip. dimensions is an array of integer values in the following format: [left, right, top, bottom]""" | |
if len(dimensions) > 4: | |
raise ValueError("dimensions can only contain 4 values.") | |
while len(dimensions) < 4: | |
dimensions.append(0) | |
if not all(isinstance(d, int) for d in dimensions): | |
raise ValueError("dimensions must be a list of integers.") | |
if 0 > min(dimensions): | |
raise ValueError("dimensions cannot be negative") | |
w_subsampling=max(src.format.subsampling_w*2, 1) | |
h_subsampling=max(src.format.subsampling_w*2, 1) | |
if True in [x % w_subsampling != 0 for x in dimensions[:2]] \ | |
or True in [y % h_subsampling != 0 for y in dimensions[2:4]]: | |
raise ValueError("The video format is "+src.format.name+".\n You need to enter valid dimension values according to the" \ | |
+ " chroma subsampling.") | |
left = dimensions[0]//w_subsampling | |
right = dimensions[1]//w_subsampling | |
top = dimensions[2]//h_subsampling | |
bottom = dimensions[3]//h_subsampling | |
for _ in range(left): src = core.std.StackHorizontal([core.std.CropRel(src, right = src.width - w_subsampling), src]) | |
for _ in range(right): src = core.std.StackHorizontal([src, core.std.CropRel(src, left = src.width - w_subsampling)]) | |
for _ in range(top): src = core.std.StackVertical([core.std.CropRel(src, bottom = src.height - h_subsampling), src]) | |
for _ in range(bottom): src = core.std.StackVertical([src, core.std.CropRel(src, top = src.height - h_subsampling)]) | |
return src | |
def MCLS(clip: vs.VideoNode, strength=1.0, sharp: vs.VideoNode=None, thsad=400, chroma=False, \ | |
ss=None, repair=None, dmode=1, masked=True, blur_gaussian=False): | |
""" | |
Motion Compensated Limited Sharpen | |
Ported from its Avisynth counterpart: https://forum.doom9.org/showthread.php?p=1767949 | |
Only supports Integer YUV and GRAY input. | |
""" | |
if clip.format.color_family not in [vs.YUV, vs.GRAY] or clip.format.sample_type != 0: | |
raise ValueError("MCLS: Only supports Integer YUV or GRAY input") | |
if ss is None: | |
ss = 1.25 if sharp is None else 1.0 | |
if repair is None: | |
repair = True if sharp is None else False | |
HD = 0 | |
if clip.width > 1280: | |
HD = 2 | |
elif clip.width > 720: | |
HD = 1 | |
pel = [2, 1, 1][HD] | |
truemotion = [True, False, False][HD] | |
blksize = [8, 16, 32][HD] | |
overlap = [4, 8, 8][HD] | |
isGray = False | |
if clip.format.num_planes == 1: | |
if chroma: | |
raise ValueError("MCLS: chroma=True cannot be used with GRAY input") | |
isGray=True | |
ss_width = round(clip.width*ss) | |
ss_height = round(clip.height*ss) | |
w_pad = 16 - (ss_width%16) | |
h_pad = 16 - (ss_height%16) | |
b_radius = round([3, 3, 4][HD] / 1.25 * ss) | |
if not chroma and not isGray: | |
u = core.std.ShufflePlanes(clip, 1, vs.GRAY) | |
v = core.std.ShufflePlanes(clip, 2, vs.GRAY) | |
clip = core.std.ShufflePlanes(clip, 0, vs.GRAY) | |
resized_source = clip if ss == 1.0 else core.resize.Spline36(clip, ss_width+w_pad, ss_height+h_pad) | |
if sharp is not None: | |
resized_sharp = sharp if ss == 1.0 else core.resize.Spline36(sharp, ss_width+w_pad, ss_height+h_pad) | |
sharp = core.std.MakeDiff(resized_sharp, resized_source) | |
else: | |
if blur_gaussian: | |
blurred = core.std.BoxBlur(resized_source, hradius=max(b_radius-2, 1), vradius=max(b_radius-2, 1)) | |
for _ in range(2): | |
blurred = core.std.BoxBlur(blurred, hradius=max(b_radius-1,1), vradius=max(b_radius-1,1)) | |
else: | |
blurred = core.std.BoxBlur(resized_source, hradius=b_radius, vradius=b_radius) | |
sharp = core.std.MakeDiff(resized_source, blurred) | |
pre = core.rgvs.RemoveGrain(resized_source, 3) | |
bitdepth = clip.format.bits_per_sample | |
if masked: | |
mask = core.std.ShufflePlanes(pre, 0, vs.GRAY) | |
mask = core.std.Expr([core.std.Maximum(mask), core.std.Minimum(mask)], ["x y - abs"]) | |
mask = core.std.Binarize(mask, 10 << bitdepth - 8, planes=0) | |
mask = core.std.Deflate(mask, planes=0).rgvs.RemoveGrain(12) | |
sup0 = core.mv.Super(pre, pel=pel, sharp=2, chroma=chroma) | |
sup1 = core.mv.Super(sharp, pel=pel, sharp=2, chroma=chroma) | |
bv1 = core.mv.Analyse(sup0, isb=True, delta=1, chroma=chroma, blksize=blksize, truemotion=truemotion, overlap=overlap) | |
fv1 = core.mv.Analyse(sup0, isb=False, delta=1, chroma=chroma, blksize=blksize, truemotion=truemotion, overlap=overlap) | |
plane = 4 if chroma else 0 | |
diff = core.mv.Degrain1(sharp, sup1, bv1, fv1, plane=plane, thsad=thsad) | |
sharpened_back = core.std.MergeDiff(resized_source, diff) | |
if repair: | |
repaired = core.rgvs.Repair(sharpened_back, resized_source, [1, 1] if chroma else 1) | |
repaired = core.std.MaskedMerge(repaired, sharpened_back, core.std.BlankClip(sharpened_back, format=vs.GRAY8, \ | |
color = 76 << bitdepth - 8).fmtc.bitdepth(bits=bitdepth)) | |
else: | |
repaired = sharpened_back | |
def scale(n, bits): | |
return n << bits - 8 | |
lut = np.arange(scale(256, bitdepth)) | |
lut = np.sqrt(np.sin(np.clip((lut - scale(16, bitdepth)) / scale(219, bitdepth), 0, 1) * np.pi)) * scale(256, bitdepth) * strength | |
lut = np.clip(lut, 0, scale(256, bitdepth) - 1) | |
lut = lut.astype(int).tolist() | |
if masked: | |
if dmode==1: | |
bias = core.std.ShufflePlanes(resized_source, 0, vs.GRAY) | |
bias = core.std.Lut(bias, lut=lut) | |
bias = core.std.Expr([bias, mask], "x y min") | |
else: | |
bias = mask | |
repaired = core.std.MaskedMerge(resized_source, repaired, bias, planes=[0, 1, 2] if chroma else 0) if dmode == 1 or masked else repaired | |
resized_back = core.resize.Spline36(repaired, clip.width, clip.height) if ss != 1.0 else repaired | |
if not chroma: | |
if not isGray: | |
return core.std.ShufflePlanes([resized_back, u, v], [0]*3, vs.YUV) | |
return resized_back | |
def credits_mask(clip: vs.VideoNode, binarize_thresh_1=46080, binarize_thresh_2=23040, expand=4, temporal=False, temporal_low=0.001, | |
temporal_high=0.008, temporal_radius=4, temporal_step=1) -> vs.VideoNode: | |
""" | |
Useful to detect credits on a >720p clip. This is a basic function that attempts to detect credits by growing a binarized clip | |
into a resampled difference clip. The temporal mode is experimental right now so it is suggested that you do not use it. | |
""" | |
clip = core.std.ShufflePlanes(clip, 0, vs.GRAY) | |
binarized = core.std.Binarize(clip, binarize_thresh_1) | |
binarized = core.std.Maximum(clip) | |
rescaled = core.resize.Spline36(clip, 1280, 720) | |
rescaled = core.resize.Spline36(rescaled, clip.width, clip.height) | |
rescaled_diff = core.std.MakeDiff(clip, rescaled) | |
rescaled_diff = core.fmtc.bitdepth(bits=8) | |
rescaled_diff = core.hist.Luma(rescaled_diff) | |
rescaled_diff = core.fmtc.bitdepth(bits=16) | |
for i in range(expand): | |
rescaled_diff = core.std.Maximum(rescaled_diff) | |
rescaled_diff = core.std.Binarize(rescaled_diff, binarize_thresh_2) | |
def temporal_check(n, f, clip): | |
high = clip | |
current_frame_average = f.props.PlaneStatsAverage | |
for i in range(1,temporal_radius+1, temporal_step): | |
if n+i < clip.num_frames: | |
forward_frame_average = clip.get_frame(n+i).props.PlaneStatsAverage | |
plane_diff_forward = forward_frame_average - current_frame_average | |
if temporal_high > plane_diff_forward > temporal_low and forward_frame_average > current_frame_average: | |
high = clip[i:clip.num_frames-1] | |
temporal_tresh = plane_diff_forward | |
if n-i > 0: | |
backward_frame_average = clip.get_frame(n-i).props.PlaneStatsAverage | |
plane_diff_backward = backward_frame_average - current_frame_average | |
if temporal_high > plane_diff_backward > temporal_low and backward_frame_average > current_frame_average: | |
high = clip[0:i]+clip | |
temporal_tresh = plane_diff_backward | |
return high | |
hysteresis = core.misc.Hysteresis(binarized, rescaled_diff) | |
if not temporal: | |
return hysteresis | |
hysteresis = core.std.PlaneStats(hysteresis) | |
return core.std.FrameEval(hysteresis, functools.partial(temporal_check, clip=hysteresis), prop_src=hysteresis) | |
def bilatinpaint(diff: vs.VideoNode, mask: vs.VideoNode, ref: vs.VideoNode, subspl=None, limit=12288, chroma=True) -> vs.VideoNode: | |
""" | |
TODO: Optimize pretty much the entire function (Will do it soon) | |
Ported from _08's Avisynth script: https://github.com/Zeght/Morefun/blob/master/Morefun.avsi#L987 | |
This work a little differently but should have virtually little difference. | |
From _08's MoreFun repository: Function tries to interpolate data in msk area of diff by blurring it using ref clip as reference. | |
Most of processing is done on clips subsampled with subspl factor. | |
""" | |
if subspl is None: | |
if mask.width > 1280: | |
subspl = 3.0 | |
else: | |
subspl = 2.0 | |
w = mask.width | |
h = mask.height | |
ws = int(w/subspl/2)*2 | |
hs = int(h/subspl/2)*2 | |
mask_y = core.std.ShufflePlanes(mask, 0, vs.GRAY) | |
mask_y = core.resize.Bilinear(mask_y, ws, hs) | |
mask = mask_y | |
if chroma == True: | |
mask_uv = core.resize.Bilinear(mask_y, ws//2, hs//2, src_left=-0.25) | |
mask = core.std.ShufflePlanes([mask_y, mask_uv, mask_uv], [0,0,0], vs.YUV) | |
planes = [0,1,2] | |
else: | |
diff = core.std.ShufflePlanes(diff, 0, vs.GRAY) | |
ref = core.std.ShufflePlanes(ref, 0, vs.GRAY) | |
planes = [0] | |
diff_copy = diff | |
diff = core.resize.Bilinear(diff, ws, hs) | |
ref_copy = ref | |
ref = core.resize.Bilinear(ref, ws, hs) | |
binarized = core.std.Binarize(mask, 49152, v0=65535, v1=0, planes=planes) | |
blank = core.std.Expr([diff, binarized], "x 0 > y 0 > and x y min 0 ?") | |
def average(n, f, clip): | |
avg_blank_luma = f[0].props.PlaneStatsAverage * (1 << 16) - 1 | |
avg_binarized_luma = f[1].props.PlaneStatsAverage * (1 << 11) - 182 | |
if chroma: | |
avg_blank_u = f[2].props.PlaneStatsAverage * (1 << 16) - 1 | |
avg_binarized_u = f[3].props.PlaneStatsAverage * (1 << 11) - 102 | |
avg_blank_v = f[4].props.PlaneStatsAverage * (1 << 16) - 1 | |
avg_binarized_v = f[5].props.PlaneStatsAverage * (1 << 11) - 102 | |
return core.std.BlankClip(clip, color=[avg_blank_luma+avg_binarized_luma, avg_blank_u+avg_binarized_u, \ | |
avg_blank_v+avg_binarized_v]) | |
return core.std.BlankClip(clip, color=[avg_blank_luma+avg_binarized_luma]) | |
plane_clips = [] | |
for i in planes: | |
plane_clips.append(core.std.PlaneStats(core.std.ShufflePlanes(blank, i, vs.GRAY))) | |
plane_clips.append(core.std.PlaneStats(core.std.ShufflePlanes(binarized, i, vs.GRAY))) | |
blank = core.std.FrameEval(blank, functools.partial(average, clip=blank), plane_clips) | |
diff = core.std.MaskedMerge(diff, blank, mask, planes=planes) | |
diff = core.std.Expr(diff, "x 32768 "+ str(limit) +" + > 32768 "+ str(limit) +" + x ?") if (0 < limit < 32768) else diff | |
bilat = core.bilateral.Bilateral(diff, ref, sigmaS=min(hs, ws/5), sigmaR=0.10, planes=planes) | |
diff = core.std.MaskedMerge(diff, bilat, mask, planes=planes) | |
bilat = core.bilateral.Bilateral(diff, ref, sigmaS=min(hs, ws*2/13), sigmaR=0.02, planes=planes) | |
diff = core.std.MaskedMerge(diff, bilat, mask, planes=planes) | |
bilat = core.bilateral.Bilateral(diff, ref, sigmaS=ws/40, sigmaR=0.02, planes=planes) | |
diff = core.std.MaskedMerge(diff, bilat, mask, planes=planes) | |
diff = core.bilateral.Bilateral(diff, ref, sigmaS=max(2, ws/80), sigmaR=0.02, planes=planes) | |
diff = core.resize.Bilinear(diff, w, h) | |
diff = core.bilateral.Bilateral(diff, ref=ref_copy, sigmaS=2.0, sigmaR=0.02) | |
return core.std.AddDiff(diff_copy, diff) if not chroma else diff | |
def kaze_mask(clip): | |
""" | |
Ported from an Avisynth script made by ???. It generates a hardsub mask. | |
""" | |
clip = core.std.ShufflePlanes(clip, 0, vs.GRAY) | |
clip = core.rgvs.RemoveGrain(clip, 3) | |
low = core.std.Binarize(clip, 56320) | |
high = core.std.Binarize(clip, 12999, v0=65535, v1=0) | |
for i in range(3): | |
low = core.std.Maximum(low) | |
high = core.std.Maximum(high) | |
sub_mask = core.std.Expr([low, high], "x y min") | |
matrix = [1] * 25 | |
sub_mask = core.std.Convolution(sub_mask, matrix, divisor=25) | |
sub_mask = core.std.Convolution(sub_mask, matrix, divisor=25) | |
sub_mask = core.std.Expr(sub_mask, "x 1000 - 8 *") | |
sub_mask = core.std.Convolution(sub_mask, matrix, divisor=25) | |
sub_mask = core.std.Convolution(sub_mask, matrix, divisor=25) | |
sub_mask = core.std.Expr(sub_mask, "x 3 *") | |
return sub_mask | |
def dehardsub(hardsubbed, ref, expand=0): | |
""" | |
This is plainly a wrapper around bilatinpaint and kazeMask and aims to "dehardsub" a video by merging the subbed parts of | |
the hardsubbed clip with the reference clip. | |
""" | |
blurred_ref = core.dfttest.DFTTest(ref, ftype=1, sigma=20) | |
mask = kaze_mask(hardsubbed) | |
for i in range(expand): | |
mask = core.std.Maximum(mask) | |
diff = core.std.MakeDiff(hardsubbed, blurred_ref) | |
diff = bilatinpaint(diff, mask, blurred_ref) | |
ref_shifted = core.std.MergeDiff(blurred_ref, diff) | |
return core.std.MaskedMerge(hardsubbed, ref_shifted, mask, first_plane=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please move your documentation inside the function. Do it like this.