Reputation: 6682
For the following code (MWE), if the line
ax.set_axis_off()
is commented out, then the animation shows nothing. Otherwise, the animation works as expected. My question is: why?
from itertools import tee
import numpy as np
import matplotlib
matplotlib.use('Agg') # noqa
from matplotlib.animation import FuncAnimation, FFMpegWriter
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
import matplotlib.pyplot as plt
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def make_segs(x, y):
xs = np.array(list(pairwise(x))) # (n,2)
ys = np.array(list(pairwise(y))) # (n,2)
segs = np.stack((xs, ys), axis=-1) # (n,2,2)
return segs
class _AnimationHelper():
def __init__(self, xs, ys, z, colors=['r', 'g']):
self.segs = np.array([make_segs(x, y) for x, y in zip(xs, ys)])
self.z = z
self.colors = colors
self.fig = plt.figure()
self.lns = []
self.ani = FuncAnimation(
self.fig,
self.update,
interval=200,
init_func=self.init,
frames=self.segs.shape[1],
blit=True)
def init(self):
m, n = self.segs.shape[:2]
self.fig.set_size_inches(m*5, 5)
self.axes = self.fig.subplots(
nrows=1, ncols=m, sharey='row')
self.fig.tight_layout()
cmap = ListedColormap(self.colors)
norm = BoundaryNorm([0, 0.5, 1], cmap.N)
zero = np.zeros(n, dtype=np.int32)
for i in range(m):
inds = (zero + i) == self.z
ln = LineCollection([], cmap=cmap, norm=norm, lw=2, animated=True)
ln.set_array(inds)
self.lns.append(ln)
ax = self.axes[i]
ax.add_collection(ln)
ax.set_xlim(0, n)
ax.set_axis_off() # <---- HERE
self.axes[0].set_ylim(0, 1.1)
for i in range(1, m):
self.axes[i].tick_params(left=False)
return self.lns
def update(self, ind):
print(ind)
m = self.segs.shape[0]
for i in range(m):
self.lns[i].set_segments(self.segs[i, :(ind + 1)])
return self.lns
N = 20
M = 3
x = np.arange(N)
xs = np.repeat(x.reshape([1, -1]), M, axis=0)
ys = np.exp(-xs) + 0.05*np.random.random((M, N))
z = np.array([2]*4 + [1]*5 + [0]*5 + [2]*5)
helper = _AnimationHelper(xs, ys, z)
writer = FFMpegWriter(fps=5, codec='mpeg4')
helper.ani.save('out/test_saveani.mp4', writer=writer, dpi=80)
Upvotes: 0
Views: 611
Reputation: 339250
For saving a figure, the use of blit
is not necessary. If that is left out, the figure is saved correctly. However, the figure size change is not taken into account, because the animation is created before that change. Creating the figure and all the axes before creating the animation works perfectly fine though, even with blitting being used.
from itertools import tee
import numpy as np
import matplotlib
matplotlib.use('Agg') # noqa
from matplotlib.animation import FuncAnimation, FFMpegWriter
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
import matplotlib.pyplot as plt
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def make_segs(x, y):
xs = np.array(list(pairwise(x))) # (n,2)
ys = np.array(list(pairwise(y))) # (n,2)
segs = np.stack((xs, ys), axis=-1) # (n,2,2)
return segs
class _AnimationHelper():
def __init__(self, xs, ys, z, colors=['r', 'g']):
self.segs = np.array([make_segs(x, y) for x, y in zip(xs, ys)])
self.z = z
self.colors = colors
self.fig = plt.figure()
self.lns = []
m, n = self.segs.shape[:2]
self.fig.set_size_inches(m*5, 5, forward=True)
self.axes = self.fig.subplots(
nrows=1, ncols=m, sharey='row')
self.fig.tight_layout()
self.ani = FuncAnimation(
self.fig,
self.update,
interval=200,
init_func=self.init,
frames=self.segs.shape[1],
blit=True)
def init(self):
m, n = self.segs.shape[:2]
cmap = ListedColormap(self.colors)
norm = BoundaryNorm([0, 0.5, 1], cmap.N)
zero = np.zeros(n, dtype=np.int32)
for i in range(m):
inds = (zero + i) == self.z
ln = LineCollection([], cmap=cmap, norm=norm, lw=2, animated=True)
ln.set_array(inds)
self.lns.append(ln)
ax = self.axes[i]
ax.add_collection(ln)
ax.set_xlim(0, n)
#ax.set_axis_off() # <---- HERE
self.axes[0].set_ylim(0, 1.1)
for i in range(1, m):
self.axes[i].tick_params(left=False)
return self.lns
def update(self, ind):
print(ind)
m = self.segs.shape[0]
for i in range(m):
self.lns[i].set_segments(self.segs[i, :(ind + 1)])
return self.lns
N = 20
M = 3
x = np.arange(N)
xs = np.repeat(x.reshape([1, -1]), M, axis=0)
ys = np.exp(-xs) + 0.05*np.random.random((M, N))
z = np.array([2]*4 + [1]*5 + [0]*5 + [2]*5)
helper = _AnimationHelper(xs, ys, z)
writer = FFMpegWriter(fps=5, codec='mpeg4')
helper.ani.save('test_saveani.mp4', writer=writer, dpi=80)
The above now even allows to use an interactive backend, matplotlib.use('TkAgg')
and showing the figure via plt.show()
.
Upvotes: 1