LineCollection动画

时间:2018-10-09 14:46:40

标签: python animation matplotlib

对于以下代码(MWE),如果有一行

ax.set_axis_off()

被注释掉,然后动画什么也没显示。否则,动画将按预期工作。我的问题是:为什么?

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)

1 个答案:

答案 0 :(得分:1)

要保存图形,不需要使用blit。如果不加说明,则该图将正确保存。但是,未考虑图形尺寸更改,因为动画是在更改之前创建的。即使在使用动画的情况下,在创建动画之前创建图形和所有轴也可以正常工作。

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)

以上内容甚至允许使用交互式后端matplotlib.use('TkAgg'),并通过plt.show()显示该图形。