使用ArtistAnimation在matplotlib中动画化png图像
我一直在尝试为我用有限元方法创建的二维热流问题制作一系列的表面图动画。在每个时间步,我保存了一张图,而不是整个矩阵,这样做更高效。
我在使用matplotlib.animation库中的FuncAnimation
时遇到了麻烦,所以我决定在每个时间点渲染一张表面图,把这张表面图保存为.png文件,然后用pyplot.imread
读取这个图片。接下来,我想把每张图片存到一个列表里,这样我就可以使用ArtistAnimation(示例)。但是我并没有得到动画,而是得到了两个空白的图表,接着才是我打印出来的表面图.png文件。
另外,当我尝试保存动画时,出现了以下错误信息:
AttributeError: 'module' object has no attribute 'save'.
如果有人能帮我从当前目录读取一组.png文件,保存到一个列表中,然后使用ArtistAnimation来“动画化”这些.png文件,我将非常感激。我不需要什么复杂的东西。
(注意 - 我需要让代码自动化,所以不幸的是,我不能使用像iMovie或ffmpeg这样的外部工具来制作我的图片动画。)
下面是我的代码:
from numpy import *
from pylab import *
import matplotlib.pyplot as plt
import matplotlib.image as mgimg
from matplotlib import animation
## Read in graphs
p = 0
myimages = []
for k in range(1, len(params.t)):
fname = "heatflow%03d.png" %p
# read in pictures
img = mgimg.imread(fname)
imgplot = plt.imshow(img)
myimages.append([imgplot])
p += 1
## Make animation
fig = plt.figure()
animation.ArtistAnimation(fig, myimages, interval=20, blit=True, repeat_delay=1000)
animation.save("animation.mp4", fps = 30)
plt.show()
2 个回答
@snake_charmer 的回答对我有用,除了保存功能(问题2:保存不工作)
如果你使用这样的写入器,它就能正常工作:
Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, metadata=dict(artist='Me'), bitrate=1800)
my_anim.save("animation.mp4", writer=writer)
可以参考这个链接:https://matplotlib.org/gallery/animation/basic_example_writer_sgskip.html
在Mac上,你可能需要通过homebrew安装FFmpeg:https://apple.stackexchange.com/questions/238295/installing-ffmpeg-with-homebrew
问题 1:图片不显示
你需要把动画对象存储在一个变量里:
my_anim = animation.ArtistAnimation(fig, myimages, interval=100)
这个要求是特定于 animation
的,和 matplotlib
中其他绘图函数不一样,后者通常可以直接用 my_plot=plt.plot()
或 plt.plot()
来使用。
这个问题在 这里 讨论得更详细。
问题 2:保存不成功
如果没有任何 animation
实例,你也无法保存图形。这是因为 save
方法属于 ArtistAnimation
类。你所做的其实是从 animation
模块调用 save
,这就导致了错误。
问题 3:出现两个窗口
最后一个问题是你会看到两个图形窗口弹出来。原因是当你调用 plt.imshow()
时,它会在当前图形上显示一张图片,但因为还没有创建任何图形,pyplot
会自动为你创建一个。当 Python 后来执行 fig = plt.figure()
这行代码时,它又创建了一个新的图形(另一个窗口),并标记为“图形 2”。把这行代码移动到你代码的开头,就能解决这个问题。
这是修改后的代码:
import matplotlib.pyplot as plt
import matplotlib.image as mgimg
from matplotlib import animation
fig = plt.figure()
# initiate an empty list of "plotted" images
myimages = []
#loops through available png:s
for p in range(1, 4):
## Read in picture
fname = "heatflow%03d.png" %p
img = mgimg.imread(fname)
imgplot = plt.imshow(img)
# append AxesImage object to the list
myimages.append([imgplot])
## create an instance of animation
my_anim = animation.ArtistAnimation(fig, myimages, interval=1000, blit=True, repeat_delay=1000)
## NB: The 'save' method here belongs to the object you created above
#my_anim.save("animation.mp4")
## Showtime!
plt.show()
(要运行上面的代码,只需在你的工作文件夹中添加3张图片,命名为 "heatflow001.png" 到 "heatflow003.png"。)
使用 FuncAnimation
的替代方法
你最开始尝试使用 FuncAnimation
是对的,因为把图片放在一个列表里会占用很多内存。我用下面的代码和上面的代码进行了比较,查看系统监控中的内存使用情况。结果显示 FuncAnimation
的方法更有效率。我相信随着你使用更多图片,这个差距会更明显。
这是第二段代码:
from matplotlib import pyplot as plt
from matplotlib import animation
import matplotlib.image as mgimg
import numpy as np
#set up the figure
fig = plt.figure()
ax = plt.gca()
#initialization of animation, plot array of zeros
def init():
imobj.set_data(np.zeros((100, 100)))
return imobj,
def animate(i):
## Read in picture
fname = "heatflow%03d.png" % i
## here I use [-1::-1], to invert the array
# IOtherwise it plots up-side down
img = mgimg.imread(fname)[-1::-1]
imobj.set_data(img)
return imobj,
## create an AxesImage object
imobj = ax.imshow( np.zeros((100, 100)), origin='lower', alpha=1.0, zorder=1, aspect=1 )
anim = animation.FuncAnimation(fig, animate, init_func=init, repeat = True,
frames=range(1,4), interval=200, blit=True, repeat_delay=1000)
plt.show()