【问题标题】:matplotlib animation: write to png files without third party modulematplotlib 动画:在没有第三方模块的情况下写入 png 文件
【发布时间】:2017-05-04 23:01:06
【问题描述】:

matplotlib 中的动画模块通常需要 FFmpeg、mencoder 或 imagemagik 等第三方模块才能将动画保存到文件中(例如:https://stackoverflow.com/a/25143651/5082048)。

即使是 matplotlib 中的 MovieWriter 类,似乎也是以一种将包含第三方模块的方式构建的(启动和关闭进程,通过管道进行通信):http://matplotlib.org/api/animation_api.html#matplotlib.animation.MovieWriter

我正在寻找一种方法,如何将matplotlib.animation.FuncAnimation 对象框架保存到 png - 直接在 python 中。之后,我想使用这种方法在 iPython 笔记本中将 .png 文件显示为动画:https://github.com/PBrockmann/ipython_animation_javascript_tool/

因此我的问题是:

  • 如何在不使用第三方模块的情况下将matplotlib.animation.FuncAnimation 对象直接保存到.png 文件?
  • 是否为此用例实现了编写器类?
  • 如何从 FuncAnimation 对象中逐帧获取图形对象(以便我自己保存)?

编辑:matplotlib.animation.FuncAnimation 对象已给出,任务是使用纯 Python 保存它的帧。不幸的是,我无法按照 ImportanceOfBeingErnest 的建议更改底层动画功能。

【问题讨论】:

    标签: python animation matplotlib


    【解决方案1】:

    虽然这看起来有点复杂,但保存动画的帧可以在动画本身内轻松完成。

    import matplotlib
    import matplotlib.pyplot as plt
    import matplotlib.animation
    import numpy as np
    
    def animate(i):
        line.set_ydata(np.sin(2*np.pi*i / 50)*np.sin(x))
        #fig.canvas.draw() not needed see comment by @tacaswell
        plt.savefig(str(i)+".png")
        return line,
    
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1,1)
    x = np.linspace(0, 2*np.pi, 200)
    line, = ax.plot(x, np.zeros_like(x))
    plt.draw()
    
    ani = matplotlib.animation.FuncAnimation(fig, animate, frames=5, repeat=False)
    plt.show()
    

    注意repeat = False 参数,它会阻止动画连续运行并重复将相同的文件写入磁盘。

    请注意,如果您愿意放宽“无外部包”的限制,您可以使用 imagemagick 来保存 pngs

    ani.save("anim.png", writer="imagemagick")
    

    这将保存文件 anim-1.png、anim-2.png 等。

    最后注意当然有easier methods to show an animation in a jupyter notebook

    【讨论】:

    • 是的,你完全正确。我不采用这种方法的原因是,我基本上只能直接访问 ani 对象,而其他一切都已经设置好了。因此,我特别在寻找一种直接访问该对象的方法。
    • 我明白了。这将是添加到问题中的重要信息。尽管原则上这应该是一件容易的事,因为每个电影作者都必须将临时图像保存在某个地方,但我目前不知道有任何其他解决方案。
    • 你又是对的。抱歉,我不知道我的问题不清楚。
    • @tacaswell 我不明白为什么这是一个坏主意。据我所知,该方法没有任何问题,更重要的是,这是我目前看到的将动画保存到一堆 png 文件的唯一方法。非常欢迎您提供更好的方法,如果有的话。当然,最好将此方法记录在 matplotlib 文档的某个地方。
    • @tacaswell 好点,我相应地编辑了答案。除此之外,我认为您对“直截了当”和“坏主意”的概念有点过于曲解了。总之,我只想说,在ani.save(some arguments to allow pngs to be saved) 的意义上,拥有一个有据可查的方法真的很有帮助。
    【解决方案2】:

    您想查看FileMovieWriter 子类(请参阅http://matplotlib.org/2.0.0rc2/api/animation_api.html#writer-classes)您可能想要子类FileMoveWriter,类似

    import matplotlib.animation as ma
    
    
    class BunchOFiles(ma.FileMovieWriter):
        def setup(self, fig, dpi, frame_prefix):
            super().setup(fig, dpi, frame_prefix, clear_temp=False)
    
        def _run(self):
            # Uses subprocess to call the program for assembling frames into a
            # movie file.  *args* returns the sequence of command line arguments
            # from a few configuration options.
            pass
    
        def grab_frame(self, **savefig_kwargs):
            '''
            Grab the image information from the figure and save as a movie frame.
            All keyword arguments in savefig_kwargs are passed on to the 'savefig'
            command that saves the figure.
            '''
    
            # Tell the figure to save its data to the sink, using the
            # frame format and dpi.
            with self._frame_sink() as myframesink:
                self.fig.savefig(myframesink, format=self.frame_format,
                                 dpi=self.dpi, **savefig_kwargs)
    
        def cleanup(self):
            # explictily skip a step in the mro
            ma.MovieWriter.cleanup(self)
    

    (这未经测试,最好只实现一个实现savinggrab_framefinishedsetup 的类)

    【讨论】:

      【解决方案3】:

      如果不进行修改,我无法获得 tacaswell 的工作答案。所以,这是我的看法。

      from matplotlib.animation import FileMovieWriter
      
      
      class BunchOFiles(FileMovieWriter):
          supported_formats = ['png', 'jpeg', 'bmp', 'svg', 'pdf']
      
          def __init__(self, *args, extra_args=None, **kwargs):
              # extra_args aren't used but we need to stop None from being passed
              super().__init__(*args, extra_args=(), **kwargs)
      
          def setup(self, fig, dpi, frame_prefix):
              super().setup(fig, dpi, frame_prefix, clear_temp=False)
              self.fname_format_str = '%s%%d.%s'
              self.temp_prefix, self.frame_format = self.outfile.split('.')
      
          def grab_frame(self, **savefig_kwargs):
              '''
              Grab the image information from the figure and save as a movie frame.
              All keyword arguments in savefig_kwargs are passed on to the 'savefig'
              command that saves the figure.
              '''
      
              # Tell the figure to save its data to the sink, using the
              # frame format and dpi.
              with self._frame_sink() as myframesink:
                  self.fig.savefig(myframesink, format=self.frame_format,
                                   dpi=self.dpi, **savefig_kwargs)
      
          def finish(self):
              self._frame_sink().close()
      

      我们可以保存一组文件:

      anim.save('filename.format', writer=BunchOFiles())
      

      它会以'filename{number}.format'的形式保存文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-04
        • 1970-01-01
        • 2020-09-27
        • 1970-01-01
        • 1970-01-01
        • 2011-02-02
        • 2014-12-02
        相关资源
        最近更新 更多