Plots are nice but sometimes an animation is even better. Thus we take the opportunity and explain in this section how to generate simple animations. An animation is nothing else than a pile of images which are shown one after the other.
This is also exactly the way we will create animations: in a first step we draw each image (we call this a frame) and after we got all the frames, we will glue them together.
There is more than one way to make animations. We describe here two possibilities which probably are used most.
Both of these options have their respective advantages and disadvantages. Animated gif images tend to be larger in file size, but will yield good results without much technicalities. If you only have a couple of images, say up to 50 frames, then animgifs are probably the easiest way to go. (The file animwave.gif from the example below has a size of 750 KB)
For longer animations you should of course use the advantages of modern video compression techniques. But mature video encoding tools usually have a huge number of options interacting in a complex manner. It’s not easy to find the parameters which give you the best result. For example the video compression algorithms are tuned for areas and can introduce bad artefacts if the image has only a bunch sharp lines in front of a uniform background. But this is what plots look like most of the time! (By the way, the file animwave.flv from the example below has a size of 700 KB.)
We additionaly need two programs which are not part of the python installation as proposed at the beginning of this tutorial. But they may be already preinstalled on a common Linux system today.
The first of these tools is called convert which is itself part of the well known ImageMagic batch image processor. We use this tool for creating animated gif images.
(This is not the only tool that can compose gif files, you may also try gifsicle which can be found at http://www.lcdf.org/gifsicle/. My own experience is that this tool sometimes is faster, uses less memory and the resulting files are smaller in size. But don’t take this as a given.)
To encode videos we use the famous mplayer / mencoder toolbox. This is one of the most versatile tools for working with video data. Mplayer can play almost any file format and has a huge set of options. Mencoder is responsible for encoding videos and does a very good job.
Describing these tools is far beyond the scope of this document. You should take a look at the documentation available on the webpages mentioned above. The man pages also provide a clean description of the parameters and show some useful examples at the very end.
For creating the animations we relay on these external tools. We just call them from inside the python scripts. You may also call them directly at your favourite shell. The commands used in the example below are for animated gifs:
convert -delay 20 filebasename*.png animfilename.gif
where filebasename is a prefix that is common to all files you want to include in the animation. Instead of filebasename*.png you can plug in any regular expression here. Similar animfilename if the name the output file will have. The parameter delay specifies the frame rate, a value of 20 means a time delay of 20 ms between each two frames.
The command for encoding videos is much more complex (note that this is all one single line):
mencoder mf://./ filenamebase*.png -fps 5 -ofps 5 -o animfilename.flv -of lavf
-oac mp3lame -lameopts abr:br=64 -srate 22050 -ovc lavc
-lavcopts vcodec=flv:keyint=50:vbitrate=700:mbd=2:mv0:trell:v4mv:cbp:last_pred=3
-vf yadif -sws 9
where filebasename and animfilename and as above. The resulting video is of file type .flv (flash video) [2]. You should change the other parameters only if you really know what you do. (These values here are passed on since the early universe.)
The following example shows a simple wave packet propagating in a one-dimensional space.
The function make_frames() is responsible for plotting the frames. Notice how we start a new figure with the command figure() in each iteration of the for loop. This is to avoid plotting into the last frame. Also we need some bits of intelligence while saving the images. Because we use regular expressions for selecting all images that will be part of the animation, we are subject to the way the shell sorts filenames. To avoid wrong sorting we use a 5-digit numbering with zero padding here:
"wave_%05d" % i
results in file names like the following ones:
wave_00000.png wave_00001.png wave_00002.png wave_00003.png ...
and assures the files will be in the correct order. The other two functions:
make_animation_animgif(filebasename, animfilename)
and:
make_animation_video(filenamebase, animfilename)
just compose a string containing the shell commands shown above. Then they will execute this command on the system’s shell. You can copy these two functions to your own code and use them to generate animations. The rest of the code should be self-explanatory.
By the way, with code similar to the one shown here you can dynamically compose and execute any command on the system’s shell! Therefore you should be very careful with this powerful mechanism.
Note: this example do not work on windows (as long as “convert” and “mencoder” are not installed)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | from numpy import *
from pylab import *
import os
import subprocess as sp
def make_frames():
"""This function generates the images (frames) of the animation.
"""
wave = lambda x, f: sin(f*x)
envelope = lambda x, l: exp(-(x-l)**2)
x = linspace(-5,5,2000)
positions = linspace(-5,5,50)
# Plot all the individual frames
for i, pos in enumerate(positions):
y = wave(x,10) * envelope(x, pos)
z = envelope(x, pos)
# Plot the frame
figure()
plot(x, z, color=(0.5,0,0))
plot(x,-z, color=(0.5,0,0))
plot(x, y, color=(0,0,1))
ylim(-1.3, 1.3)
# Save the with a filename that ensures correct order on the filesystem
savefig("wave_"+(5-len(str(i)))*"0"+str(i)+".png")
def make_animation_video(filenamebase, animfilename):
"""A function that takes frames and composes them into a video.
"""
# The shell command controlling the 'mencoder' tool.
# Please refer to the man page for the parameters.
# Warning: Use of multiline strings with EOL escaping to reduce line length!
command = """mencoder mf://./""" + filenamebase + """*.png\
-fps 5 -ofps 5 -o """ + animfilename + """.flv\
-of lavf -oac mp3lame -lameopts abr:br=64 -srate 22050 -ovc lavc\
-lavcopts vcodec=flv:keyint=50:vbitrate=700:mbd=2:mv0:trell:v4mv:cbp:last_pred=3\
-vf yadif -sws 9"""
# Execute the command on the system's shell
proc = sp.Popen(command, shell=True)
os.waitpid(proc.pid, 0)
def make_animation_animgif(filebasename, animfilename):
"""Take a couple of images and compose them into an animated gif image.
"""
# The shell command controlling the 'convert' tool.
# Please refer to the man page for the parameters.
command = "convert -delay 20 " + filebasename + "*.png " + animfilename + ".gif"
# Execute the command on the system's shell
proc = sp.Popen(command, shell=True)
os.waitpid(proc.pid, 0)
if __name__ == "__main__":
make_frames()
make_animation_video("wave_", "animwave")
make_animation_animgif("wave_", "animwave")
|
Footnotes
[2] | For more information on this particular video format see http://en.wikipedia.org/wiki/Flv. |