Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/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}"
@DavidOliver
Copy link

DavidOliver commented Jul 9, 2016

Hi @anguyen8. Thanks for this script.

Between each crossfade there is a pause of a second or two where the image is shown. How can I remove this please? I'm trying to fade multiple eight-second exposures of polar lights into each other to improve the sense of slowish movement.

Edit: I'm now accomplishing what I need to do with G'MIC.

@jeffehobbs
Copy link

jeffehobbs commented Aug 20, 2016

Great script! Really helps with those complicated ffmpeg filter config statements.

@anguyen8
Copy link
Author

anguyen8 commented Sep 21, 2016

@DavidOliver: probably you got your solution by now. But I'd say it's the crossfade of 0.9 that you can tweak.

@kosar
Copy link

kosar commented Oct 1, 2016

@DavidOliver: I would appreciate if you could post how you did this in G'MIC. As far as I can tell it is not easy to do. I tried posting a question to their Google Group.
@anguyen8: I did something similar to this using Powershell -- I will try this now, using a *nix system.
EDIT: here is my super early super hacky version of this in Powershell. https://github.com/kosar/miv

@hasnhasan
Copy link

hasnhasan commented Jan 31, 2017

I'm getting an error. Could you help ?

Invalid file index 5 in filtergraph description [1:v][0:v]blend=all_expr='A*(if(gte(T,0.9),1,T/0.9))+B*(1-(if(gte(T,0.9),1,T/0.9)))'[b1v]; [2:v][1:v]blend=all_expr='A*(if(gte(T,0.9),1,T/0.9))+B*(1-(if(gte(T,0.9),1,T/0.9)))'[b2v]; [3:v][2:v]blend=all_expr='A*(if(gte(T,0.9),1,T/0.9))+B*(1-(if(gte(T,0.9),1,T/0.9)))'[b3v]; [4:v][3:v]blend=all_expr='A*(if(gte(T,0.9),1,T/0.9))+B*(1-(if(gte(T,0.9),1,T/0.9)))'[b4v]; [5:v][4:v]blend=all_expr='A*(if(gte(T,0.9),1,T/0.9))+B*(1-(if(gte(T,0.9),1,T/0.9)))'[b5v]; [0:v][b1v][1:v][b2v][2:v][b3v][3:v][b4v][4:v]concat=n=9:v=1:a=0,format=yuv420p[v].

@iBobik
Copy link

iBobik commented Mar 1, 2017

On line 46 I changed time to -t 15, so one image will be there shown for 15 seconds, but it prints very long output like this:

[Parsed_blend_5 @ 0x7fcdff606900] [framesync @ 0x7fcdff606ec8] Buffer queue overflow, dropping.
[Parsed_concat_8 @ 0x7fcdff609660] Buffer queue overflow, dropping.
[Parsed_blend_5 @ 0x7fcdff606900] [framesync @ 0x7fcdff606ec8] Buffer queue overflow, dropping.
[Parsed_concat_8 @ 0x7fcdff609660] Buffer queue overflow, dropping.
[Parsed_blend_5 @ 0x7fcdff606900] [framesync @ 0x7fcdff606ec8] Buffer queue overflow, dropping.
[Parsed_concat_8 @ 0x7fcdff609660] Buffer queue overflow, dropping.

Do you know how to fix it?

@MotionDesignStudio
Copy link

MotionDesignStudio commented Apr 10, 2017

How can I control the display length of time for each image? Changing this input+=" -loop 1 -t 1 -i $f" to input+=" -loop 1 -t 15 -i $f" only makes the second image display 30 seconds and the third 45 seconds. So it adds 15 seconds to each image.

@KalpeshJadvani
Copy link

KalpeshJadvani commented Apr 25, 2017

can you tell me how i can add mp3 file in this command ... plz i have done this script but i could not add mp3 file in this complex command ....

@ZuzooVn
Copy link

ZuzooVn commented Jul 15, 2017

@anguyen8: can you add more animation for ffmpeg slideshow?

@spiralofhope
Copy link

spiralofhope commented Apr 20, 2018

The URL can be shortened:

http://superuser.com/questions/833232/create-video-with-5-images-with-fadein-out-effect-in-ffmpeg/1071748#1071748
=>
http://superuser.com/questions/833232#1071748

@gcrowder1
Copy link

gcrowder1 commented Dec 7, 2018

Thank you friends, your script successfully solved my problem. If the image size is different, an error will occur. But the problem is not big, I have processed the image to the same size. Thanks again

@shakram02
Copy link

shakram02 commented Apr 24, 2019

Thank you so much, worked like a charm 🙏.
I converted it to python if anyone is interested, here

@Butateng
Copy link

Butateng commented Nov 12, 2019

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!

@MrMegamind
Copy link

MrMegamind commented Apr 13, 2020

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

@rossomaltese
Copy link

rossomaltese commented Oct 18, 2022

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

anguyen8 commented Oct 18, 2022

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

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