matplotlib.animation
#
动画#
在 Matplotlib 中制作实时动画的最简单方法是使用其中一个 Animation
类。
参见
动画
动画的基类。 |
|
|
|
|
在这两种情况下,保持对实例对象的引用至关重要。动画由计时器(通常来自主机GUI框架)推进,而 Animation
对象持有对该计时器的唯一引用。如果你不持有对 Animation
对象的引用,它(以及计时器)将被垃圾回收,从而停止动画。
要保存动画,请使用 Animation.save
、Animation.to_html5_video
或 Animation.to_jshtml
。
有关支持的电影格式的详细信息,请参见下面的 辅助类。
FuncAnimation
#
FuncAnimation
的内部工作原理大致如下:
for d in frames:
artists = func(d, *fargs)
fig.canvas.draw_idle()
fig.canvas.start_event_loop(interval)
详细处理 'blitting'(以显著提高实时性能),实现非阻塞,不重复启动/停止 GUI 事件循环,处理重复,多个动画轴,并轻松将动画保存到电影文件。
'Blitting' 是计算机图形学中的 标准技术 。其基本思路是取一个现有的位图(在我们的例子中是一个大部分栅格化的图形),然后在上面再 'blit' 一个艺术家。因此,通过管理一个保存的 '干净' 位图,我们只需重新绘制每一帧中正在变化的少数艺术家,从而可能节省大量时间。当我们使用 blitting(通过传递 blit=True
)时,FuncAnimation
的核心循环会变得更加复杂:
ax = fig.gca()
def update_blit(artists):
fig.canvas.restore_region(bg_cache)
for a in artists:
a.axes.draw_artist(a)
ax.figure.canvas.blit(ax.bbox)
artists = init_func()
for a in artists:
a.set_animated(True)
fig.canvas.draw()
bg_cache = fig.canvas.copy_from_bbox(ax.bbox)
for f in frames:
artists = func(f, *fargs)
update_blit(artists)
fig.canvas.start_event_loop(interval)
当然,这省略了许多细节(例如在图形调整大小或完全重绘时更新背景)。然而,这个希望是最小化的例子给出了一个关于 init_func
和 func
如何在 FuncAnimation
内部使用以及'blitting'工作原理的理论的感觉。
备注
在进行'blitting'时,不会考虑艺术家的zorder,因为'blitted'艺术家总是绘制在最上层。
func
和 init_func
上的预期签名非常简单,以使 FuncAnimation
远离您的簿记和绘图逻辑,但这意味着您传递的可调用对象必须知道它们应该处理哪些艺术家。有几种方法可以处理这个问题,复杂性和封装性各不相同。最简单的方法,在脚本的情况下效果非常好,是在全局范围内定义艺术家,并让 Python 处理其余的事情。例如:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = ax.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
第二种方法是使用 functools.partial
将参数传递给函数:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from functools import partial
fig, ax = plt.subplots()
line1, = ax.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return line1,
def update(frame, ln, x, y):
x.append(frame)
y.append(np.sin(frame))
ln.set_data(x, y)
return ln,
ani = FuncAnimation(
fig, partial(update, ln=line1, x=[], y=[]),
frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
第三种方法是使用闭包来构建所需的艺术家和函数。第四种方法是创建一个类。
示例#
ArtistAnimation
#
示例#
Writer 类#
提供的写入器分为几个大类。
Pillow 写入器依赖 Pillow 库来写入动画,将所有数据保存在内存中。
HTML 写入器生成基于 JavaScript 的动画。
用于基于JavaScript的HTML电影的编写器。 |
基于管道的写入器通过管道将捕获的帧流式传输到外部进程。基于管道的变体往往性能更高,但可能并非在所有系统上都适用。
基于管道的 ffmpeg 写入器。 |
|
基于管道的动画GIF写入器。 |
基于文件的写入器为每个帧保存临时文件,这些文件最终会被拼接成一个单一文件。尽管速度较慢,但这些写入器更容易调试。
基于文件的 ffmpeg 写入器。 |
|
基于文件的动画 GIF 写入器。 |
写入器类提供了一种从相同的底层 Figure
抓取连续帧的方法。它们都提供了三个必须按顺序调用的方法:
setup
准备写入器(例如,打开一个管道)。基于管道和基于文件的写入器在调用setup()
时需要不同的参数。grab_frame
可以按需多次调用,以一次捕捉一帧。finish
完成电影并将其输出文件写入磁盘。
示例:
moviewriter = MovieWriter(...)
moviewriter.setup(fig, 'my_movie.ext', dpi=100)
for j in range(n):
update_figure(j)
moviewriter.grab_frame()
moviewriter.finish()
如果直接使用写入器类(而不是通过 Animation.save
),强烈建议使用 saving
上下文管理器:
with moviewriter.saving(fig, 'myfile.mp4', dpi=100):
for j in range(n):
update_figure(j)
moviewriter.grab_frame()
以确保在必要时执行设置和清理。
示例#
辅助类#
动画基类#
动画的基类。 |
|
|
写作者注册表#
提供了一个模块级别的注册表,用于在写入器的名称和类之间进行映射,以便可以将字符串传递给 Animation.save
而不是写入器实例。
按人类可读名称分类的可用写入器类注册表。 |
Writer 基类#
为了减少代码重复,基类
编写电影的抽象基类,提供通过调用 |
|
编写电影的基类。 |
|
|
和混入
FFMpeg 输出的 Mixin 类。 |
|
ImageMagick 输出的 Mixin 类。 |
提供。
查看源代码以了解如何轻松实现新的 MovieWriter
类。