web-dev-qa-db-fra.com

Impossible de sauvegarder une animation matplotlib avec ffmpeg

J'essaie de sauvegarder une animation matplotlib simple de Jake Vanderplas , mais je continue à obtenir OSError: [Errno 13] Permission denied

Je dois noter que j'ai apporté deux petites modifications à l'exemple de Jake Vanderplas. J'ai installé ffmpeg à partir de MacPorts, alors j'ai ajouté la ligne plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin' et j'ai rencontré le problème décrit dans ( Utilisation de FFmpeg et IPython ). J'ai donc ajouté FFwriter = animation.FFMpegWriter().

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

Voici la 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

J'ai également essayé d'utiliser le python intégré de Spyder et j'ai reçu une trace similaire. Aucune suggestion?


EDIT: Je me suis rendu compte que je n’avais pas donné le bon chemin à ffmpeg. Apparemment, plt.rcParams['animation.ffmpeg_path'] ne fonctionne pas de la même manière que PYTHONPATH. Vous devez indiquer au module d'animation exactement où se trouve ffmpeg avec plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'.

Maintenant, je reçois un fichier vidéo qui va être lu, mais le contenu est complètement brouillé. Je ne peux pas dire ce que je regarde.

Voici la 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: Pour une raison quelconque, tout fonctionne bien maintenant. J'ai essayé des choses sur mon ordinateur à la maison et mon ordinateur de travail, et aucun ne peut recréer le fichier vidéo brouillé que j'ai obtenu après avoir résolu le problème du chemin d'accès ffmpeg.


EDIT: Aaaahaaa! J'ai traqué cette ventouse. Parfois, j'importais un module contenant plt.rcParams['savefig.bbox'] = 'tight'. (Je n'utiliserais jamais ce module, mais rcParams persiste jusqu'à ce que vous redémarriez votre interpréteur python.) Ce paramètre rend la vidéo complètement brouillée. Je vais poster ma solution ci-dessous.

13
Stretch

Donc, il s'avère qu'il y avait deux problèmes. 

Problème n ° 1: le chemin d'accès à ffmpeg était incorrect. Je pensais que je devais fournir le chemin du répertoire dans lequel ffmpeg réside, mais je devais fournir le chemin jusqu'au fichier binaire de ffmpeg. 

Problème n ° 2: avant de tester mon code pour générer des vidéos, il m'arrivait parfois d'importer un module avec le paramètre plt.rcParams['savefig.bbox'] = 'tight'. (Je n'y ai pas beaucoup réfléchi, car je n'ai pas utilisé le module, mais rcParams persiste jusqu'à ce que vous redémarriez l'interpréteur python.) Ce plt.rcParams['savefig.bbox'] = 'tight' enregistre le fichier vidéo sans erreur, mais les images sont toutes confuses lorsque vous essayez de le faire. jouer la vidéo. Bien que cela ait pris toute la soirée pour le retrouver, il s’avère que c’est un problème connu .

Voici la solution mise à jour qui crée un fichier vidéo pour moi avec une onde sinusoïdale sympa et traduisant.

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

J'ai eu des problèmes de garbling quand j'ai d'abord essayé (naïvement) de modifier l'exemple de travail De la réponse 3 afin d'afficher le graphique en temps réel (ainsi que de conserver le film).

Pas tout à fait correct mods de réponse 3 (qui a fonctionné pour moi)

  1. plt.ion () # interaction on
  2. plt.draw () et plt.show () dans la fonction animate, avant le retour de l'état
  3. images = 20, intervalle = 200 pour ralentir un peu la création du graphique, mais continue de faire un film de 4 secondes

Maintenant, le tracé apparaît dans la fenêtre en cours de création, , Mais le film en sortie est tronqué.

Étape 2 correcte:

  • 2a: plt.draw () inside animate function
  • 2b: plt.show () juste après la fonction animate

Maintenant, le film est lu sans problème.

0
Erik Kruus

Suite à La réponse de Stretch , j’ai constaté que certains des paramètres transmis à anim.save() ne semblent pas produire l’effet souhaité. fps était spécifiquement égal à 5 ​​(valeur par défaut) et non à la valeur 30 définie. En passant fps=30 à animation.FFMpegWriter, cela fonctionne.

Alors: 

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

Notez que la vidéo dure maintenant 7 secondes (200 images à 30 ips) au lieu de 40 secondes (200 images à 5 ips). Notez également qu'une valeur par défaut de 5 ips correspond à l'intervalle par défaut de 200 ms/image dans FuncAnimation et que, à proprement parler, l'intervalle d'animation de 20 ms utilisé ici correspond à 50 ips.

Pour ceux qui ont du mal à obtenir une meilleure qualité vidéo, il est également possible de passer un débit (entier en kbps) à animation.FFMpegWriter, par exemple:

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

J'ai essayé divers extra_args dans le but d'obtenir une meilleure qualité, sans grand succès.

0
wheeled

Merci Stretch pour votre précieuse réponse. J'ai trouvé que mentionner des arguments supplémentaires dans anim.save () entraîne une erreur. Par conséquent, le code est mis à jour comme indiqué ci-dessous,

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

J'espère que cela aidera quelqu'un qui essaiera de sauvegarder les graphiques d'animation au format .mp4.

0
Paul Thomas