Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@anguyen8
Last active March 18, 2024 13:10
Show Gist options
  • Star 57 You must be signed in to star a gist
  • Fork 17 You must be signed in to fork a gist
  • Save anguyen8/d0630b6aef6c1cd79b9a1341e88a573e to your computer and use it in GitHub Desktop.
Save anguyen8/d0630b6aef6c1cd79b9a1341e88a573e to your computer and use it in GitHub Desktop.
#!/bin/bash
# Anh Nguyen <anh.ng8@gmail.com>
# 2016-04-30
# MIT License
# This script takes in same-size images from a folder and make a crossfade video from the images using ffmpeg.
# Make sure you have ffmpeg installed before running.
# The output command looks something like the below, but for as many images as you have in the folder.
# See the answer by LordNeckbeard at:
# http://superuser.com/questions/833232/create-video-with-5-images-with-fadein-out-effect-in-ffmpeg/1071748#1071748
#
#
# ffmpeg \
# -loop 1 -t 1 -i 001.png \
# -loop 1 -t 1 -i 002.png \
# -loop 1 -t 1 -i 003.png \
# -loop 1 -t 1 -i 004.png \
# -loop 1 -t 1 -i 005.png \
# -filter_complex \
# "[1:v][0:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b1v]; \
# [2:v][1:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b2v]; \
# [3:v][2:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b3v]; \
# [4:v][3:v]blend=all_expr='A*(if(gte(T,0.5),1,T/0.5))+B*(1-(if(gte(T,0.5),1,T/0.5)))'[b4v]; \
# [0:v][b1v][1:v][b2v][2:v][b3v][3:v][b4v][4:v]concat=n=9:v=1:a=0,format=yuv420p[v]" -map "[v]" out.mp4
#----------------------------------------------------------------
# SETTINGS
input_dir="/path/to/your/folder" # Replace this by a path to your folder /path/to/your/folder
n_files=10 # Replace this by a number of images
files=`ls ${input_dir}/*.jpg | head -${n_files}` # Change the file type to the correct type of your images
output_file="video.mp4" # Name of output video
crossfade=0.9 # Crossfade duration between two images
#----------------------------------------------------------------
# Making an ffmpeg script...
input=""
filters=""
output="[0:v]"
i=0
for f in ${files}; do
input+=" -loop 1 -t 1 -i $f"
next=$((i+1))
if [ "${i}" -ne "$((n_files-1))" ]; then
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];"
fi
if [ "${i}" -gt "0" ]; then
output+="[b${i}v][${i}:v]"
fi
i=$((i+1))
done
output+="concat=n=$((i * 2 - 1)):v=1:a=0,format=yuv420p[v]\" -map \"[v]\" ${output_file}"
script="ffmpeg ${input} -filter_complex \"${filters} ${output}"
echo ${script}
# Run it
eval "${script}"
@MrMegamind
Copy link

Guys thank you so much, can I attach bg voice or I have to attach later in an other command?

@rossomaltese
Copy link

Thanks @anguyen8 for the script, it worked like a charm!
I post a little patch I did to manage some ffmpeg parameters, remove some useless brackets and refactor the main loop a little bit; I mainly did it to better understand the ffmpeg use, so all the kudos go to you! ;-)
With this patch, the script uses all the .jpg files in the current directory.
NOTE: all the images must have the same size.

--- make_crossfade_ffmpeg_video_from_images.sh.orig	2022-10-18 11:11:33.851470982 +0200
+++ make_crossfade_ffmpeg_video_from_images.sh	2022-10-18 11:40:52.239914716 +0200
@@ -4,7 +4,7 @@
 # 2016-04-30
 # MIT License
 
-# This script takes in images from a folder and make a crossfade video from the images using ffmpeg.
+# This script takes in all .jpg from current folder and make a crossfade video from the images using ffmpeg.
 # Make sure you have ffmpeg installed before running.
 
 # The output command looks something like the below, but for as many images as you have in the folder.
@@ -27,40 +27,42 @@
 
 #----------------------------------------------------------------
 # SETTINGS
-input_dir="/path/to/your/folder"  # Replace this by a path to your folder /path/to/your/folder
-n_files=10                        # Replace this by a number of images
-files=`ls ${input_dir}/*.jpg | head -${n_files}`  # Change the file type to the correct type of your images
-output_file="video.mp4"           # Name of output video
-crossfade=0.9                     # Crossfade duration between two images
+if (($# != 3)); then
+  echo "usage: $0 time[s] crossfade[s] output"
+  exit 1
+fi
+files=(*.jpg)                     # Change the file type to the correct type of your images
+output_file="$3"                  # Name of output video
+crossfade=$2                      # Crossfade duration between two images
+time=$1
+
+if [[ -e $output_file ]]; then
+  echo "$0: file $output_file already exists." >&2
+  exit 2
+fi
 #----------------------------------------------------------------
 
 # Making an ffmpeg script...
-input=""
-filters=""
-output="[0:v]"
-
-i=0
-
-for f in ${files}; do
-  input+=" -loop 1 -t 1 -i $f"
-
-  next=$((i+1))
-  if [ "${i}" -ne "$((n_files-1))" ]; then
-    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];"
-  fi
-
-  if [ "${i}" -gt "0" ]; then
-    output+="[b${i}v][${i}:v]"
-  fi
-
-  i=$((i+1))
+input="-hide_banner"
+for ((i = 0; i < ${#files[*]}; ++i)); do
+  input+=" -loop 1 -t $time -i "\'"${files[i]}"\'
 done
 
-output+="concat=n=$((i * 2 - 1)):v=1:a=0,format=yuv420p[v]\" -map \"[v]\" ${output_file}"
+filters=`mktemp`
+expr="if(gte(T,$crossfade),1,T/$crossfade)"
+output="[0:v]"
+{
+  for ((i = 1; i < ${#files[*]}; ++i)); do
+    echo "[$i:v][$((i-1)):v]blend=all_expr='A*($expr)+B*(1-($expr))'[b${i}v];"
+    output+="[b${i}v][$i:v]"
+  done
+  echo "$output"
+  echo "concat=n=$((${#files[*]} * 2 - 1)):v=1:a=0,format=yuv420p[v]"
+} >> $filters
 
-script="ffmpeg ${input} -filter_complex \"${filters} ${output}"
+script="ffmpeg $input -filter_complex_script $filters -map '[v]' "\'"$output_file"\'
 
-echo ${script}
+echo "$script"
 
 # Run it
-eval "${script}"
\ No newline at end of file
+eval "$script"

@anguyen8
Copy link
Author

Thanks @rossomaltese for sharing with us your updated/improved version!! :)

@martin3000
Copy link

martin3000 commented Jan 27, 2023

I have 30 jpgs and the script eats up all of the 8 GB system memory and then the machine crashes.
So I do

script="systemd-run --user --scope -p MemoryMax=2G -p MemorySwapMax=2G ffmpeg  ...

@HemangiVekaria
Copy link

Hey guys, I just wanna asked if this is feasible with ffmpeg. https://trello-attachments.s3.amazonaws.com/545a93cff0b222f456c5ad82/5d8cfc0d75e80e63dae2fe6a/5a523ed328f24b0454c5c56cd0ee68e6/23442279.mp4

I've been googling around finding on how to have smooth easing and effects on the video link above. I am really new to ffmpeg, please please yung help will be really meant a lot. Cheers!

Hello, Did you get any solution for this? Able to do animation like this?

@iRGBit
Copy link

iRGBit commented Mar 17, 2024

thanks for the work @anguyen8 and @rossomaltese
i have been trying both your versions and have ended up with 0MB .mp4s unfortunately... although a simple slideshow with ffmpeg like
ffmpeg -framerate 1 -pattern_type glob -i '*.jpg' -c:v libx264 -r 30 -pix_fmt yuv420p output.mp4
worked perfectly...
any hints/ideas where the error could lie? :/

@rossomaltese
Copy link

thanks for the work @anguyen8 and @rossomaltese i have been trying both your versions and have ended up with 0MB .mp4s unfortunately... although a simple slideshow with ffmpeg like ffmpeg -framerate 1 -pattern_type glob -i '*.jpg' -c:v libx264 -r 30 -pix_fmt yuv420p output.mp4 worked perfectly... any hints/ideas where the error could lie? :/

I retried executing the updated version and worked for me.
Two hints:

  1. pictures must have the same dimensions (width and height)
  2. there must not be blanks in the files name (or double quote each occurrence of variable files in the script).

Also, please take care of @martin3000 comment.

@rossomaltese
Copy link

For the ones who asked for adding a soundtrack, I did using something like that:

ffmpeg -i output_from_the_script.mp4 -itsoffset 00:00:17 -i soundtrack.mp3 -c copy -map 0:v:0 -map 1:a:0 output_with_sound.mp4

-itsoffset is the time at which soundtrack must start playing.

HTH

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