To create a flipbook (or frame-by-frame) animation from a sequence of images, there are many command-line and GUI-based tools. An overview is given on the page "Creating Animations for Presentations and the web". Here, I'll discuss one method, which I called makeQuicktime
.
This is a python script which allows you to assemble a sequence of images into a Quicktime movie that may contain a separate frame duration for each image. In frame-by-frame animation, it is inefficient to keep an image on the screen for long periods by just copying that frame multiple times to fit it into the constant global frame rate. Pausing on certain images is an effective stylistic element, but it shouldn't take up excessive amounts of disk space.
In Quicktime, it is possible to give each frame of an image sequence its own duration, allowing a paused frame to appear static while the movie keeps running, at no additional expense in disk storage.
I've addressed this problem in this StackExchange post, and here I'm listing the required Python code in a more readable form:
#!/usr/bin/python from Foundation import NSNumber from AppKit import NSImage from QTKit import * class QuickTimeError(Exception): @classmethod def from_nserror(cls, nserror): return cls(nserror.userInfo()['NSLocalizedDescription']) def createQT(infiles, sequence, durations, outfile): attrs = {QTAddImageCodecType: 'avc1', QTAddImageCodecQuality: NSNumber.numberWithLong_(codecHighQuality)} mov, err = QTMovie.alloc().initToWritableFile_error_(outfile, None) if mov is None: raise QuickTimeError.from_nserror(err) n = len(durations)-1 i = 0 for index in sequence: img = NSImage.alloc().initWithContentsOfFile_(infiles[sequence[index]]) t = durations[i] if i<n: i = i+1 else: i = 0 time = QTMakeTime(t, 600) mov.addImage_forDuration_withAttributes_(img, time, attrs) mov.updateMovieFile() if __name__ == '__main__': import os,sys,csv,string buildfile = sys.argv[1] framedata = csv.reader(open(buildfile, 'rb'), delimiter=' ') imagefiles = [] durations = [] for row in framedata: if os.path.exists(row[0]): imagefiles.append(row[0]) if len(row)>1: durations.append(float(row[1])) else: durations.append(30) else: print row[0]+': File not found' if len(imagefiles)==0: print 'No images found. Movie not created' else: print 'Creating movie from frames:' print '\n'.join(imagefiles) outfileName = os.path.abspath(sys.argv[2]) createQT(imagefiles, range(len(durations)), durations, outfileName)
This source code can be saved under any file name, e.g., makeQuicktime
, and must then be made executable (chmod 700 makeQuicktime
).
Here is a Mathematica
line that generates 11 frames of an example animation:
frames = Table[ Graphics[ Text[Style["Slow Down", FontFamily -> "Futura", FontColor -> Blue, FontSize -> 48], {0, y}], Background -> Cyan, PlotRange -> {{-1, 1}, {.1, 1.5}}], {y, 1.2, .2, -.1}]; Export["frame00000001.png", frames, "VideoFrames"];
To call the script, you first have to prepare a driver file (e.g., buildFile
) that lists each image file name in a separate row. Next to each frame, you add an image duration (separated by a space), given in 1/600
of a second:
frame00000001.png 30 frame00000002.png 30 frame00000003.png 30 frame00000004.png 30 frame00000005.png 50 frame00000006.png 50 frame00000007.png 50 frame00000008.png 100 frame00000009.png 100 frame00000010.png 100 frame00000011.png 1200
If you leave out the duration, a standard value (30
) is inserted instead. If you call createQT
from another program, it is also allowed to give a durations
array that is shorter than the list of frames (sequence
). In that case, the elements of durations
are repeated cyclically.
To call the script, you specify the name of the driver file as the first argument, and give the name of the movie output file as the second argument:
makeQuicktime buildFile outputMovie.mov
Attached here is a zip file containing some test images. The command to execute in this directory is ./makeQTmovie.py buildfile newMovie.mov
.