将matplotlib动画保存为动态gif非常缓慢

7
我正在制作一个收敛过程的动画,这个过程是在IPython 3.1笔记本中模拟的。我用matplotlib动画来可视化散点图结果,并通过ImageMagick将其写成动态gif文件。总共有3000帧,每帧大约有5000个点。
我不确定matplotlib如何创建这些动画文件,但它似乎会缓存一堆帧,然后一起写出它们——当我查看CPU使用情况时,开始时由Python主导,然后由convert主导。
写入gif的速度非常慢。在现代MacBook Pro的SSD上,将70MB的文件写入需要超过一个小时的时间。'convert'占用了4(8个超线程)核心机器中90%的一个核心。
写入前65MB需要大约15分钟,而最后5MB需要超过2小时的时间。
我认为代码的有趣部分如下——如果还有其他有用的信息,请让我知道。
def updateAnim(i,cg,scat,mags):
    if mags[i]==0: return scat,
    cg.convergeStep(mags[i])
    scat.set_offsets(cg._chrgs[::2,0:2])
    return scat,

fig=plt.figure(figsize=(6,10))
plt.axis('equal')
plt.xlim(-1.2,1.2);plt.ylim(-1,3)
c=np.where(co._chrgs[::2,3]>0,'blue','red')
scat=plt.scatter(co._chrgs[::2,0],co._chrgs[::2,1],s=4,color=c,marker='o',alpha=0.25);
ani=animation.FuncAnimation(fig,updateAnim,frames=mags.size,fargs=(co,scat,mags),blit=True);
ani.save('Files/Capacitance/SpherePlateAnimation.gif',writer='imagemagick',fps=30);

任何想法瓶颈可能是什么,或者我如何加速它? 我希望写出时间相对于模拟时间很小。 版本:ImageMagick 6.9.0-0 Q16 x86_64 2015-05-30 http://www.imagemagick.org 版权:版权所有(C)1999-2014 ImageMagick Studio LLC 特点:DPC模块 代表(内置):bzlib cairo djvu fftw fontconfig freetype gslib gvc jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png ps rsvg tiff webp wmf x xml zlib ps -aef报告: convert -size 432x720 -depth 8 -delay 3.3333333333333335 -loop 0 rgba:- Files/Capacitance/SpherePlateAnimation.gif

我不熟悉Matplotlib,但我非常熟悉ImageMagick。你能看到convert是如何被调用的吗?我的意思是它接收了什么参数? - Mark Setchell
您可以在终端中运行 ps -aef | grep convert 命令来查看参数。 - Mark Setchell
已将转换调用参数添加到问题中。感谢您查看此内容! - Omegaman
FYI,在OSX上使用blit = True存在已知问题。(http://matplotlib.1069221.n5.nabble.com/Using-blit-True-in-matplotlib-animation-td45644.html) - Orko
2个回答

5

更新

在进行下面的任何操作之前,请先阅读原始答案。

如果您想深入调试此问题,您可以将ImageMagick部分分离出来并确定问题所在。要做到这一点,我会像这样定位您的ImageMagick convert 程序:

which convert    # result may be "/usr/local/bin/convert"

然后进入所在的目录,例如:

cd /usr/local/bin

现在将原始的convert程序保存为convert.real - 你随时可以通过反转下面的最后两个参数来改回去:
mv convert convert.real

现在,请将以下文件保存为convert
#!/bin/bash
dd bs=128k > $HOME/plot.rgba 2> /dev/null

并通过以下方式使其可执行

chmod +x convert

现在,当您再次运行matplotlib时,它将执行上面的脚本而不是ImageMagick,并且该脚本将在名为plot.rgba的文件中保存原始RGBA数据到您的登录目录中。这将告诉您两件事...首先,您将看到如果matplotlib现在运行得更快,因为不再有ImageMagick处理;其次,您将看到文件大小是否约为4GB,就像我猜测的那样。
现在,您可以使用ImageMagick在matplotlib完成后使用此文件进行处理,并设置10GB的内存限制。
convert.real -limit memory 10000000 -size 432x720 -depth 8 -delay 3.33 -loop 0  $HOME/plot.rgba Files/Capacitance/SpherePlateAnimation.gif

你也可以考虑将文件分成2个(或4个),使用dd并同时处理两个部分,然后将它们附加在一起,看看是否有帮助。如果您想调查该选项,请提出要求。
原始答案:
我在这里大声说出来,希望能直接帮助您或激发其他人的头脑来解决问题...
从您分享的命令行中可以看出,matplotlib正在直接写入ImageMagick的convert工具的stdin - 我可以从RGBA:-参数中看到,这告诉我它正在以原始值的形式发送RGB加Alpha透明度到stdin
这意味着没有中间文件,我可以建议将其放在RAM磁盘上,这是我的评论方向...
第二件事是,由于原始像素数据正在被发送,因此每个像素都是由matplotlib计算和发送的,因此与您模拟中的5,000个点不变 - 因此没有减少或优化点数的必要。
另一个要注意的问题是,您正在使用ImageMagick的16位量化版本(Q16在您的版本字符串中)。这实际上使内存要求加倍,因此,如果您可以轻松重新编译ImageMagick以获得8位量子深度,则可能会有所帮助。
现在,让我们看一下输入流,RGBA -depth 8表示每个像素4个字节,每帧432x720像素,或每帧1.2MB。现在,您有3,000个帧,因此最少需要3.6GB,加上75MB的输出文件。我怀疑这超过了ImageMagick的自然内存限制,这就是为什么它在末尾变慢的原因,因此我的建议是检查ImageMagick的内存限制并考虑将其增加到4GB-6GB或更多。
要检查内存和其他资源限制:
identify -list resource

Resource limits:
  Width: 214.7MP
  Height: 214.7MP
  Area: 4.295GP
  Memory: 2GiB    <---
  Map: 4GiB
  Disk: unlimited
  File: 192
  Thread: 1
  Throttle: 0
  Time: unlimited

由于无法在matplotlib执行的命令行上提高内存限制,因此您可以通过在启动matplotlib之前导出环境变量来完成此操作,例如:

export MAGICK_MEMORY_LIMIT=4294967296

identify -list resource

Resource limits:
  Width: 214.7MP
  Height: 214.7MP
  Area: 4.295GP
  Memory: 4GiB    <---
  Map: 4GiB
  Disk: unlimited
  File: 192
  Thread: 1
  Throttle: 0
  Time: unlimited

您也可以在policy.xml文件中更改它,但这需要更多的操作步骤,因此请先尝试这种方法,如果遇到困难,请询问!请给我反馈,因为根据此方法是否有效,我可能会建议其他事项。请还运行identify -list configure并编辑您的问题,在那里粘贴输出结果。

看起来已经解决了!我现在运行了两个测试,写出时间只需要几分钟而不是几小时——太神奇了。Matplotlib应该允许我使用extra_args关键字将“-limit memory 10GiB”传递给animate.save(),但似乎并没有起作用。rcParams中的animate.convert_args也不行。不过,设置环境变量就可以解决问题,我可以从Python中完成这个操作。 - Omegaman

0

Mark Setchell的回答提供了一个很好的解释,但是导出更高的内存限制并没有起作用。我实际上建议改变policy.xml,唯一棘手的部分是找到它在你的系统上保存的位置。这个回答列出了一些位置,但最可能在

/etc/ImageMagick-6/policy.xml

打开它,编辑那一行说的话

<policy domain="resource" name="memory" value="256MiB"/>

转换成类似于这样的东西

<policy domain="resource" name="memory" value="4GiB"/>

或者设置任何大于未处理gif大小的限制。如果没有,convert子进程将被终止。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接