Невозможно сохранить анимацию matplotlib с помощью ffmpeg

Я пытаюсь сохранить простую анимацию matplotlib из Jake Vanderplas, но я продолжаю получать OSError: [Errno 13] Permission denied.

Я должен отметить, что я сделал две небольшие модификации к примеру Джейка Вандерпласа. Я установил ffmpeg из MacPorts, поэтому добавил строку 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 и получил аналогичную трассировку. Любые предложения?


EDIT: Я понял, что я не дал правильный путь к 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>

EDIT: По какой-то причине все работает нормально. Я пробовал вещи на своем домашнем компьютере и моем рабочем компьютере, и никто не может воссоздать искаженный видеофайл, который я получил после того, как исправил проблему пути ffmpeg.


EDIT: Aaaahaaa! Я отследил этот сосунок. Иногда я импортировал модуль с plt.rcParams['savefig.bbox'] = 'tight' в нем. (Я бы никогда не использовал этот модуль, но rcParams сохраняются до тех пор, пока вы не перезапустите свой интерпретатор python.) Этот параметр заставляет видео выводить все искаженные. Я отправлю свое решение ниже.

Ответ 1

Так получается, что было два вопроса.

Проблема №1: Путь к ffmpeg был неправильным. Я думал, что мне нужно указать путь к каталогу, в котором находится ffmpeg, но мне нужно было предоставить путь до двоичного файла ffmpeg.

Проблема № 2: перед тестированием моего кода для генерации видео я бы иногда импортировал модуль с настройкой plt.rcParams['savefig.bbox'] = 'tight'. (Я не очень много думал об этом, потому что я не использовал модуль, но rcParams сохраняются до перезапуска интерпретатора 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()
anim.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])

Ответ 2

У меня были проблемы с искажением, когда я сначала (наивно) попытался изменить работу пример ответа 3, чтобы показать график в реальном времени (а также сохранить фильм).

Не совсем правильные моды ответа 3 (который работал у меня)

  • plt.ion() # взаимодействие на
  • plt.draw() и plt.show() внутри функции анимации, перед возвратом statent
  • frames = 20, interval = 200 для медленного создания графика, но все же сделать 4-секундный фильм

Теперь сюжет появляется в окне по мере его создания, но выходной фильм искажен.

Правильный шаг 2:

  • 2a: функция анимации plt.draw() внутри
  • 2b: plt.show() только после функция анимации

Теперь фильм воспроизводится без искажений.