Python 无法使用 ffmpeg 保存 matplotlib 动画

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23074484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 02:12:35  来源:igfitidea点击:

Cannot save matplotlib animation with ffmpeg

pythonanimationmatplotlib

提问by Stretch

I am trying to save a simple matplotlib animation from Jake Vanderplas, but I keep getting OSError: [Errno 13] Permission denied.

我正在尝试从Jake Vanderplas保存一个简单的 matplotlib 动画,但我不断收到OSError: [Errno 13] Permission denied.

I should note that I made two small modifications to Jake Vanderplas's example. I installed ffmpeg from MacPorts so I added the line plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin'and I ran into the problem discussed in (Using FFmpeg and IPython), so I added FFwriter = animation.FFMpegWriter().

我应该注意到我对 Jake Vanderplas 的例子做了两个小的修改。我从 MacPorts 安装了 ffmpeg,所以我添加了该行plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin',但遇到了(使用 FFmpeg 和 IPython)中讨论的问题,所以我添加了FFwriter = animation.FFMpegWriter().

Here is the code:

这是代码:

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'])

Here is the traceback:

这是回溯:

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

I have also tried using Spyder's built-in python and received a similar traceback. Any suggestions?

我也尝试过使用 Spyder 的内置 python 并收到类似的回溯。有什么建议?



EDIT: I realized that I did not give the proper path to ffmpeg. Apparently, plt.rcParams['animation.ffmpeg_path']does not work similar to PYTHONPATH. You must tell the animation module exactly where ffmpeg is with plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'.

编辑:我意识到我没有给出 ffmpeg 的正确路径。显然,plt.rcParams['animation.ffmpeg_path']PYTHONPATH. 你必须告诉动画模块 ffmpeg 的确切位置plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'

Now, I get a movie file that will play, but the content is completely garbled. I can't tell what I am looking at.

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

Here is the traceback:

这是回溯:

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>


EDIT: For some reason, everything is working fine now. I have tried things on my home computer and my work computer, and neither one can recreate the garbled video file I got after I fixed the ffmpeg path issue.

编辑:出于某种原因,现在一切正常。我已经在我的家用电脑和我的工作电脑上尝试过一些东西,但在我修复了 ffmpeg 路径问题后,没有人能够重现我得到的乱码视频文件。



EDIT: Aaaahaaa! I tracked this sucker down. Sometimes I would import a module that had plt.rcParams['savefig.bbox'] = 'tight'in it. (I would never use that module, but rcParams persist, until you restart your python interpreter.) That setting causes the video to come out all garbled. I will post my solution below.

编辑:啊啊啊啊!我追踪到了这个傻瓜。有时我会导入其中包含的模块plt.rcParams['savefig.bbox'] = 'tight'。(我永远不会使用该模块,但 rcParams 会一直存在,直到您重新启动 Python 解释器。)该设置会导致视频出现乱码。我将在下面发布我的解决方案。

采纳答案by Stretch

So it turns out there were two issues.

所以事实证明有两个问题。

Issue #1: The path to ffmpeg was wrong. I thought I needed to provide the path to the directory that ffmpeg resides in, but I needed to provide the path all the way to the ffmpeg binary.

问题 #1:ffmpeg 的路径错误。我以为我需要提供 ffmpeg 所在目录的路径,但我需要提供一直到 ffmpeg 二进制文件的路径。

Issue #2: Prior to testing out my code to generate videos, I sometimes would import a module with the setting plt.rcParams['savefig.bbox'] = 'tight'. (I did not think much of it, because I did not use the module, but rcParams persist until you restart the python interpreter.) This plt.rcParams['savefig.bbox'] = 'tight'causes the video file to save without any errors, but the frames are all garbled when you try to play the video. Although it took me all evening to track this down, it turns out this is a known issue.

问题#2:在测试我的代码以生成视频之前,我有时会导入一个设置为plt.rcParams['savefig.bbox'] = 'tight'. (我没多想,因为我没有用模块,但是rcParams一直存在,直到你重启python解释器。)这plt.rcParams['savefig.bbox'] = 'tight'导致视频文件保存没有任何错误,但是当你尝试播放时帧都是乱码该视频。虽然我花了整晚才找到这个问题,但事实证明这是一个已知问题

Here is the updated solution that creates a video file for me with a nice, translating, sine wave.

这是更新的解决方案,它为我创建了一个带有漂亮的平移正弦波的视频文件。

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()
anim.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])

回答by Erik Kruus

I had garbling issues when I first (naively) tried to modify the working example of answer 3 to show the graph in realtime (as well as keep the movie).

当我第一次(天真地)尝试修改答案 3 的工作示例以实时显示图形(以及保留电影)时,我遇到了乱码问题。

Not quite right mods of answer 3 (which worked for me)

答案 3 的模式不太正确(对我有用)

  1. plt.ion() # interaction on
  2. plt.draw() andplt.show() inside animate function, before return statent
  3. frames=20, interval=200 to slow graph creation a bit, but still make a 4 second movie
  1. plt.ion() # 交互
  2. plt.draw()plt.show() 在 animate 函数内,在 return statent 之前
  3. 帧 = 20,间隔 = 200 以稍微减慢图形创建速度,但仍可制作 4 秒的电影

Now plot shows up in a window as it is being created, but the output movie is garbled.

现在绘图在创建时显示在窗口中,但输出的电影是乱码。

Correct step 2:

正确的步骤2:

  • 2a: plt.draw() insideanimate function
  • 2b: plt.show() just afterthe animate function
  • 2a:动画函数内的plt.draw()
  • 2b: plt.show() 就animate 函数之后

Now movie plays back ungarbled.

现在电影播放无乱码。

回答by wheeled

Further to Stretch's answer, I found that some of the parameters being passed to anim.save()do not appear to achieve the desired effect. Specifically fpswas 5 (default) and not the 30 being set. By passing fps=30to animation.FFMpegWriterit does work.

进一步对Stretch 的回答,我发现传递给的一些参数anim.save()似乎没有达到预期的效果。特别fps是 5(默认值)而不是设置的 30。通过传递fps=30animation.FFMpegWriter它确实有效。

So:

所以:

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

Note that the video is now 7 seconds long (200 frames @ 30 fps) rather than 40 seconds (200 frames @ 5 fps). Note also that a default of 5 fps corresponds to the default 200 ms/frame interval in FuncAnimation, and strictly speaking the 20 ms animation interval used here corresponds to 50 fps.

请注意,视频现在是 7 秒长(200 帧 @ 30 fps)而不是 40 秒(200 帧 @ 5 fps)。另请注意,默认 5 fps 对应于 中的默认 200 毫秒/帧间隔FuncAnimation,严格来说此处使用的 20 毫秒动画间隔对应于 50 fps。

For those struggling to achieve better video quality, it is also possible to pass a bitrate (integer in kbps) to animation.FFMpegWriter, eg:

对于那些努力获得更好视频质量的人,还可以将比特率(以 kbps 为单位的整数)传递给animation.FFMpegWriter,例如:

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

I tried various extra_args in an effort to achieve better quality, without much success.

为了获得更好的质量,我尝试了各种 extra_args,但没有取得多大成功。

回答by Paul Thomas

Thank you Stretch for your valuable answer. I found that mentioning extra args inside anim.save() results in error. Hence the code is updated as shown below,

感谢 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()

Hope this might helps someone trying to save animation plots to .mp4 format.

希望这可以帮助尝试将动画图保存为 .mp4 格式的人。