Skip to content

Instantly share code, notes, and snippets.

@shakram02
Last active January 17, 2020 22:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shakram02/04f2485d67ec6cc37da6c55681cab407 to your computer and use it in GitHub Desktop.
Save shakram02/04f2485d67ec6cc37da6c55681cab407 to your computer and use it in GitHub Desktop.
Fade consecutive frames using ffmpeg and python a modified clone of https://gist.github.com/anguyen8/d0630b6aef6c1cd79b9a1341e88a573e
import os
import math
import sys
import glob
import subprocess
import time
from subprocess import Popen
from os.path import join
from subprocess import PIPE
# https://gist.github.com/anguyen8/d0630b6aef6c1cd79b9a1341e88a573e
def execute_subprocess(cmd, ignore_log=None):
""" Executes a given cmd and reads the output and error streams """
# https://crashcourse.housegordon.org/python-subprocess.html
if ignore_log is None:
ignore_log = True
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
(out, err) = p.communicate()
if ignore_log and p.returncode == 0:
return p, out.decode(), err.decode()
if p.returncode == 0:
print("command '{}' succeeded, returned: {}".format(cmd, out.decode()))
elif p.returncode <= 125:
print("command '{}' failed, exit-code={} \nerror = \n{}".format(
cmd, p.returncode, err.decode()))
elif p.returncode == 127:
print("program '{}' not found: {}".format(cmd, err.decode()))
else:
# Things get hairy and unportable - different shells return
# different values for coredumps, signals, etc.
sys.exit("'{}' likely crashed, shell returned code {}".format(
cmd, p.returncode))
return (p, out.decode(), err.decode())
# you can use this to round floating point numbers
def round_up(n, decimals=0):
multiplier = 10**decimals
return math.ceil(n * multiplier) / multiplier
def make_command(input_files, crossfade_duration, output_path):
filters = ""
output = "[0:v]"
in_command = ""
len_files = len(input_files)
is_background = False
for i, file in enumerate(input_files):
frame_duration = 1
# input+=" -loop 1 -t 1 -i $f"
in_command += " -loop 1 -t {} -i {}".format(frame_duration, file)
frame_num = i + 1
if i != len_files - 1:
# filters+=" [${next}:v][${i}:v]blend=all_expr='A*(if(gte(T,${crossfade}),1,T/${crossfade}))+B*(1-(if(gte(T,${crossfade}),1,T/${crossfade})))'[b${next}v];"
filters += " [{}:v][{}:v]blend=all_expr='".format(frame_num, i)
filters += "A*(if(gte(T,{0}),1,T/{0}))".format(crossfade_duration)
filters += "+B*(1-(if(gte(T,{0}),1,T/{0})))'".format(
crossfade_duration)
filters += "[b{}v];".format(frame_num)
if i > 0:
# output+="[b${i}v][${i}:v]"
output += "[b{0}v][{0}:v]".format(i)
# output+="concat=n=$((i * 2 - 1)):v=1:a=0,format=yuv420p[v]\" -map \"[v]\" ${output_file}"
frame_count = len_files * 2 - 1
output += "concat=n={}:v=1:a=0,format=yuv420p[v]\"".format(frame_count)
output += " -map \"[v]\" -y {}".format(output_path)
command = "ffmpeg -r 10 {} -filter_complex \"{} {}".format(
in_command, filters, output)
return command
def main():
im_format = "png"
input_path = sys.argv[1]
cross_fade_duration = 0.3
out_name = "faded.mp4"
files = sorted(glob.glob("{}/*.{}".format(input_path, im_format)))
out_path = join(input_path, out_name)
command = make_command(files, cross_fade_duration, out_path)
execute_subprocess(command, ignore_log=False)
if __name__ == "__main__":
main()
@abishur
Copy link

abishur commented Sep 5, 2019

I have a folder with a lot of pictures in it, to the point where the batch script of this code I was using failed saying the input was too long (windows machine). Since the subprocess you're using has shell=true does that mean I'd have the same issue?

@shakram02
Copy link
Author

can you please explain this Since the subprocess you're using has shell=true does that mean I'd have the same issue? ? I don't think I'm getting it right.

Maybe one solution is to split the files into batches (e.g. each 100 files together) then join the output of each batch together to a final video

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment