I am trying to save a simple matplotlib animation from Jake Vanderplas, but I keep getting OSError: [Errno 13] Permission denied
.
I should note that I
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.
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.
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'])
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 fps
was 5 (default) and not the 30 being set. By passing fps=30
to animation.FFMpegWriter
it does work.
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.
For those struggling to achieve better video quality, it is also possible to pass a bitrate (integer in kbps) to animation.FFMpegWriter
, eg:
FFwriter = animation.FFMpegWriter(fps=30, bitrate=2000)
I tried various extra_args in an effort to achieve better quality, without much success.
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).
Not quite right mods of answer 3 (which worked for me)
Now plot shows up in a window as it is being created, but the output movie is garbled.
Correct step 2:
Now movie plays back ungarbled.
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,
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.