Skip to content

Instantly share code, notes, and snippets.

@rlaphoenix
Last active April 2, 2023 05:04
Show Gist options
  • Save rlaphoenix/03ef3b8342d675dfd33616af499684e0 to your computer and use it in GitHub Desktop.
Save rlaphoenix/03ef3b8342d675dfd33616af499684e0 to your computer and use it in GitHub Desktop.
Add Soft Pulldown manually to any clip using VapourSynth
def software_pulldown(clip: vs.VideoNode, *pattern: list[int], tff: bool = True) -> vs.VideoNode:
"""
Add Software Pulldown by interlacing only where needed (aka Soft Pulldown).
No data is lost in the process. All frames interlaced by Pulldown would be
duplicate data as it uses n for the top field, and n-1 for the bottom field.
Parameters:
clip: Clip to apply pulldown to.
*args: The pulldown pattern to apply. Each integer is how many fields to
return for each frame. E.g., (2, 3) will return 2 fields for frame n
and 3 fields for frame n+1.
tff: Use top-field-first if field order information is not available.
"""
fields_out = sum(pattern)
new_fps = clip.fps_num + (clip.fps_num // (fields_out - 1))
factor = (new_fps / clip.fps_den) * (clip.fps_den / clip.fps_num)
temp_clip = clip.std.BlankClip(length=math.floor(len(clip) * factor), fpsnum=new_fps, fpsden=clip.fps_den)
def _change_fps(n: int, c: vs.VideoNode, tc: vs.VideoNode) -> vs.VideoNode:
real_n = math.floor(n / factor)
next_real_n = math.floor((n + 1) / factor)
duplicate = next_real_n == real_n
current_frame = c[real_n]
if duplicate and n != 0:
# Apply Pulldown to duplicate frames
prev_frame = c[real_n - 1]
current_frame = core.std.SetFieldBased(current_frame, value=0)
prev_frame = core.std.SetFieldBased(prev_frame, value=0)
current_fields = core.std.SeparateFields(current_frame * (len(c) + 100), tff)
prev_fields = core.std.SeparateFields(prev_frame * (len(c) + 100), tff)
current_frame = Weave(
prev_fields[math.floor(n * 2)] + # takes first field from previous frame
current_fields[math.floor(n * 2) + 1], # take last field from current frame
tff
)
return current_frame
clip = core.std.FrameEval(temp_clip, functools.partial(_change_fps, c=clip, tc=temp_clip))
return clip
clip = software_pulldown(clip, 3, 2, tff=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment