Skip to content

Instantly share code, notes, and snippets.

@awojnowski
Created May 12, 2023 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save awojnowski/1781c2aca5cf99367fa75cdd6414bb42 to your computer and use it in GitHub Desktop.
Save awojnowski/1781c2aca5cf99367fa75cdd6414bb42 to your computer and use it in GitHub Desktop.
Generate slideshow from photos using FFMPEG
import natsort
import os
from PIL import Image
import sys
INPUT_DIR = "/Users/aaron/Desktop/pictures"
OUTPUT_DIR = "/Users/aaron/Desktop/pictures-processed"
OUTPUT_WIDTH = 1920
OUTPUT_HEIGHT = 1080
FFMPEG_IMAGE_DURATION = 5
def fetch_sorted_files(dirname):
files = natsort.natsorted(os.listdir(dirname))
return files
if __name__=="__main__":
# clear the output directory
for file in os.listdir(OUTPUT_DIR):
file_path = os.path.join(OUTPUT_DIR, file)
os.remove(file_path)
# process the images
input_files = fetch_sorted_files(INPUT_DIR)
idx = 0
for file in input_files:
if ".DS_Store" in file:
continue
input_path = os.path.join(INPUT_DIR, file)
output_path = os.path.join(OUTPUT_DIR, "{}.jpg".format(idx))
image = Image.open(input_path)
image_width = float(image.size[0])
image_height = float(image.size[1])
scaling_factor = min(OUTPUT_WIDTH / image_width, OUTPUT_HEIGHT / image_height)
image_width *= scaling_factor
image_height *= scaling_factor
image = image.resize((int(image_width), int(image_height)), Image.Resampling.LANCZOS)
output_image = Image.new("RGB", (OUTPUT_WIDTH, OUTPUT_HEIGHT), color="black")
output_image.paste(image, (
int((OUTPUT_WIDTH - image_width) / 2),
int((OUTPUT_HEIGHT - image_height) / 2)
))
output_image.save(output_path, format="jpeg")
idx += 1
# print the command
print("ffmpeg \\")
output_files = fetch_sorted_files(OUTPUT_DIR)
for file in output_files:
file_path = os.path.join(OUTPUT_DIR, file)
print("-i \"{}\" \\".format(file_path))
print("-filter_complex \"\\")
for idx in range(0, len(output_files)):
line = "[{}]scale={}:-2,setsar=1:1[out];[out]crop={}:{}[out];[out]scale=8000:-1,zoompan=z='zoom+0.0005':x=iw/2-(iw/zoom/2):y=ih/2-(ih/zoom/2):d={}:s={}x{}:fps=25".format(idx, OUTPUT_WIDTH, OUTPUT_WIDTH, OUTPUT_HEIGHT, 25 * FFMPEG_IMAGE_DURATION, OUTPUT_WIDTH, OUTPUT_HEIGHT)
if idx > 0:
line += ",fade=d=1:t=in:alpha=1,setpts=PTS-STARTPTS+{}/TB".format(idx * FFMPEG_IMAGE_DURATION - 1)
line += "[f{}];\\".format(idx)
print(line)
idx = 0
output_string = "[f0]"
for idx in range(1, len(output_files)):
if idx > 1:
output_string += "[bg{}]".format(idx - 1)
output_string += "[f{}]overlay".format(idx)
if idx + 1 < len(output_files):
output_string += "[bg{}],".format(idx)\
print("{},format=yuv420p[v]\" \\".format(output_string))
print("-map \"[v]\" -r 25 output.mp4")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment