Skip to content

Commit

Permalink
Add option to stabilise output GIFs
Browse files Browse the repository at this point in the history
When making a GIF that is using content filmed hand-held (or faking it)
it can be shaky. Whilst this is a great dramatic look, it adds
considerable size to a GIF as each frame changes.

The option "stabilise" takes either a number (frames before/after the
current frame to use for stability detection), or a special keyword
`static` that tries to emulate a static camera.

The vidstab library also comes with a `tripod` option but that is not
being used as it doesn't behave well when the input cuts from one scene
to another (very common in GIFs); `static` is almost as good and does
survive scene changes.
  • Loading branch information
norm committed Nov 14, 2021
1 parent 3eb56a9 commit 1623268
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 10 deletions.
7 changes: 6 additions & 1 deletion README.markdown
Expand Up @@ -57,6 +57,7 @@ denoise = true
mode = 'full'
max_size = '2mb' # or 'auto'
dither = 'floyd_steinberg'
stabilise = 'static'

[[clip]]
start = '2'
Expand Down Expand Up @@ -149,10 +150,14 @@ the video source.
source video (slo-mo). A multiplier lower than 1 will speed it up.
* `dither` is how the palette colours are dithered to create the appearance
of more colours:
* `bayer:bayer_scale=1` the scale is an integer between 0 and 5
* `bayer:bayer_scale=1` the scale is an integer between 0 and 5
* `floyd_steinberg`
* `sierra2`
* `sierra2_4a`
* `stabilise` attempts to stabilise (deshake) the output GIF, the value is the
number of frames before/after the current frame to consider for
stabilisation, or the value `static` which tries to emulate a static camera
(note that this will crop the video somewhat)

The default is `bayer:bayer_scale=4`. Illustrative samples can be found in
"[High quality GIF with FFmpeg][quality]".
Expand Down
30 changes: 29 additions & 1 deletion bin/make_gif
Expand Up @@ -117,6 +117,17 @@ function main {
[ -n "$(toml $toml palette.show)" ] && \
open "${TEMP}/palette.png"

# first-pass stabilisation
local stabilise="$(get_stabilise $toml)"
if [ -n "$stabilise" ]; then
debug ffmpeg \
-loglevel error \
${timing_args} \
-i "$video" \
-vf vidstabdetect=shakiness=10:accuracy=15 \
-f null -
fi

filter_debug "$(generate_filter $toml)"
debug ffmpeg \
-loglevel error \
Expand Down Expand Up @@ -271,10 +282,13 @@ function generate_filter {
local bright=$(get_bright $toml)
local denoise=$(get_denoise $toml)
local crop=$(get_crop $toml)
local stabilise=$(get_stabilise $toml)
local fps=$(get_fps $toml)
local scale=$(get_scale $toml)
local slowdown="$(get_slowdown $toml)"
local alter="$(join ',' $fps $crop $scale $bright $denoise $slowdown)"
local alter="$(
join ',' $stabilise $fps $crop $scale $bright $denoise $slowdown
)"
alter="${alter:-scale=w=in_w:h=in_h}"
echo "[0:v] $alter [iv]; [iv]${captions}${paletteuse}"
fi
Expand All @@ -290,6 +304,20 @@ function get_crop {
echo $crop
}

function get_stabilise {
local toml="$1"

local output_stabilise="$(toml $toml output.stabilise)"
if [ -n "$output_stabilise" ]; then
case "$output_stabilise" in
static) echo "vidstabtransform=smoothing=0"
;;
*) echo "vidstabtransform=smoothing=$output_stabilise"
;;
esac
fi
}

function get_fps {
local toml="$1"

Expand Down
1 change: 1 addition & 0 deletions source/new
Expand Up @@ -46,6 +46,7 @@ fps = 'original'
loss = 'no'
max_size = 'auto'
# mode = 'full'
# stabilise = 'static | ##'
# width = 'original'
# width = '400'

Expand Down
1 change: 1 addition & 0 deletions source/ted-lasso/new
Expand Up @@ -66,6 +66,7 @@ fps = '18'
loss = 'no'
max_size = 'auto'
# mode = 'full'
# stabilise = 'static | ##'
# width = 'original'
# width = '400'

Expand Down
16 changes: 8 additions & 8 deletions tests/filter_generate.bats
Expand Up @@ -129,11 +129,11 @@ source bin/make_gif

@test generate_captions {
expected='[0:v] fps=18,scale=480:-1 [iv]; '
expected+='[iv][3:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[iv][2:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0,0.3)' [v1]; "
expected+='[v1][4:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[v1][3:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0.31,0.6)' [v2]; "
expected+='[v2][5:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[v2][4:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0.61,0.9)' [v3]; "
expected+='[v3][1:v] paletteuse='
expected+='dither=bayer:bayer_scale=4:diff_mode=rectangle'
Expand All @@ -146,9 +146,9 @@ source bin/make_gif

@test generate_captions_noscale {
expected='[0:v] scale=w=in_w:h=in_h [iv]; '
expected+='[iv][3:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[iv][2:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0,0.5)' [v1]; "
expected+='[v1][4:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[v1][3:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0.51,1)' [v2]; "
expected+='[v2][1:v] paletteuse='
expected+='dither=bayer:bayer_scale=4:diff_mode=rectangle'
Expand All @@ -163,11 +163,11 @@ source bin/make_gif
expected='[0:v] fps=18,trim=start=10:end=12,setpts=PTS-STARTPTS [c1]; '
expected+='[0:v] fps=18,trim=start=70:end=72,setpts=PTS-STARTPTS [c2]; '
expected+='[c1][c2] concat=n=2:v=1,scale=480:-1 [cv]; '
expected+='[cv][3:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[cv][2:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0,0.3)' [v1]; "
expected+='[v1][4:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[v1][3:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0.31,0.6)' [v2]; "
expected+='[v2][5:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+='[v2][4:v] overlay=(main_w-overlay_w):(main_h-overlay_h)'
expected+=":enable='between(t,0.61,0.9)' [v3]; "
expected+='[v3][1:v] paletteuse='
expected+='dither=bayer:bayer_scale=4:diff_mode=rectangle'
Expand Down

0 comments on commit 1623268

Please sign in to comment.