Page 1 of 1

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

Posted: Sat Mar 30, 2024 9:39 pm
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 ;)


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

Posted: Sat Mar 30, 2024 10:08 pm
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.


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

Posted: Sat Mar 30, 2024 10:16 pm
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]


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

Posted: Sun Mar 31, 2024 1:53 am
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.