script to use ffmpeg to animate images with the "Ken Burns Effect"

interpretive language scripts


Moderator: Forum moderators

Post Reply
HerrBert
Posts: 364
Joined: Mon Jul 13, 2020 6:14 pm
Location: Germany, NRW
Has thanked: 19 times
Been thanked: 135 times

script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by HerrBert »

What it's about:

Especially after christmas i was 'spammed' with a lot of photos by some family members, asking if i could make a video-slide-show of them. Of course, i can. Problem is that nowadays most people take photos with a mobile phone in portrait mode...

Add black borders to fill vertical hd to horizontal hd :/ The result is more than dissapointing.

I recall on my imac there was a screensaver zooming and panning a picture. Did some search on the internet and found it is called the Ken-Burns-Effect. Further searching led to some few results for using ffmpeg on linux to achieve this animation effect.
Threw together a script to get what i wanted.

Not at least because of the few search results i thought about posting a script to give some more advanced options to use the zoompan filter of ffmpeg (not only in Puppy Linux...).

Code: Select all

#!/bin/bash

# Abhängigkeiten: bc, ffprobe, ffmpeg, urxvt, mtpaint (optional)

GEOMETRY=1280x720
TIME=5
FPS=30
INTRO=1
STILL=1
ACC="time"

[ -e "$1" ] && INFILE="$1" && shift # dnd

# rudimental:
chkarg() { [ "$(tr -d [:digit:]$3 <<< "$2")" = "" ] || { echo "invalid argument to option: $1 $2"; DRYRUN="error"; }; }

while [ "$1" ]; do
	case $1 in
		--debug) set -x; HOLD="-hold";;
		-i) [ "$2" ] && shift && INFILE="$1";;
		-o) [ "$2" ] && shift && OUTFILE="$1";;
		-fps) chkarg "$1" "$2" "" && shift && FPS=$1;;
		-d) chkarg "$1" "$2" "" && shift && TIME=$1;;
		-u) chkarg "$1" "$2" "" && shift && UPSCALE="$1";;
		-p) chkarg "$1" "$2" "" && shift && INTRO=$1;;
		-w) chkarg "$1" "$2" "" && shift && STILL=$1;;
		-f) chkarg "$1" "$2" "" && shift && F_IN="$1";;
		-b) chkarg "$1" "$2" "" && shift && F_OUT="$1";;
		-x|-x1) chkarg "$1" "$2" "-" && shift && X1="$1";;
		-x2) chkarg "$1" "$2" "-" && shift && X2="$1";;
		-y|-y1) chkarg "$1" "$2" "" && shift && Y1=$1;;
		-y2) chkarg "$1" "$2" "" && shift && Y2=$1;;
		-z|-z1) chkarg "$1" "$2" "." && shift && Z1=$1;;
		-z2) chkarg "$1" "$2" "." && shift && Z2=$1;;
		-g) chkarg "$1" "$2" "x" && shift && GEOMETRY=$1;;
		-s) chkarg "$1" "$2" "xy" && shift && SYNC=$1;;
		-a) ACC="zoom";;
		-e) OVR="-y";;
		-v) VERBOSE=true;;
		-m) DRYRUN="true";VERBOSE=true;PREVIEW=true;;
		-n) DRYRUN="true";VERBOSE=true;;
		-h|--help) echo "usage: ${0##*/} -i FILE OPTIONS

Options:
	-i	input file
	-o	output file name (can be full path and|or file name)
	-a	force acceleration method zoom
	-b I	blank time (fade out)
	-d I	duration - total length of output video
	-e	overwrite existing file
	-f I	fade in time
	-fps I	frames per second (default: 30)
	-g WxH	geometry of output video (default: 1280x720)
	-h	show this help
	-m	preview zoom areas in mtpaint
	-n	dryrun - show some info and exit
	-p I	pause time after zoompan
	-s [xy]	sync pan and zoom speed for x and/or y
	-u I	upscale (default: width+4000)
	-v	verbose output
	-w I	wait time before zoompan
	-x I	horizontal start position (default: 0)
	-x2 I	horizontal stop position (default: 0)
	-y I	vertical start position (default: 0)
	-y2 I	vertical stop position (default: bottom of image)
	-z D	start zoom factor (default: fit to image width)
	-z2 D	stop zoom factor (default: fit to image width)
"; exit;;
	esac
	shift
done

[ -z "$INFILE" ] && exit
[ -d "$OUTFILE" ] && OUTNAME="${INFILE##*/}" && OUTFILE="$OUTFILE/${OUTNAME%.*}_zoompan.mp4"

SIZE="$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0:s=x "$INFILE")"
INWIDTH=${SIZE%%x*}
INHEIGHT=${SIZE##*x}

# upscaling
(( INWIDTH > INHEIGHT )) && {
	(( INHEIGHT < 4000 || UPSCALE )) && SCALE="scale=-2:${UPSCALE:-ih+400$((INHEIGHT % 2))}," || :
} || {
	(( INWIDTH < 4000 || UPSCALE )) && SCALE="scale=${UPSCALE:-iw+400$((INWIDTH % 2))}:-2,"
}

# padding
OUTHEIGHT=${GEOMETRY##*x}
OUTWIDTH=${GEOMETRY%%x*}
OUTASPECT="$(bc <<< "scale=4;$OUTWIDTH / $OUTHEIGHT")"
(( $(bc <<< "scale=4;$INWIDTH / $INHEIGHT > $OUTASPECT") )) && {
	echo "vertical padding not available..."
	exit
}
PAD="pad=ih*${OUTWIDTH}/${OUTHEIGHT}:ih:(ow-iw)/2:(oh-ih)/2,"
PADWIDTH="$((INHEIGHT * OUTWIDTH / OUTHEIGHT))"

OUTWIDTH=$(( OUTWIDTH - (OUTWIDTH % 2) ))
OUTHEIGHT=$(( OUTHEIGHT - (OUTHEIGHT % 2) ))

# ffmpeg zoompan filter
# zoom
[ -z "$Z1" ] && Z1=$(bc <<< "scale=6;$PADWIDTH / $INWIDTH")
[ -z "$Z2" ] && Z2=$(bc <<< "scale=6;$PADWIDTH / $INWIDTH")

DURATION=$((TIME * FPS))
PAUSE=$((INTRO * FPS))
WAIT=$((STILL * FPS))
NET=$((DURATION - PAUSE - WAIT))

(( $(bc <<< "scale=4;$Z1 > $Z2") )) && { # zoom out
	STEP=$(bc <<< "scale=6;($Z1 - $Z2) / $NET")
	[ "$ACC" = "time" ] && {
		DIRECTION="if(lte(on,$WAIT),$Z1,max(zoom-$STEP,$Z2))"
	} || {
		DIRECTION="if(lte(on,$WAIT),$Z1,zoom-$STEP*max((2-(on-$WAIT)/($NET+1)*2),0))" # S-Kurve linksrechtsmitte
	}
} || { # zoom in
	STEP=$(bc <<< "scale=6;(($Z2-1) - ($Z1-1)) / $NET")
	[ "$ACC" = "time" ] && {
		DIRECTION="if(lte(on,$WAIT),$Z1,min(zoom+$STEP,$Z2))"
	} || {
		DIRECTION="if(lte(on,$WAIT),$Z1,min(zoom+$STEP*(on-$WAIT)/$NET*2,$Z2))" # S-Kurve linksrechtsmitte
	}
}

# pan
# engine
[ "$Z1" = "$Z2" -o "$ACC" = "time" ] && {
	ENGINE="if(lte(on,$WAIT),0,min((on-$WAIT)/$NET,1))"
} || {
	ENGINE="(zoom-$Z1)/($Z2-$Z1)"
}

[[ $SYNC = *x* ]] && SX="*$Z2/zoom"

OFFSET=$(( (PADWIDTH - INWIDTH) / 2 ))
XDIFF=$(bc <<< "scale=6;(($OFFSET + ${X2:-0}) - ($OFFSET + ${X1:-0})) / $PADWIDTH")
PAN_X="iw*$(bc <<< "scale=6;($OFFSET + ${X1:-0}) / $PADWIDTH")+iw*$XDIFF*$ENGINE$SX"

[[ $SYNC = *y* ]] && SY="*$Z2/zoom"
[ -z "$Y2" ] && Y2=$(bc <<< "$INHEIGHT - $INHEIGHT / $Z2")

YDIFF=$(bc <<< "scale=6;($Y2 - ${Y1:-0}) / $INHEIGHT")
PAN_Y="ih*$(bc <<< "scale=6;${Y1:-0} / $INHEIGHT")+ih*$YDIFF*$ENGINE$SY"

# fade
[ "$F_IN" ] && FADE_IN=",fade=t=in:st=0:d=$F_IN"
[ "$F_OUT" ] && FADE_OUT=",fade=t=out:st=$((TIME - F_OUT)):d=$F_OUT"

# info
[ "$VERBOSE" ] && {
	PX1=$(( $OFFSET + ${X1:-0} ))
	PX2=$(( $OFFSET + ${X2:-0} ))
	PZ1="$(bc <<< "$PADWIDTH / $Z1"):$(bc <<< "$PADWIDTH * $OUTHEIGHT / $OUTWIDTH / $Z1")"
	PZ2="$(bc <<< "$PADWIDTH / $Z2"):$(bc <<< "$PADWIDTH * $OUTHEIGHT / $OUTWIDTH / $Z2")"
	echo "Input image geometry: $SIZE"
	echo "Output video geometry: ${OUTWIDTH}x${OUTHEIGHT}"
	echo "Pad window width: $PADWIDTH"
	echo "Left padding is $OFFSET"
	echo "Zoom is $Z1 to $Z2"
	echo "Zoom window geometry: $PZ1:$PX1:${Y1:-0} to $PZ2:$PX2:$Y2"
	echo "Acceleration method is $ACC"
}

[ "$PREVIEW" ] && {
	[ -z "$(which mtpaint)" ] && echo "mtpaint not found. Exiting..." && exit
	PREVIEW="/tmp/ffzoompan_tmp/${INFILE##*/}"
	[ ! -d "${PREVIEW%/*}" ] && mkdir -p "${PREVIEW%/*}"
	mtpaint --cmd -f/open="$INFILE" -s/all -e/copy -f/new w=$PADWIDTH h=$INHEIGHT =24 -e/centre -e/Brush Size=3 -e/col A=2 -s/all \($PX1,${Y1:-0},${PZ1%:*},${PZ1##*:}\) -s/"Outline Selection" -e/col A=1 -s/all \($PX2,$Y2,${PZ2%:*},${PZ2##*:}\) -s/"Outline Selection" -f/as="${PREVIEW%.*}_preview.jpg" f=jpeg 1>/dev/null
	echo
	echo "NOTICE:"
	echo "When using coordinates from preview image keep in mind that x is absolute to the original image and you have to substract padding... "
	echo "e.g. -x \$((coord - $OFFSET))"
	echo
	mtpaint "${PREVIEW%.*}_preview.jpg" & exit
}

[ "$DRYRUN" ] && echo "Exiting now (dryrun)" && exit

# run in separate window
urxvt $HOLD -e ffmpeg $OVR -i "$INFILE" -vf "${SCALE}${PAD}zoompan=z='${DIRECTION}':d='${DURATION}':x='${PAN_X}':y='${PAN_Y}':s=${OUTWIDTH}x${OUTHEIGHT}:fps=${FPS}${FADE_IN}${FADE_OUT}" -pix_fmt yuv420p -c:v libx264 -crf 24 -preset veryfast "${OUTFILE:-${INFILE%.*}_zoompan.mp4}"

Notice the amount of options. Try out what works for you...

Have fun and stay curious ;)

User avatar
Flash
Moderator
Posts: 1003
Joined: Tue Dec 03, 2019 3:13 pm
Location: Arizona, U.S.
Has thanked: 56 times
Been thanked: 135 times

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by Flash »

Here's an excellent YouTube video describing the 'Ken Burns Effect' and how to make it happen. It's basically just zooming and panning within the still frame.

Chaos coordinator :?
HerrBert
Posts: 364
Joined: Mon Jul 13, 2020 6:14 pm
Location: Germany, NRW
Has thanked: 19 times
Been thanked: 135 times

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by HerrBert »

Flash wrote: Sat Mar 30, 2024 10:08 pm

Here's an excellent YouTube video describing the 'Ken Burns Effect' and how to make it happen. It's basically just zooming and panning within the still frame.
...

One of my favourites... But how do you do it in Puppyland?

[edit]removed repeated url to youtube video[/edit]

User avatar
rockedge
Site Admin
Posts: 6817
Joined: Mon Dec 02, 2019 1:38 am
Location: Connecticut,U.S.A.
Has thanked: 2968 times
Been thanked: 2793 times
Contact:

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by rockedge »

part of the Ken Burns Effect is cutting out foreground objects and pasting them back in the original position on a new layer. Possibly several objects on individual layers or alpha channels. Then panning the background separated from the stationary foreground object. You see the full effect used in Ken Burns' documentaries.

User avatar
greengeek
Posts: 1462
Joined: Thu Jul 16, 2020 11:06 pm
Has thanked: 590 times
Been thanked: 209 times

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by greengeek »

HerrBert wrote: Sat Mar 30, 2024 9:39 pm

Did some search on the internet and found it is called the Ken-Burns-Effect. Further searching led to some few results for using ffmpeg on linux to achieve this animation effect.
Threw together a script to get what i wanted.
......
Notice the amount of options. Try out what works for you...

Have fun and stay curious ;)

Does it have any form of gui or is it cli only?
Do i need to specify a single image file or does it look for a directory of images?

Very interesting concept. Hoping to get it working in Fossa 9.5

HerrBert
Posts: 364
Joined: Mon Jul 13, 2020 6:14 pm
Location: Germany, NRW
Has thanked: 19 times
Been thanked: 135 times

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by HerrBert »

@greengeek
The script in the first post has no gui.
I've written a very basic yad gui for personal use. Works with a modified version of the main script.
[edit]
If you start with a new file, all entries are empty.
To get basic values click mtPaint. This creates a preview of zoom areas and auto fills the most important entries.
[/edit]

yad_gui_ffzoompan.jpg
yad_gui_ffzoompan.jpg (30.73 KiB) Viewed 730 times

I'll attache both in a tared folder. Use at own risk.
Make sure to decompress the archive to a location that does not contain a directory named zoompan!

zoompan.tar.gz
(3.69 KiB) Downloaded 33 times

And yes, a single image has to be specified.

Last edited by HerrBert on Mon Dec 23, 2024 7:59 am, edited 1 time in total.
User avatar
greengeek
Posts: 1462
Joined: Thu Jul 16, 2020 11:06 pm
Has thanked: 590 times
Been thanked: 209 times

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by greengeek »

Wow! That's amazing!!
I could not get it to work initially - but then I copied your sample settings from the gui image in your post - and the result was fantastic.
Well done! Awesome stuff. :thumbup: :thumbup: :thumbup: :!: :!:

I just grabbed a still pic from a dashcam shot for testing.
(This zoompan effect will be great for family photos!)

Remove false .gz suffix

pic01_zoompan.mp4.gz
Remove false .gz suffix
(226.75 KiB) Downloaded 34 times
HerrBert
Posts: 364
Joined: Mon Jul 13, 2020 6:14 pm
Location: Germany, NRW
Has thanked: 19 times
Been thanked: 135 times

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by HerrBert »

@greengeek
Forgot to mention how to use the gui. Sorry.
If you start with a new file, all entries are empty.
To get basic values click mtPaint. This creates a preview of zoom areas and auto fills the most important entries.
(also added instruction to my previous post)

ozsouth
Posts: 1668
Joined: Sun Jul 12, 2020 2:38 am
Location: S.E. Australia
Has thanked: 252 times
Been thanked: 754 times

Re: script to use ffmpeg to animate images with the "Ken Burns Effect"

Post by ozsouth »

Just got sent this by MyHeritage - seems similar:

We invite you to try LiveMemory™, our incredible new AI feature that enables you to relive your favorite memories by turning any photo into a short video clip! The video reimagines the scene as if you were watching it live, and is perfect for sharing with family and friends. LiveMemory™ is available on the MyHeritage mobile app for iOS and Android.

Post Reply

Return to “Scripts”