Skip to content

Instantly share code, notes, and snippets.

@astiob
Created May 30, 2024 20:42
Show Gist options
  • Save astiob/9e687c04d2e712fed930318b1a9b00b5 to your computer and use it in GitHub Desktop.
Save astiob/9e687c04d2e712fed930318b1a9b00b5 to your computer and use it in GitHub Desktop.
VapourSynth script for converting interlaced fades over telecined content to pure-progressive fades
import vapoursynth as vs
from vsdeinterlace.combing import vinverse
__all__ = (
'smooth_stable_fade',
'unfade_unique_into_repeating',
'unfade_repeating_into_unique',
'fade_unique_into_repeating',
'fade_repeating_into_unique',
'fade_repeating_into_repeating',
)
c = vs.core
def smooth_stable_fade_fields(clip, first, last, triple_field_index, extra_clip=None, merge_expr=None, unique_expr=None):
end = last + 1
clip = clip.std.SeparateFields()
first *= 2
end *= 2
offset = 0
if first:
match triple_field_index:
case 3:
clip = c.std.Splice([clip[:first], clip[first - 2], clip[first + 1:]])
offset = -1
case 4:
offset = 1
first -= offset
triple_field_index = (triple_field_index + offset) % 5
n = end - first
match (n - triple_field_index) % 5:
case 1:
end -= 1
case 2:
if end < clip.num_frames:
clip = c.std.Splice([clip[:end - 2], clip[end], clip[end - 1:]])
end -= 2
fade = clip[first:end].akarin.PropExpr(lambda: {'FadeFieldIndex': f'N {offset} -'})
if extra_clip:
extra_clip = extra_clip[first:end]
a = triple_field_index
b = a + 2
if merge_expr is None:
mean = c.std.Merge(fade[a::5], fade[b::5])
elif extra_clip:
mean = c.akarin.Expr([fade[a::5], fade[b::5], extra_clip[a::5], extra_clip[b::5]], merge_expr)
else:
mean = c.akarin.Expr([fade[a::5], fade[b::5]], merge_expr)
mean = mean.akarin.PropExpr(lambda: {'FadeFieldIndex': f'N 5 * {a + 1 - offset} +'})
if unique_expr is None:
cycle = [mean if i in (a, b) else fade[i::5] for i in range(5)]
elif extra_clip:
cycle = [mean if i in (a, b) else c.akarin.Expr([fade[i::5], extra_clip[i::5]], unique_expr) for i in range(5)]
else:
cycle = [mean if i in (a, b) else fade[i::5].akarin.Expr(unique_expr) for i in range(5)]
return c.std.Splice([
clip[:first],
c.std.Interleave(cycle),
clip[end:],
])
def smooth_stable_fade(clip, first, last, triple_field_index):
return smooth_stable_fade_fields(clip, first, last, triple_field_index).std.DoubleWeave()[::2]
def unfade_unique_into_repeating(clip, first, last, repeating_clip, triple_field_index=None):
end = last + 1
n = (end - first) * 2
right = repeating_clip
right = (right * (clip.num_frames // right.num_frames + 2))[-end % right.num_frames:][:clip.num_frames]
if triple_field_index is None:
return c.akarin.Expr([clip, right], f'N {first} - 2 * 1 + Y 2 % + I! x {n} * y I@ * - {n} I@ - /')
else:
right = c.akarin.PropExpr([right, clip], lambda: {'_FieldBased': 'y._FieldBased'})
right = right.std.SeparateFields()
# NB: may alter one additional field preceding {first}
clip = smooth_stable_fade_fields(clip, first, last, triple_field_index,
extra_clip=right,
merge_expr=f'''
x.FadeFieldIndex 1 + I!
y.FadeFieldIndex 1 + J!
{n} x * I@ z * - {n} I@ - *
{n} y * J@ a * - {n} J@ - * +
{n} I@ - dup *
{n} J@ - dup * +
/
''',
unique_expr=f'x.FadeFieldIndex 1 + I! x {n} * y I@ * - {n} I@ - /',
)
return clip.std.DoubleWeave()[::2]
def unfade_repeating_into_unique(clip, first, last, repeating_clip, triple_field_index=None):
end = last + 1
n = (end - first) * 2
left = repeating_clip
left = (left * (clip.num_frames // left.num_frames + 2))[-first % left.num_frames:][:clip.num_frames]
if triple_field_index is None:
return c.akarin.Expr([left, clip], f'N {first} - 2 * 1 + Y 2 % + I! y {n} * x {n} I@ - * - I@ /')
else:
left = c.akarin.PropExpr([left, clip], lambda: {'_FieldBased': 'y._FieldBased'})
left = left.std.SeparateFields()
# NB: may alter one additional field preceding {first}
clip = smooth_stable_fade_fields(clip, first, last, triple_field_index,
extra_clip=left,
merge_expr=f'''
x.FadeFieldIndex 1 + I!
y.FadeFieldIndex 1 + J!
{n} x * {n} I@ - z * - I@ *
{n} y * {n} J@ - a * - J@ * +
I@ dup *
J@ dup * +
/
''',
unique_expr=f'x.FadeFieldIndex 1 + I! x {n} * y {n} I@ - * - I@ /',
)
return clip.std.DoubleWeave()[::2]
def fade_unique_into_repeating(clip, first, last, repeating_clip, delay: float, protect_top=0, protect_bottom=0):
end = last + 1
right = repeating_clip
right = (right * (clip.num_frames // right.num_frames + 2))[-end % right.num_frames:][:clip.num_frames]
# Some top & bottom pixels of this flashback are 2px-per-band gradients.
# Vinverse happily smooths them out to 1px per band,
# but that contrasts with the non-crossfade frames, so avoid that.
stack = []
if protect_top:
stack.append(clip.std.Crop(bottom = clip.height - protect_top))
stack.append(vinverse(clip).std.Crop(top=protect_top, bottom=protect_bottom))
if protect_bottom:
stack.append(clip.std.Crop(top = clip.height - protect_bottom))
clip = c.std.StackVertical(stack)
return c.akarin.Expr([clip, right], f'N {first - 1 + delay} - {end - first} / A! x 1 A@ - * y A@ * +')
def fade_repeating_into_unique(clip, first, last, repeating_clip, delay: float, protect_top=0, protect_bottom=0):
end = last + 1
left = repeating_clip
left = (left * (clip.num_frames // left.num_frames + 2))[-first % left.num_frames:][:clip.num_frames]
# Some top & bottom pixels of this flashback are 2px-per-band gradients.
# Vinverse happily smooths them out to 1px per band,
# but that contrasts with the non-crossfade frames, so avoid that.
stack = []
if protect_top:
stack.append(clip.std.Crop(bottom = clip.height - protect_top))
stack.append(vinverse(clip).std.Crop(top=protect_top, bottom=protect_bottom))
if protect_bottom:
stack.append(clip.std.Crop(top = clip.height - protect_bottom))
clip = c.std.StackVertical(stack)
return c.akarin.Expr([left, clip], f'N {first - 1 + delay} - {end - first} / A! x 1 A@ - * y A@ * +')
def fade_repeating_into_repeating(clip, first, last, left, right, delay: float):
end = last + 1
left = (left * (clip.num_frames // left.num_frames + 2))[-first % left.num_frames:][:clip.num_frames]
right = (right * (clip.num_frames // right.num_frames + 2))[-end % right.num_frames:][:clip.num_frames]
return c.akarin.Expr([left, right], f'N {first - 1 + delay} - {end - first} / A! x 1 A@ - * y A@ * +')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment