image-gif
image-gif copied to clipboard
Improve the efficiency of encoder
Here's the comparison of ffmpeg and image-gif relative to source mp4 video All in 30 FPS, 640x360 resolution
source mp4
https://user-images.githubusercontent.com/64297935/125145565-3f463400-e154-11eb-93d2-319daa87067b.mp4
size = 913.5 KB
ffmpeg's gif
size = 887.4 KB
image-gif's gifenc
size = 2.5 MB
There's a really intriguing color difference between the two images. ffmpeg has inserted some sort of dithering in some of the solid color blocks. Other than that defect, the big difference comes from delta-encoding. The individual frames of ffmpeg
use this canvas:
canvas: 160x93+480+267
Which is merely the changing square at the bottom right. The image-rs
encoder does not try to detect where delta encoding could be used so it rewrites the full image for every frame—just like the user is asking for when providing the Frame
instance. For the moment you can probably throw any gif optimizer on it to rectify the difference. gifski
might also work for this.
However given that this might a somewhat common occurance I'm going to keep this open as a starting point if someone wants to add an optional layer that can be enabled to optimize away these 'obvious' repeating pixels somewhere in Encoder
.
I think by default the ffmpeg encoder uses a global palette quantized for every color in the animation with a sierra2_4a
dither.
https://ffmpeg.org/ffmpeg-filters.html#palettegen
https://ffmpeg.org/ffmpeg-filters.html#paletteuse
You can see an example generated palette with:
ffmpeg -i source.mp4 -vf palettegen pattern-palette.png
ffmpeg straight to gif, 865kB
.
ffmpeg -i source.mp4 pattern-ffmpeg.gif
Using the global palette but disabling dither, 1950kB
.
ffmpeg -i source.mp4 -lavfi "palettegen[pal],[0:v][pal]paletteuse=new=1:dither=none" pattern-ffmpeg-global-palette-no-dither.gif
I think the following command is closest to what this crate does: generate a palette for every frame, 2779kB
.
ffmpeg -i source.mp4 -lavfi "palettegen=stats_mode=single[pal],[0:v][pal]paletteuse=new=1:dither=none" pattern-ffmpeg-single-frame-no-dither.gif
With that context, 2.4MB
isn't wildly out of place for what's being done.
I was curious about the gains that could be had from delta encoding with a naive approach by modifying the Frame::from_rgba_speed
function. I was able to get the image-gif gifenc
file down to 2019kB
by re-encoding it, which is about a 17% file size decrease in this case. I would have used the source mp4 but I don't know how the frames were produced to make the original gif.
Delta-encoded gif
The second frame
So, more advanced delta encoding and global palette quanitzation to avoid the overhead of a palette for every frame would get much closer to the size of the ffmpeg gifs. The library supports global palettes but users have to generate it and map the pixels to the nearest color.
I think this crate encodes fine. The difference you see is not from improved compression, but quite the opposite: it's because ffmpeg's naive remapping destroys so much of the color detail that it has less data left to encode.
If I use low quality setting in gifski (that uses this crate to encode) I get even smaller file (570KB):
gifsicle-optimized LZW can take it down to 530KB.