Any way to automatically compress a .gif file?
I have a 2.0 mb gif I want to make smaller, preferably by dropping some frames.
Is there a command line utility or a program that will do it for me automatically?
Thanks.
Solution 1:
ImageOptim uses Gifsicle for its Gif compression. As far as I know, it works quite well (I don't generally use Gif these days). From their site:
Optimize your animations! This stores only the changed portion of each frame, and can radically shrink your GIFs. You can also use transparency to make them even smaller. Gifsicle’s optimizer is pretty powerful, and usually reduces animations to within a couple bytes of the best commercial optimizers.
Solution 2:
You can use the Gimp to remove some frames from the GIF as well as re-optimizing the file by saving it back to a GIF again.
Solution 3:
I use ezgif.com/optimize, as GIMP and other tools aren't good at this job. The difference is that ezgif uses a gifsicle
modification (with a lossy
flag), not available on the apt
version. See release 1.82.1 here (replace /usr/bin/gifsicle
with the appropriate one for your system (the binary ending with -static
).
My notes on a full gif optimisation workflow are here.
Edit: current script as kept in use will be updated on GitHub here
I won't describe how I do it all here, the general idea is a few bash functions/aliases:
function gifopt() {
# args: $input_file ($loss_level)
if [ -z "$2" ]
then
# use default of 30
local loss_level=30
elif [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 30 -a "$2" -le 200 ]
then
local loss_level=$2
else
echo "${2:-"Loss level parameter must be an integer from 30-200"}" 1>&2
exit 1
fi
local inputgif="${1?'Missing input file parameter'}"
local gifname="$(basename $inputgif .gif)"
local basegifname=$(echo "$gifname" | sed 's/_reduced_x[0-9]//g')
local outputgif="$basegifname-opt.gif"
gifsicle -O3 --lossy="$loss_level" -o "$outputgif" "$inputgif";
local oldfilesize=$(du -h $inputgif | cut -f1)
local newfilesize=$(du -h $outputgif | cut -f1)
echo "File reduced from $oldfilesize to $newfilesize as $outputgif"
}
function gifopt() {
# args: $input_file ($loss_level)
if [ -z "$2" ]
then
# use default of 30
loss_level=30
elif [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 30 -a "$2" -le 200 ]
then
loss_level=$2
else
echo "${2:-"Loss level parameter must be an integer from 30-200"}" 1>&2
exit 1
fi
local inputgif="${1?'Missing input file parameter'}"
local gifname="$(basename $inputgif .gif)"
local basegifname=$(echo "$gifname" | sed 's/_reduced_x[0-9]//g')
local outputgif="$basegifname-opt.gif"
gifsicle -O3 --lossy="$loss_level" -o "$outputgif" "$inputgif";
local oldfilesize=$(du -h $inputgif | cut -f1)
local newfilesize=$(du -h $outputgif | cut -f1)
echo "File reduced from $oldfilesize to $newfilesize as $outputgif"
}
function gifspeedchange() {
# args: $gif_path $frame_delay (1 = 0.1s)
local orig_gif="${1?'Missing GIF filename parameter'}"
local frame_delay=${2?'Missing frame delay parameter'}
gifsicle --batch --delay $frame_delay $orig_gif
local newframerate=$(echo "$frame_delay*10" | bc)
echo "new GIF frame rate: $newframerate ms"
}
The --lossy
flag takes an integer from 30 to 200, so for best quality:
gifsicle -O3 --lossy=30 -o output.gif input.gif
With the above function gifopt
you could simplify matters, since it defaults to 30
gifopt input.gif
...the output would be automatically named input-opt.gif
. You could change the function to use the --batch
flag for in-place editing but I wouldn't recommend overwriting your starting material.
That function also works to remove any _reduced_x
suffix added by the frame count reduction function, i.e. you could have an intermediate file input_reduced_x2.gif
(a step that would ~halve file size by dropping every other frame).
If you did do that the speed may change, fixed with gifspeedchange input-opt.gif 5
for example, to use a 50ms frame delay.