-
-
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}" |
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?
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.
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 ....
@anguyen8: can you add more animation for ffmpeg slideshow?
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
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
Thank you so much, worked like a charm 🙏.
I converted it to python if anyone is interested, here
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!
Guys thank you so much, can I attach bg voice or I have to attach later in an other command?
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"
Thanks @rossomaltese for sharing with us your updated/improved version!! :)
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 ...
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?
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? :/
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:
- pictures must have the same dimensions (width and height)
- 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.
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
If you have some hundred images, your memory will exhaust. I created a version which combines every 2 images and at the end, the resulting videos are combined into one:
#!/bin/bash
# make a crossfade transition for every 2 images
# jm schlatter 2024
# parms
if (($# != 3))
then
echo "usage: $0 time[s] crossfade[s] outputvideoname"
exit 1
else
time=$1
crossfade=$2 # Crossfade duration between two images
output_file="$3" # Name of output video
fi
if (( $(echo "$time <= 0" | bc -l) )); then
time=1
fi
files=(*.jpg) # Change the file type to the type of your images
for ((i = 1; i < ${#files[*]}; ++i)); do
ffmpeg -hide_banner -loop 1 -t $time -i "${files[i-1]}" -loop 1 -t $time -i "${files[i]}" -filter_complex \
"[1:v][0:v]blend=all_expr='A*(if(gte(T,$crossfade),1,T/$crossfade))+B*(1-(if(gte(T,$crossfade),1,T/$crossfade)))'[b1v];\
[0:v][b1v][1:v] \
concat=n=3:v=1:a=0,format=yuv420p[v]"\
-map '[v]' /tmp/output_$i.mp4
done
ffmpeg -f concat -safe 0 -i <(for f in $('ls' -1rt /tmp/output_*.mp4); do echo "file '$f'"; done) -c copy "$output_file.mp4"
@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.
Sorry for the somewhat delayed reply.
Hi Anguyen, FYI I've added parameters for the "same input size" problem, lowered memory requirements, a longer lead-in and last frame (parameterized, too), it's my my fork of the gist.
thanks for sharing this you#ve saved me lots of time :-)
That's wonderful! Thank you for sharing your ver. @froh ! ❤️
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].