Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The default GIF saving algorithm lacks dithering and palette optimization which results in noticeable banding artifacts. In this PR I add a new option (turned off by default) that enables these features. The new imageio v3 API is used for that because it supports pyav plugin which creates the resulting GIF with
ffmpeg
and a filter graph that precalculates the palette from all frames in the sequence. If theav
library is missing it installs it automatically. As a result, the GIF looks much better but the file might be bigger (not always), and it also requires a dependency to install.There's a weird quirk that I couldn't explain. If the filter graph is used, the image is always resized to
640x480
no matter what its source size is. It's probably a bug somewhere in the pyav plugin or the av library because ffmpeg with the same graph doesn't do that. So to mitigate it I added a scale filter to the graph in the beginning.Comparison
Banding is visible on the ground in the first GIF, it's even worse in dynamic.
Note that these images are not perfectly in sync. Turns out the Pillow GIF creator is a bit less smart than ffmpeg. The default 8 FPS setting means that every frame should be shown for 125 ms (1000/8), however, the GIF spec says that the frame time is specified in hundredths of a second which would be 12.5. Since 0.5 can't be represented, Pillow uses just 12 which gives us 8.(3) FPS instead of 8.
Ffmpeg does an interesting trick, it alternates the frame delays between 130 and 120 ms which, on average, gives us 125 ms and 8 FPS. So the optimized version is technically more correct in that regard too.