如何利用Matplotlib库绘制动画及保存GIF图片
作者:frank909 发布时间:2021-06-08 17:15:40
前言
在自学机器学习或者是深度学习的过程中,有的时候总想把执行过程或者执行结果显示出来,所以就想到了动画。好在用 Python 实现动画有许多中方式,而大家熟知的 Matplotlib 库就可以实现。
本文的目的是对 Matplotlib 的动画实现手段做一个简单的说明。
绘制动画
import matplotlib.pyplot as plt
import matplotlib.animation as animation
如果要让 matplotlib 实现动画功能的话,那么就要引入 animation 模块。
然后再创建 animation 的对象。
anim = animation.FuncAnimation(fig, run, data_gen, blit=False, interval=10,
repeat=False, init_func=init)
animation 的实现类是 FuncAnimation,它有一个构造方法。下面先通过一个示例,讲解 animation 的基本用法,然后再来细致分析 FuncAnimation 构造方法中各项参数的意义。
我们的目标是做一个 Sin 函数的动画示例。
代码很简单。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro',animated=True)
def init():
ax.set_xlim(-np.pi,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,
anim = animation.FuncAnimation(fig, update, frames=np.linspace(-np.pi,np.pi, 90),interval=10,
init_func=init,blit=True)
plt.show()
核心代码是这一行。
anim = animation.FuncAnimation(fig, update, frames=np.linspace(-np.pi,np.pi, 90),interval=10,
init_func=init,blit=True)
按照上面的示例代码,我们可以依葫芦画瓢编写动画代码了。
但,如果我们需要达到灵活运用的话,就需要花点心思,了解它们的机制。
我们先来看看 FuncAnimation 的构造方法。
def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
save_count=None, **kwargs):
fig 自然是 matplotlib 中的 figure 对象。
func 是每一次更新时所调用的方法,它是回调函数。因此,我们可以在这个方法中更新 figure 当中的 axes 中的 line2d 对象,它是动态更新 figure 的根本。
frames 代表了整个动画过程中帧的取值范围,而本质上是一个数据发生器。我将在后面重点讲解它。
init_func 是初始函数,用来初始 figure 的画面。
fargs 是每次附加给 func 回调函数的参数,可以为 None
save_count 是缓存的数量
除此之外,还有一些可选的参数,它们分别是
interval 是每 2 个 frame 发生的时间间隔,单位是 ms,默认值是 200.
repeat_delay 取值是数值,如果 animation 是重复播放的话,这个值就是每次播放之间的延迟时间,单位是 ms。
repeat bool 型可选参数,默认为 True,代表动画是否会重复执行
blit bool 型可选参数,控制绘制的优化。默认是 False。
如何理解 animation 呢?
我认为,animation 的核心是 frames 和 func。
frames 可以取值:iterable,int,generator 生成器函数 或者是 None。
在上面的代码中,我们给 frames 的取值是这样的。
frames=np.linspace(-np.pi,np.pi, 90)
其实就是一个 list,它的值范围为 -pi 到 pi,frames 总共有 90 帧,而 list 是一个 iterable 类型,所以它可以不停的迭代。
frames 也可以取值为整数,相当于给参数赋值 range(frames)。
frames 也可以取值为 None,那么它的结果相当于传递 itertools.count,结构就是从 0 开始,每次步进 1,无限的执行下去。
frames 还接受 generator 函数,也就是生成器,但有个前提是,生成器要符合下面的签名格式。
def gen_function() -> obj
参数列表为空,但需要返回一个值,这个值就会传入到 func 回调函数当中。
func 是回调函数,它会在每次更新的时候被调用,所以我们只需要在这个函数中更新 figure 中的数值就可以了,就像下面代码。
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
return ln,
frames 和 func 的关系是什么?
实际上,frames 决定了整个动画 frame 的取值范围,它会在 interval 时间内迭代一次,然后将值传递给 func,直到整个 frames 迭代完毕。
我本人而言,也更倾向于用 generator 函数去定义 frames 而不是直接分配一个列表,所以我可以将之前的代码改写如下。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro',animated=True)
def init():
ax.set_xlim(-np.pi,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,
def data_gen():
frame = -np.pi
step = 2 * np.pi / 90
while frame < np.pi:
frame += step
yield frame
# anim = animation.FuncAnimation(fig, update, frames=np.linspace(-np.pi,np.pi, 360),interval=10,
# init_func=init,blit=True)
anim = animation.FuncAnimation(fig, update, frames=data_gen,interval=10,
init_func=init,blit=True)
plt.show()
data_gen 就是一个生成器函数,它会每隔 10ms 运行一次,然后将结果传递给 update 函数。
data_gen 里面运用到了 yield 关键字,这是的我们可以在每次迭代时才返回相应的结构,而不要在一开始就分配。如果不熟悉这方面知识点的同学,可以自行搜索相应的知识。
保存动画
因为经常写博客,所以也经常需要将结果保存下来,一般我会保存为 .gif 格式图片,本篇博文的 gif 图像就是通过 matplotlib 保存的。
好在用 matplotlib 实现它也并不难。
anim.save('test_animation.gif',writer='imagemagick')
一句代码就搞定了,运行成功后,会在当前目录下生成 test_animation.gif 图像。
需要注意到的是,如果要保存 gif 图像,这要求开发者电脑已经安装了 ImageMagicK。
ubuntu 用户可以通过如下命令安装。
sudo apt-get install imagemagick
并且,动画保存的时候要指定 writer 为 imagemagick.
动画可以保存为 gif 图像,自然也能保存为 mp4 视频格式。
但这要求开发者计算机已经安装好 ffmpeg 库,并且 save 方法中指定 writer 为 ffmpeg,具体细节请读者自行扩展阅读。
总结
来源:https://frank909.blog.csdn.net/article/details/84940997
猜你喜欢
- 推箱子游戏是老游戏了, 网上有各种各样的版本, 说下推箱子游戏的简单实现,以及我找到的一些参考视频和实例;如下是效果图:这个拖箱子游戏做了移
- 监听select2的值改变进行查询由于前端项目使用的是Vue.js和bootstrap整合开发,中间用到了select2下拉框,今天在做查询
- 这个python的小案例是五子棋游戏的实现,在这个案例中,我们可以实现五子棋游戏的两个玩家在指定的位置落子,画出落子后的棋盘,并且根据函数判
- 调用JSON.stringify将对象转为对应的字符串时,如果包含时间对象,时间对象会被转换为国家标准时间(ISO),而不是当前国家区域的时
- JsonServer主要的作用就是搭建本地的数据接口,创建json文件,便于调试调用是一个 Node 模块,运行 Express 服务器,可
- 下面这些命令可以在命令行下用isql执行,isql -E -Q "命令",isql.exe
- 世界疫情数据下载请点击》》:疫情数据下载注:此数据是2022年3月12号的结果,其中透明的地方代表确诊人数小于10万人,白色的地方代表无该国
- js格式化金额,可选是否带千分位,可选保留精度,也是网上搜到的,但是使用没问题 /* 将数值四舍五入后格式化. @param num 数值(
- 最近,找到了一种新的pycharm激活方法,支持Jetbrains全家桶,比如 idea、pychram、WebStorm等等,没得zhil
- numpy.mean计算矩阵均值计算矩阵的均值>>> a = np.array([[1, 2], [3, 4]])>
- 前言现在正是卡塔尔世界杯激战正酣的时候,每天都有各种各样的新闻。而且,不同的球队,随着比赛的进程,关注的热度也会发生翻天覆地的变化。今天我们
- transforms按住Ctrl查看transforms的源码可以知道,transforms就是一个python文件,里面定义了很多类,每一
- 概述for循环是一个循环控制结构,可以执行指定次数的循环语法第一种for {} //无线循环第二种for 条件语
- 项目中有这样的需求,通过IP地址判断客户端是网通的还是电信的。从同事那拿了个纯文本的IP纯真数据库,用Python写了一个小程序,感觉挺好的
- 前言cookie:在网站中,http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪
- Perl利用函数rand()和srand()为随机数(更确切的说是"伪随机数")字符串的生成提供了基本的工具。这些函数不
- 如果你的数据量有几十万条,用户又搜索一些很通俗的词,然后要依次读最后几页重温旧梦。mysql该很悲壮的不停操作硬盘。 所以,可以试着让mys
- 通常报表都存储在一个地方,但是在单独的文件中。随着文件数量的增加在结构和搜索上也困难多多,针对这个问题,我遇到了一个非常有趣的报表对象属性&
- 本文实例讲述了python+numpy实现的基本矩阵操作。分享给大家供大家参考,具体如下:#! usr/bin/env python# co
- 一、安装软件包并创建项目$sudo pip install django$sudo python -c "import djang