Last active
April 2, 2023 05:04
-
-
Save rlaphoenix/03ef3b8342d675dfd33616af499684e0 to your computer and use it in GitHub Desktop.
Add Soft Pulldown manually to any clip using VapourSynth
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
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