No se puede guardar la animación matplotlib con ffmpeg

Estoy intentando guardar una animación matplotlib simple de Jake Vanderplas , pero sigo obteniendo OSError: [Errno 13] Permission denied .

Debo notar que hice dos pequeñas modificaciones al ejemplo de Jake Vanderplas. Instalé ffmpeg desde MacPorts, así que agregué la línea plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin' y me encontré con el problema descrito en ( Usando FFmpeg e IPython ), así que agregué FFwriter = animation.FFMpegWriter() .

Aquí está el código:

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

Aquí está el rastro:

 File "ani_debug.py", line 34, in  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 

También he intentado usar el python incorporado de Spyder y recibí un rastreo similar. ¿Alguna sugerencia?


EDITAR: Me di cuenta de que no le di el camino correcto a ffmpeg. Aparentemente, plt.rcParams['animation.ffmpeg_path'] no funciona de forma similar a PYTHONPATH . Debe indicar al módulo de animación exactamente dónde está ffmpeg con plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg' .

Ahora, obtengo un archivo de película que se reproducirá, pero el contenido está completamente confuso. No puedo decir lo que estoy mirando.

Aquí está el rastro:

 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.restre_region(bg_cache[a]) KeyError:  

EDITAR: Por alguna razón, todo está funcionando bien ahora. He intentado cosas en la computadora de mi hogar y en la computadora de mi trabajo, y ninguno de los dos puede recrear el archivo de video confuso que obtuve después de haber solucionado el problema de la ruta de acceso de ffmpeg.


EDITAR: Aaaahaaa! He rastreado este lechón hacia abajo. A veces importaba un módulo que tenía plt.rcParams['savefig.bbox'] = 'tight' en él. (Nunca usaría ese módulo, pero rcParams persiste, hasta que reinicies tu intérprete de python). Esa configuración hace que el video salga todo confuso. Voy a publicar mi solución a continuación.

Así que resulta que hubo dos problemas.

Problema nº 1: la ruta a ffmpeg estaba equivocada. Pensé que necesitaba proporcionar la ruta al directorio en el que reside ffmpeg, pero necesitaba proporcionar la ruta hasta el binario ffmpeg.

Problema nº 2: antes de probar mi código para generar videos, a veces importaba un módulo con la configuración plt.rcParams['savefig.bbox'] = 'tight' . (No pensé mucho en eso, porque no usé el módulo, pero rcParams persiste hasta que reinicies el intérprete de python). Este plt.rcParams['savefig.bbox'] = 'tight' hace que el archivo de video se guarde sin cualquier error, pero los cuadros están distorsionados cuando intentas reproducir el video. A pesar de que me tomó toda la noche para rastrear esto, resulta que este es un problema conocido .

Aquí está la solución actualizada que crea un archivo de video para mí con una onda sinusoidal agradable que se traduce.

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

Tuve problemas de confusión cuando primero (ingenuamente) intenté modificar el ejemplo de trabajo de la respuesta 3 para mostrar el gráfico en tiempo real (además de mantener la película).

Mods no correctos de respuesta 3 (que funcionó para mí)

  1. plt.ion () # interacción en
  2. plt.draw () y plt.show () dentro de la función animar, antes de devolver el estado.
  3. fotogtwigs = 20, intervalo = 200 para ralentizar un poco la creación de gráficos, pero aún así se hace una película de 4 segundos

Ahora la ttwig aparece en una ventana a medida que se crea, pero la película de salida está distorsionada.

Paso 2 correcto:

  • 2a: plt.draw () dentro de la función animar
  • 2b: plt.show () justo después de la función animar

Ahora la película se reproduce intacta.

Además de la respuesta de Stretch , encontré que algunos de los parámetros que se pasan a anim.save() no parecen lograr el efecto deseado. Específicamente, fps fue 5 (predeterminado) y no los 30 que se configuraron. Al pasar fps=30 a animation.FFMpegWriter funciona.

Asi que:

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

Tenga en cuenta que el video dura ahora 7 segundos (200 cuadros a 30 fps) en lugar de 40 segundos (200 cuadros a 5 fps). Tenga en cuenta también que un valor predeterminado de 5 fps corresponde al intervalo predeterminado de 200 ms / cuadro en FuncAnimation , y estrictamente, el intervalo de animación de 20 ms utilizado aquí corresponde a 50 fps.

Para aquellos que luchan por lograr una mejor calidad de video, también es posible pasar una tasa de bits (entero en kbps) a animation.FFMpegWriter , por ejemplo:

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

Intenté varios extra_args en un esfuerzo por lograr una mejor calidad, sin mucho éxito.

Gracias Stretch por su valiosa respuesta. Encontré que mencionar argumentos adicionales dentro de anim.save () produce un error. Por lo tanto, el código se actualiza como se muestra a continuación,

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

Espero que esto ayude a alguien que intenta guardar gráficos de animación en formato .mp4.