I recently needed to overlay a Matplotlib animation on an image in a keynote
presentation. This requires creating an animation with a transparent background
so that only the plot elements are shown. It turns out that this is possible
using the Matplotlib animation objects (note: I’ve only tried this on Mac). The
key elements to doing this are to (1) make the Matplotlib figure background
invisible, (2) save the video using a png codec (yes, the image format), and (3)
to pass keyword arguments through the animation object to the individual
Matplotlib savefig
calls. I’ll show a simple example below of a circle
orbiting in a circle with a trail of points that fade out. First, some imports:
Input[1]:
# Third-party
from matplotlib import animation
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
from IPython.display import HTML
We start by defining how many frames (timesteps) to use in the animation, and how many circles to draw in the trail:
Input[2]:
n_frames = 128
n_trails = 8
The circle will orbit in a circle:
Input[3]:
t = np.linspace(0, 10, n_frames)
x = np.sin(t)
y = np.cos(t)
Finally, the meat of the code containing the calls to Matplotlib:
Input[4]:
fig, ax = plt.subplots(1, 1, figsize=(8, 8))
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
# turn off axis spines
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
ax.set_frame_on(False)
# set figure background opacity (alpha) to 0
fig.patch.set_alpha(0.0)
fig.tight_layout()
(pt,) = ax.plot([], [], linestyle="none", marker="o", ms=15, color="r")
trails = []
for i, alpha in enumerate(np.linspace(1.0, 0, n_trails)):
(l,) = ax.plot(
[], [], linestyle="none", marker="o", ms=6, alpha=alpha, c="w", zorder=-1000
)
trails.append(l)
def init():
pt.set_data([], [])
for trail in trails:
trail.set_data([], [])
return (pt,) + tuple(trails)
def update(i):
ix = i - n_trails
pt.set_data(x[i], y[i])
for j, trail in zip(range(len(trails))[::-1], trails):
if ix + j < 0:
continue
trail.set_data(x[ix + j], y[ix + j])
return (pt,) + tuple(trails)
ani = animation.FuncAnimation(
fig, update, n_frames, init_func=init, interval=20, blit=True
)
HTML(ani.to_html5_video())
Output[4]:
Notice the lines that hide the plot elements and make the figure background transparent:
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
ax.set_frame_on(False)
and
fig.patch.set_alpha(0.)
To save the video out with a transparent background, the other critical
arguments are to the save()
call, especially the keyword arguments passed
through via savefig_kwargs
:
Input[5]:
ani.save(
"circle_anim.mov",
codec="png",
dpi=100,
bitrate=-1,
savefig_kwargs={"transparent": True, "facecolor": "none"},
)