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)
当然,这省略了许多细节(例如当图形被调整大小或完全重绘时更新背景)。然而,这个极简示例有望让您了解 FuncAnimation
内部如何使用 init_func
和 func
,以及“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
#
示例#
写入器类#
提供的写入器分为几个主要类别。
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
,而不是写入器实例。
按人类可读名称列出的可用写入器类的注册表。 |
写入器基类#
为减少代码重复,提供以下基类:
用于写入电影的抽象基类,通过调用 |
|
电影写入器的基类。 |
|
用于写入单个文件并在最后拼接的 |
和混入类:
FFMpeg 输出的混入类。 |
|
ImageMagick 输出的混入类。 |
。
有关如何轻松实现新的 MovieWriter
类,请参阅源代码。