Skip to content

Improve saving animated GIF with ffmpeg #18093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 29, 2020

Conversation

dopplershift
Copy link
Contributor

PR Summary

As of #17401, we enabled an error-free path to save animated GIFs with ffmpeg, which is the default writer. This means with matplotlib 3.3.0 the follow code works without error:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig, ax = plt.subplots(figsize=(2, 2))
x = np.arange(0, 2*np.pi, 0.01)
line, = ax.plot(x, np.sin(x))

def animate(i):
    line.set_ydata(np.sin(x + i / 50))  # update the data.
    return line,

anim = animation.FuncAnimation(fig, animate, interval=20, blit=True, save_count=50)
anim.save('test.gif')

Unfortunately, it also produces this by default (notice the weird coloring):

ffmpeg

This is a consequence of GIF being a palette-d image format and the default palette used by ffmpeg being...suboptimal for matplotlib (I'm pretty sure anti-aliasing plays a role, otherwise there are only 3 colors in the original image). Luckily this blog from GIPHY, which helped me understand this, also provides some magic incantations for ffmpeg to get it to determine an optimal palette for us. The result is:

ffmpeg

Best of all, if I run that same animation through ffmpeg, imagemagick, and pillow we see:

 128K  ffmpeg.gif
 661K  pillow.gif
 231K  imagemagick.gif

So our default writer now produces our smallest animated GIFs.

I also fixed a bug where parameters to save() like codec were not being passed and set in the base class--thus the rcParam defaults were always used. (Introduced in #15961.) I tried to add a test for this, but couldn't figure out how since you can't pass them along with a class instance, which was the straightforward path to check they're set properly.

I'll leave it to @QuLogic to decide whether this belongs in 3.3.1.

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/next_api_changes/* if API changed in a backward-incompatible way

The default GIF palette from ffmpeg looks terrible with our plots. With
some ffmpeg magic, we can have it autogenerate a palette from the frames
of the animation.
Missed forwarding a couple of parameters when `AbstractMovieWriter`
gained an `__init__`.
@tacaswell tacaswell added this to the v3.3.1 milestone Jul 29, 2020
@tacaswell tacaswell merged commit 33503a2 into matplotlib:master Jul 29, 2020
meeseeksmachine pushed a commit to meeseeksmachine/matplotlib that referenced this pull request Jul 29, 2020
@dopplershift dopplershift deleted the fix-ffmpeg-gif branch July 29, 2020 05:48
QuLogic added a commit that referenced this pull request Jul 29, 2020
…093-on-v3.3.x

Backport PR #18093 on branch v3.3.x (Improve saving animated GIF with ffmpeg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants