无法用ffmpeg保存matplotlib动画

19 投票
4 回答
41819 浏览
提问于 2025-04-18 02:46

我正在尝试保存一个简单的matplotlib动画,来源于Jake Vanderplas的教程,但我总是遇到一个错误:OSError: [Errno 13] Permission denied

我需要说明的是,我对Jake Vanderplas的示例做了两个小修改。我通过MacPorts安装了ffmpeg,所以我添加了这一行:plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin'。然后我遇到了在(使用FFmpeg和IPython)中讨论的问题,所以我又添加了FFwriter = animation.FFMpegWriter()

这是我的代码:

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin'

fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

def init():
    line.set_data([], [])
    return line,

def animate(i):
    x = np.linspace(0, 2, 1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))
    line.set_data(x, y)
    return line,

anim = animation.FuncAnimation(fig, animate, init_func=init,
                           frames=200, interval=20, blit=True)

FFwriter = animation.FFMpegWriter()
anim.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])

这是错误追踪信息:

File "ani_debug.py", line 34, in <module>
  anim.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site- packages/matplotlib/animation.py", line 712, in save
  with writer.saving(self._fig, filename, dpi):
File "/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/contextlib.py", line 17, in __enter__
  return self.gen.next()
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.py", line 169, in saving
  self.setup(*args)
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.py", line 159, in setup
  self._run()
File "/Users/Ben/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.py", line 186, in _run
  stdin=subprocess.PIPE)
File "/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/subprocess.py", line 709, in __init__
  errread, errwrite)
File "/Applications/Canopy.app/appdata/canopy-1.3.0.1715.macosx-x86_64/Canopy.app/Contents/lib/python2.7/subprocess.py", line 1326, in _execute_child
  raise child_exception
OSError: [Errno 13] Permission denied

我还尝试过使用Spyder自带的Python,结果也得到了类似的错误追踪信息。有什么建议吗?


编辑:我意识到我没有给ffmpeg提供正确的路径。显然,plt.rcParams['animation.ffmpeg_path']的用法和PYTHONPATH不一样。你必须准确告诉动画模块ffmpeg的具体位置,像这样:plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'

现在,我得到了一个可以播放的电影文件,但内容完全混乱。我根本看不出我在看什么。

这是错误追踪信息:

Exception in Tkinter callback
Traceback (most recent call last):
  File "Tkinter.pyc", line 1470, in __call__
  File "Tkinter.pyc", line 531, in callit
  File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/backends/backend_tkagg.py", line 141, in _on_timer
    TimerBase._on_timer(self)
  File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/backend_bases.py", line 1203, in _on_timer
    ret = func(*args, **kwargs)
  File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 876, in _step
    still_going = Animation._step(self, *args)
  File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 735, in _step
    self._draw_next_frame(framedata, self._blit)
  File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 753, in _draw_next_frame
    self._pre_draw(framedata, blit)
  File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 766, in _pre_draw
    self._blit_clear(self._drawn_artists, self._blit_cache)
  File "/Applications/Spyder.app/Contents/Resources/lib/python2.7/matplotlib/animation.py", line 806, in _blit_clear
    a.figure.canvas.restore_region(bg_cache[a])
KeyError: <matplotlib.axes.AxesSubplot object at 0x104cfb150>

编辑:出于某种原因,现在一切都正常了。我在我的家用电脑和工作电脑上都试过,两个都无法重现我在修复ffmpeg路径问题后得到的混乱视频文件。


编辑:啊哈!我找到了问题所在。有时候我会导入一个模块,其中包含plt.rcParams['savefig.bbox'] = 'tight'。虽然我从来不使用那个模块,但rcParams的设置会一直保留,直到你重启Python解释器。这个设置会导致视频输出混乱。我会在下面发布我的解决方案。

4 个回答

0

根据Stretch的回答,我发现传给anim.save()的一些参数似乎没有达到预期的效果。具体来说,fps的值是5(默认值),而不是设置的30。通过将fps=30传给animation.FFMpegWriter,效果就好了。

所以:

FFwriter = animation.FFMpegWriter(fps=30)
anim.save('basic_animation.mp4', writer=FFwriter, extra_args=['-vcodec', 'libx264'])

注意,现在视频的时长是7秒(200帧 @ 30 fps),而不是40秒(200帧 @ 5 fps)。另外,默认的5 fps对应于FuncAnimation中的200毫秒/帧的间隔,而这里使用的20毫秒动画间隔实际上对应于50 fps。

对于那些想要提高视频质量的人,还可以将比特率(以kbps为单位的整数)传给animation.FFMpegWriter,例如:

FFwriter = animation.FFMpegWriter(fps=30, bitrate=2000)

我尝试了各种extra_args来提高质量,但效果不太理想。

0

我刚开始尝试修改一个示例时,遇到了乱码问题。这个示例是来自Stretch的回答,我想让图表实时显示,同时还能保存成视频。

我对Stretch的回答做了一些不太正确的修改(但对我来说有效):

  1. plt.ion() 开启交互模式
  2. animate函数里,plt.draw() plt.show() 要放在 return 语句之前
  3. 设置frames=20, interval=200,这样图表生成会慢一点,但仍然能做出一个4秒的视频

现在图表在创建时会显示在一个窗口里,但输出的视频却是乱码。

正确的第二步:

  • 2a: plt.draw() 要放在 animate 函数里面
  • 2b: plt.show() 要放在 animate 函数之后

现在视频播放时没有乱码了。

1

感谢Stretch提供的宝贵答案。我发现如果在anim.save()里面提到额外的参数,就会出现错误。所以代码更新成了下面这样,

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
plt.rcParams['animation.ffmpeg_path'] = r'I:\FFmpeg\bin\ffmpeg' #make sure you download FFmpeg files for windows 10 from https://ffmpeg.zeranoe.com/builds/

fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

def init():
    line.set_data([], [])
    return line,

def animate(i):
    x = np.linspace(0, 2, 1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))
    line.set_data(x, y)
    return line,

anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)

FFwriter=animation.FFMpegWriter(fps=30, extra_args=['-vcodec', 'libx264'])
anim.save(r'I:\Understanding_objective functions\test\basic_animation.mp4', writer=FFwriter)

plt.show()

希望这能帮助到一些想把动画图保存为.mp4格式的人。

25

结果发现有两个问题。

问题一:ffmpeg的路径设置错了。我原以为只需要提供ffmpeg所在文件夹的路径,但其实我需要提供到ffmpeg程序本身的完整路径。

问题二:在测试生成视频的代码之前,我有时会导入一个模块,并设置 plt.rcParams['savefig.bbox'] = 'tight'。我当时没太在意,因为我并没有使用这个模块,但这个设置会一直保留,直到你重启python解释器。这个 plt.rcParams['savefig.bbox'] = 'tight' 设置虽然可以让视频文件保存得没有错误,但播放视频时画面却会变得很乱。虽然我花了整个晚上才找到这个问题,但结果发现这是一个已知的问题

这里是更新后的解决方案,可以为我创建一个带有漂亮、变化的正弦波的视频文件。

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'

fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

def init():
    line.set_data([], [])
    return line,

def animate(i):
    x = np.linspace(0, 2, 1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))
    line.set_data(x, y)
    return line,

anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)

FFwriter = animation.FFMpegWriter(fps=30, extra_args=['-vcodec', 'libx264'])
anim.save('basic_animation.mp4', writer=FFwriter)

撰写回答