I've been keeping Chip's blog for a few years now and have been finding it increasingly enjoyable. But while it does reflect my diverse interests, the lack of any coherent theme must make it difficult to follow for anyone interested in a specific topic.
So lately, as I've been posting more about photography, I have been thinking about creating a separate, specialized blog as the new home for all my photo-related posts. I was still thinking about it when, a couple of weeks ago, Trey Ratcliff (StuckInCustoms.com) challenged his readers to consider trying out SmugMug. Well, I'd been thinking about that too.
So I decided to do both at the same time.
The result is my new blog and photo/video sharing site Time - Light - Distance. As much as possible I am trying to integrate SmugMug with Blogger to offer a better photo and video sharing experience, as well as providing a soapbox where I can discuss my work.
This isn't the end of Chip's Blog, but all my new photography and video posts will be going to TLD. Over time I also plan to migrate the older posts as well. For anyone who finds the things I'm doing in these areas helpful or interesting, I hope TLD will be easier to follow. For myself, I hope that the sharper focus will make a difference in the quality and quantity of my work.
Thursday, February 25, 2010
Introducing Time-Light-Distance: My New Photo Blog

This work by Chip Chapin is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Permissions beyond the scope of this license may be available at http://cchapin.blogspot.com/p/permissions-and-attributions.html.
Labels:
Photography
Saturday, February 13, 2010
Enhancing An Image Sequence With PhotoShop Filters
I've been working on an article about incorporating PhotoShop filters into my time-lapse workflow, and decided to post this tutorial separately. Note that this requires the Extended versions of PhotoShop.
As I investigated that further, I learned two new things about PhotoShop (Extended):
You need the Extended version of PhotoShop for either of these.
The rest of this article is a step-by-step guide to filtering each frame of an image sequence. I'll discuss the workflow considerations in a separate article.
1. Start PhotoShop and from the menu choose File::Open. Navigate to the folder containing your image sequence.
2. Select the first file in your sequence and select the "Image Sequence" button. Then click "Open".
3. Choose a frame rate -- I happen to be using 60 for this project -- and click "OK". PhotoShop takes about 10 seconds to open my 1,139 frame sample project.
4. I like to see the Animation Timeline, though it really isn't needed for what we're doing here. If you don't see it, click on the workspace dropdown at the top right and switch to the "Video" workspace.
5. In order to apply filters to all frames in the sequence, you need to first convert it to a "Smart Object". You can use the main menu Layer::Smart Objects::Convert to Smart Object, or just right click in the Layer panel and select "Convert to Smart Object".
6. Now apply your filter to the layer. In this example I'm using Topaz Adjust 3 from Topaz Labs.
7. Each filter will be different, so I won't go through the Topaz dialog here. By the way, Trey Ratcliff has a great review of Topaz Adjust on his blog, StuckInCustoms, and provides a discount code for purchase.
8. When you finish the filter dialog, PhotoShop works for a while and shows you the result on the selected frame. Notice the new "Smart Filters" section appearing in your layers panel.
9. Now we're going to save our sequence as a new set of images with the filtering applied. In the main menu choose "File::Export::Render Video". No, we are not actually going to render video, and yes, it is quite confusing.
10. You have many choices here. I'll explain each of the ones that we're going to use right now. The numbers in the screen shot are keyed to each item in the explanation.
11. Click "OK" on the JPEG Options dialog and then click the "Render" button. After 10-20 seconds you should see an "Exporting Video" progress bar. Now find something else to do, because it is s-l-o-w. My machine, with these settings, will render at 12-13 frames per minute. For this example, the sequence consists of 1,139 frames and I expect it to take about 90 minutes.
You can also watch its progress by looking in the output directory and watching the files appear. I find this more soothing than the progress bar.
When PhotoShop is finished, you have a new image sequence that has been filtered. This result is the same as applying a batch operation to all the files, but in my experience it is much faster.
I'll be discussing this technique in the workflow article, coming soon.
Two Things I Didn't Know About PhotoShop
I need efficient methods to enhance the images in my time-lapse projects. For some projects I want to use PhotoShop and the question is how best to apply it to a large number of images. PhotoShop provides automation, batch tools, and a scripting interface, so there are lots of ways to do this. But my first efforts suggested that it might be too slow.As I investigated that further, I learned two new things about PhotoShop (Extended):
- PhotoShop supports image sequences. I should be able to load a sequence into PS and apply powerful photographic filters such as Topaz to each frame automatically without additional scripting.
- PhotoShop supports video. You can indeed set up filters and apply them to the entire video.
You need the Extended version of PhotoShop for either of these.
The rest of this article is a step-by-step guide to filtering each frame of an image sequence. I'll discuss the workflow considerations in a separate article.
Filtering an Image Sequence, Step-By-Step
I'm using Photoshop CS4 Extended on Windows Vista. The procedure is essentially the same on Mac.1. Start PhotoShop and from the menu choose File::Open. Navigate to the folder containing your image sequence.
2. Select the first file in your sequence and select the "Image Sequence" button. Then click "Open".
3. Choose a frame rate -- I happen to be using 60 for this project -- and click "OK". PhotoShop takes about 10 seconds to open my 1,139 frame sample project.
4. I like to see the Animation Timeline, though it really isn't needed for what we're doing here. If you don't see it, click on the workspace dropdown at the top right and switch to the "Video" workspace.
5. In order to apply filters to all frames in the sequence, you need to first convert it to a "Smart Object". You can use the main menu Layer::Smart Objects::Convert to Smart Object, or just right click in the Layer panel and select "Convert to Smart Object".
6. Now apply your filter to the layer. In this example I'm using Topaz Adjust 3 from Topaz Labs.
7. Each filter will be different, so I won't go through the Topaz dialog here. By the way, Trey Ratcliff has a great review of Topaz Adjust on his blog, StuckInCustoms, and provides a discount code for purchase.
8. When you finish the filter dialog, PhotoShop works for a while and shows you the result on the selected frame. Notice the new "Smart Filters" section appearing in your layers panel.
9. Now we're going to save our sequence as a new set of images with the filtering applied. In the main menu choose "File::Export::Render Video". No, we are not actually going to render video, and yes, it is quite confusing.
10. You have many choices here. I'll explain each of the ones that we're going to use right now. The numbers in the screen shot are keyed to each item in the explanation.
- Item 1: Location Name. This is where you will set the "prefix name" of your image sequence filenames. My screen shot is a bad example! The prefix name of "Untitled-1" results in an image sequence with names like Untitled-10001.jpg, Untitled-10002.jpg and so forth.
- Item 2: Select Folder and Create New Subfolder. You have choices; I leave "Select Folder" set to my "project" folder and click "Create New Subfolder" so that all my images will go into a new subfolder. This is faster than using Select Folder to create a new folder.
- Item 3: File Options. Select "Image Sequence", not "QuickTime Export". You are welcome to experiment for yourself, but I've had nothing but problems attempting to perform the filter rendering and video rendering in the same step.
- Item 4: Details about the exported files. Keep the original image size ("Size: Document Size"), export "All frames" rather than a subset, and render them at same frame rate you started with ("Document Frame Rate").
- Item 5: JPEG Settings. Click the "Settings" button to popup the JPEG Options dialog. Set the quality level however suits you -- if you check the "preview" box it will estimate the file size for you.
11. Click "OK" on the JPEG Options dialog and then click the "Render" button. After 10-20 seconds you should see an "Exporting Video" progress bar. Now find something else to do, because it is s-l-o-w. My machine, with these settings, will render at 12-13 frames per minute. For this example, the sequence consists of 1,139 frames and I expect it to take about 90 minutes.
You can also watch its progress by looking in the output directory and watching the files appear. I find this more soothing than the progress bar.
When PhotoShop is finished, you have a new image sequence that has been filtered. This result is the same as applying a batch operation to all the files, but in my experience it is much faster.
I'll be discussing this technique in the workflow article, coming soon.

This work by Chip Chapin is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Permissions beyond the scope of this license may be available at http://cchapin.blogspot.com/p/permissions-and-attributions.html.
Labels:
Photography,
PhotoShop,
TimeLapse,
Topaz Adjust
Saturday, February 06, 2010
Setting Up a Project at "Google Code"
Last weekend I set up my seq-to-qt project at Google Code. Here's a short tutorial.
In the past few years Google has been promoting their own infrastructure vision for open source software projects. It's called "Project Hosting on Google Code".
For a long time now we've had SourceForge and it is still the leading home for open source software projects. SourceForge is a great resource -- why Google Code?
"Google Code" itself was born as a way to host Google's own APIs and open source projects -- Google's channel to the developer community. It made sense to implement a flexible infrastructure that others could use as well, hence "Project Hosting". Offering such a service is consistent with the company's other hosted services, like Sites and Blogger -- you can read their mission statement if you like.
To change anything except the project name visit the "Administer" tab. That's also where you can set up your project to use tools like Google Analytics and Google Groups.
Compressed archives are produced by many different programs. Some of the most well-known are WinZip (Windows), gzip (Unix/Linux) and Stuffit (Mac). Your operating system may even have the capability built-in, like Windows Vista (select files, right-click, "Send to:Compressed (zipped) Folder").
Let's step through this.
People who are interested in your project need to communicate, not just with you, but with each other. The "Google Groups" facility provides more features than a conventional mail list and can be linked automatically to your project page.
In the past few years Google has been promoting their own infrastructure vision for open source software projects. It's called "Project Hosting on Google Code".
"Google Code" itself was born as a way to host Google's own APIs and open source projects -- Google's channel to the developer community. It made sense to implement a flexible infrastructure that others could use as well, hence "Project Hosting". Offering such a service is consistent with the company's other hosted services, like Sites and Blogger -- you can read their mission statement if you like.
Getting Started
Here's how to set up a new project using Project Hosting.- Open Google's "Getting Started" page. Keep it open in one browser tab or window so you can refer to it while performing the various actions in another window.
- Visit "Create Project" and fill out the form shown at right. The most important thing is the project name -- you can change anything else later, including your choice of license and version control system.
- Click the "Create project" button at the bottom.
You've now created your project at Google Code and you'll see your project's home page for the first time. The URL in this case is http://code.google.com/p/my-cool-project/
To change anything except the project name visit the "Administer" tab. That's also where you can set up your project to use tools like Google Analytics and Google Groups.
Posting Your Code
One of the first things you'll want to do is to actually post your source code and perhaps compiled binaries. Even if you're using a version control system (and you should), open source projects typically also post compressed archives -- bundles of files that a user can easily download. A common example would be a zip file.Compressed archives are produced by many different programs. Some of the most well-known are WinZip (Windows), gzip (Unix/Linux) and Stuffit (Mac). Your operating system may even have the capability built-in, like Windows Vista (select files, right-click, "Send to:Compressed (zipped) Folder").
Let's step through this.
- Create your archive. The name should be descriptive and include a date or version number, like "my-cool-project_2010-02-06.zip"
- Click on your project "Downloads" tab. A page opens like the one below:
- Click on "New download" to open the download dialog:
- Enter a short summary of what's in your file, some labels that you think will help others find it, and use the "Choose File" button to pick the file on your computer. Finally click "Submit file".
Next Steps
Open Source software development is not just about posting your code for all to use. It's about enabling communities of developer/users to share their improvements. Doing this successfully at any kind of scale requires a revision control system. So your next step is probably to set up your source code repository using either Mercurial or Subversion.People who are interested in your project need to communicate, not just with you, but with each other. The "Google Groups" facility provides more features than a conventional mail list and can be linked automatically to your project page.
References
- Google Code: Home base for Google APIs and developer relations.
- Project Hosting on Google Code: A few links, but mostly just a splash screen.
- Project Hosting on Google Code: "Getting Started". The good stuff.

This work by Chip Chapin is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Permissions beyond the scope of this license may be available at http://cchapin.blogspot.com/p/permissions-and-attributions.html.
Labels:
Software Development
Monday, February 01, 2010
seq_to_qt: Python Conversion of Still Image Sequences to QuickTime Movies on Windows
Update: I've created a project for seq_to_qt at code.google.com. This makes it easier for me to manage updates to the code.
In several recent posts (1, 2, 3) I've discussed my progress toward automating the workflow of creating time-lapse movies from still image sequences on Windows. I used JScript+WSH for the original scripts because that's how the samples I started with were written. But I've now rewritten the system in Python and extended it to provide "seamless" automation from end to end of the QuickTime part of my process.
Download and install compatible versions of Python and PyWin32. I have tested seq_to_qt with Python 2.6.
Download my Python files seq_to_qt (links to download page at the project site) and put them in a convenient place.
My working folders are organized like this:
In several recent posts (1, 2, 3) I've discussed my progress toward automating the workflow of creating time-lapse movies from still image sequences on Windows. I used JScript+WSH for the original scripts because that's how the samples I started with were written. But I've now rewritten the system in Python and extended it to provide "seamless" automation from end to end of the QuickTime part of my process.
What It Does
seq_to_qt provides a set of functions that I am finding useful for automating my workflow in producing timelapse movies. My camera software saves images in separate folders for each day and numbers the images sequentially. That's where seq_to_qt enters the picture. I can give it a simple list of folders that need to processed and it will take care of everything else involved in generating a QuickTime movie for each sequence.Key Benefits
- Saves me time, errors, and drudgery. Fire it off once to handle multiple image sequences and leave for the day (or weekend).
- Splits long image sequences into chunks, if necessary, so that QuickTime doesn't choke.
- The sequence splitter itself is very general and could be used to divide any sequence of numbered files.
- Flexible Python implementation is relatively easy to extend and modify.
Getting Started
Download and install Apple QuickTime. You will need to pay for the "Pro" license.Download and install compatible versions of Python and PyWin32. I have tested seq_to_qt with Python 2.6.
Download my Python files seq_to_qt (links to download page at the project site) and put them in a convenient place.
Running The Scripts
Python is flexible and there are a lot of things you can do. But here is a simple way to get started.My working folders are organized like this:
Project
scripts (this is where I put seq_to_qt and any others)
seq1 (each set of image sequence files is in its own folder)
XXX_0001.jpg (numbered sequentially with a common prefix)
XXX_0002.jpg
...
seq2
seq3
...
You don't have to set yours up the same way, but I thought it might help to see how I'm using it.- Create a "test" sequence with no more than about 200 frames. To do this, create a new folder in your Project folder and there copy the first 1-200 frames of your favorite sequence.
- Make a copy of seq_batch_convert.py and edit it to provide the file paths for your image sequences. If you want to see how the chunk splitting works, set the chunk_size to something small, like 50.
- Now run your script! From a command window, your command will look something like this:
c:\Python26\python scripts\seq_batch_convert.py - And if all goes well a QuickTime movie will be produced.
Future Work
I want to extend the automation tools to include image processing prior to movie production. Also, I may want to automate more of the production work that takes place in my video editor after the QuickTime portion is done.References
- Automating Conversion of Still Image Sequences to QuickTime Movies, Chip Chapin January 2010. Demonstrates JScript calling the QuickTime COM (Component Object Model) interface under the Windows Scripting Host (WSH) environment.
- Splitting Still Image Sequences for use with QuickTime Movies, Chip Chapin January 2010. Describes JScript for dividing the image sequence files into chunks that QuickTime can handle more easily.
- Automating QuickTime at the Command Line on Windows… XSIBlog article by Luc-Eric Rousseau, 2006. This excellent post got me started in this direction.
- QuickTime for .NET and COM Developers by John Cromie (Elsevier 2006). The sample scripts in Chapter 8 were used by Rousseau in developing his script.
- "Scripting the COM/ActiveX Control in QuickTime 7 for Windows", Apple Developer Connection (September 2005). This is the only official Apple documentation on this scripting interface that I have been able to find so far.
- "Programmatic Export using QTControl", John Cromie comments in Apple quicktime-api mail list (October 2006).
- QuickTime Overview, Mac OS X Reference Library. Apple's starting point for QuickTime developers.
- Apple Technical Note 2120: QuickTime for Windows ActiveX/COM Frequently Asked Questions (May 2006), Rousseau mentions this FAQ in his code. It is an excellent source of examples in C# and VBScript using the host-based script interface.
- "Using OLEView to Explore the QuickTime COM Interface", Chip Chapin January 2010.
- Microsoft Windows OLEView. The link is for the Windows 2000 Resource Kit version. You can also get it as part of the Windows Server 2003 Resource Kit.
- "Exploiting Perl on Windows with OLE/COM" (PowerPoint) presentation by Roth Consulting provides an excellent overview of Component Object Model (COM) and how it can be used in scripting.
- Official Python.org downloads.
- Official SourceForge PyWin32 downloads.
- Python Programming Language -- the official website (python.org).
- Mark Hammond's Python for Windows Extensions (PythonWin/pywin32)
- "Win32 How Do I?" by Tim Golden
- SourceForge Python for Windows Extensions (newer PythonWin/pywin32).
- Chapter 12 of Python Programming on Win32 by Mark Hammond and Andy Robinson (O'Reilly 2000).
- Python and COM by David and Paul Boddie.
- Pygments syntax highlighter.
Source Code
You can download a zip file of the seq_to_qt package. Or just copy and paste it from here.- seq_to_qt.py is the module that contains all the functionality, and is used by the other files. The other files are intended to be used as commands.
- seq_batch_convert.py -- A simple command file used to enter your data and initiate the complete conversion. Copy this file and edit it with your own image sequence information, then run it.
- qt_movie_from_stills.py -- If you wish, you can use this for the conversion of a single sequence, without splitting. I would normally use seq_batch_convert.py instead.
- make_seq_chunks.py -- Does only the chunk splitting on a sequence, no conversion, and therefore no dependence on QuickTime.
Files have been syntax-highlighted using Pygments.
seq_to_qt.py
#! /usr/bin/env python """ Convert one or more still image sequences into QuickTime movies. Author: Chip ChapinFor More Info: http://cchapin.blogspot.com You must have the PyWin32 COM interface installed, see http://sourceforge.net/projects/pywin32/ Acknowledgements: The QuickTime interface was originally written as a JScript+WSH script, which I based on a 2006 blog post by Luc-Eric Rousseau (XSIBlog http://www.xsi-blog.com/archives/103). Rousseau's script was in turn based on sample code by John Cromie, author of the book "QuickTime for .NET and COM Developers" (Elsevier 2006, http://www.skylark.ie/qt4.net/samplecode.asp). Latest Update: 2010-01-31 TODO: Some sort of progress indicator during the initial rendering stage. TODO: Better solution to the QT_PLAYER_DELAY hack. """ import datetime import math import os import re import shutil import sys import tempfile import time import win32com.client CODEC_INFO_FILENAME = "C:\\qtMovieFromStillsCodecInfo.xml" DEFAULT_CHUNK_FILES=7000 DEFAULT_FRAME_RATE = "60" # HACK: Delay (sec) while the QuickTime player initializes. QT_PLAYER_DELAY = 7.0 def abort(message=None): """Print error message and exit.""" if message: print "ERROR: ", message sys.exit(1) def usage(message=None): """Print usage message and exit.""" if message: print message sys.exit(2) def check_file_folder(fpath): """Returns true if the directory of fpath exists and is writable.""" fdir = os.path.dirname(fpath) try: f = tempfile.TemporaryFile("w", dir=fdir) except OSError: return False f.close() return True def file_exists(fpath): """Returns true if file fpath exists and is readable.""" try: f = open(fpath, "r") except IOError: return False f.close() return True def uniqueify(fpath): """Check to see if the file exists. If so, uniqueify the filename.""" upath = fpath count = 0 while file_exists(upath): count = count + 1 (root, ext) = os.path.splitext(fpath) upath = "%s-%02d%s" % (root, count, ext) if count: print "WARN: You already have a file '%s'" % fpath print " Saving as '%s' instead." % upath return upath def create_new_movie_from_images(sourcePath, frameRate, qtControl): """Create the movie from the still image sequence.""" print "Creating new movie from still sequence '" + sourcePath + "'..." try: qtControl.CreateNewMovieFromImages(sourcePath, frameRate, True) # rate is in frames per second except: print "ERROR creating movie " #+ e.number + # " (" + (e.number>>16 & 0x1FFF) + # "-" + (e.number & 0xffff) + ")"); #WScript.Echo(e.description); # TODO: Find more reliable error reporting. # The following doesn't work if the QTControl object is gone. print "QuickTime error " + qtControl.ErrorCode qte = qtControl.QuickTime.Error print " " + qte.ErrorCode + ", " + qte.Description print " " + qte.SourceReference raise qtMovie = qtControl.Movie; if not qtMovie: abort("No movie created (" + qtControl.ErrorCode + ")") duration = qtMovie.Duration; if (duration == 0): # This test isn't as helpful as I thought it would be. I thought it # would catch the case where QT does not have a valid input file, but in # that case it seems to create a two-second empty movie # (ie. duration = 20*framerate). abort("Movie has duration 0.") # Duration is the number of frames * 10. print "Created new movie, duration %d" % duration def get_quicktime_exporter(qtControl): """Set up the QuickTime movie exporter.""" qt = qtControl.QuickTime if (qt.Exporters.Count == 0): # Only add an exporter if needed. qt.Exporters.Add() print "Adding new Exporter." else: print "Using existing Exporter." qtExporter = qt.Exporters(1) if not qtExporter: abort("Unable to get Exporter.") qtExporter.TypeName = "QuickTime Movie" qtExporter.SetDataSource(qtControl.Movie) if file_exists(CODEC_INFO_FILENAME): print "Reading codec config from '" + CODEC_INFO_FILENAME + "'" CodecFileInfo = open(CODEC_INFO_FILENAME, "r") xmlCodecInfoText = CodecFileInfo.read() # Cause the exporter to be reconfigured. # http://developer.apple.com/technotes/tn2006/tn2120.html tempSettings = qtExporter.Settings tempSettings.XML = xmlCodecInfoText qtExporter.Settings = tempSettings else: # Use the Settings dialog box, then save the results. qtExporter.ShowSettingsDialog() xmlCodecInfoText = qtExporter.Settings.XML try: CodecFileInfo = open(CODEC_INFO_FILENAME, "w") CodecFileInfo.write(xmlCodecInfoText) CodecFileInfo.close() except IOError: print ("Warning: failed to save codec info to '" + CODEC_INFO_FILENAME + "'") print "continuing ..." return qtExporter def export_movie(qtExporter, destPath): """Export the movie.""" print "Exporting ..." try: qtExporter.DestinationFileName = destPath qtExporter.ShowProgressDialog = True # Uncomment this line if you want the export dialog box to appear. # qtExporter.ShowExportDialog(); qtExporter.BeginExport() # This can take a l-o-n-g time. print "Exported to '%s'" % destPath except: print "ERROR exporting '%s'" % destPath #print "ERROR " + (e.number>>16 & 0x1FFF) + # "-" + (e.number & 0xffff) + # " exporting '" + destPath + "'" #print e.description #WScript.Echo(JSON.stringify(e, null, 2)); #qte = qt.Error; #print "QuickTime Error %d, %s" % (qte.ErrorCode, qte.Description) #WScript.Echo(JSON.stringify(qte, null, 2)); raise def qt_movie_from_stills(sourcePath, destPath, frameRate): """Create and export a QuickTime movie from a sequence of images.""" # Launch QuickTime Player Application qtPlayerApp = win32com.client.Dispatch( "QuickTimePlayerLib.QuickTimePlayerApp") time.sleep(QT_PLAYER_DELAY); # Give it time to launch. if not qtPlayerApp: abort("Failed to launch QuickTime Player App.") # Get the QuickTime player and its associated controller. # NOTE: The script will abort here if the player hasn't had time # to initialize. It should work if you run it again, or you can increase # QT_PLAYER_DELAY. qtPlayer = qtPlayerApp.Players(1); if not qtPlayer: abort("Failed to get QuickTime Player."); print "Got player '" + qtPlayer.Caption + "'" qtControl = qtPlayer.QTControl create_new_movie_from_images(sourcePath, frameRate, qtControl) qtExporter = get_quicktime_exporter(qtControl) export_movie(qtExporter, destPath) # Closing the player causes failures for subsequent invocations. # qtPlayer.Close(); def seq_file_list(a_seq_file): """Return the list of file names in the sequence.""" # Extract the leading invariant part of the sequence filename. # For example, if the name is Fooo-0001.jpg, the invariant is "Fooo-". # Use non-greedy match so the numbers are kept out. (work_folder, sequence_proto) = os.path.split(a_seq_file) sm = re.match("(.*?)[0-9]+\.", sequence_proto) if not sm: usage(("'%s' doesn't look like the start of a sequence.\n" % sequence_proto) + "Sequences consist of sequentially numbered file names like\n" + " Grog-001.jpg, Grog-002.jpg, Grog-...\n" + "\n" + " sequenceExample -- Path to any file in the sequence.\n" + " maxSize -- Maximum number of sequence files to put in a folder."); # Construct a regexp for matching sequence file names. sq_name = sm.group(1) sq_re = re.compile("^" + sq_name + "[0-9]+\."); # Make a list of files that match the sq_re pattern. "fnmatch" patterns # are not powerful enough to be reliable so we don't use glob. sq_files = [] dir = os.listdir(work_folder) dir.sort() for f in dir: if sq_re.match(f): sq_files.append(f) return (sq_files, sq_name) def split_file_list(a_seq_file, sq_files, sq_name, max_size): """Split the sequence files into successive split folders. Returns the list of split folder names. """ work_folder = os.path.dirname(a_seq_file) sq_count = len(sq_files) num_splits = math.ceil(sq_count / max_size) print ("Splitting file sequence '%s' into %d parts." % (sq_name, num_splits)) parent_folder = os.path.dirname(work_folder) file_count = 0 next_file_in_split = 1 current_split = 0 splits = [] for fname in sq_files: if (file_count == 0 or next_file_in_split > max_size): current_split = current_split + 1 next_file_in_split = 1 split_folder = os.path.join(parent_folder, ("%s-%d" % (work_folder, current_split))) print "New split folder '%s' (%d)" % (split_folder, file_count) try: os.mkdir(split_folder, 0755) except EnvironmentError as (errno, strerror): print "ERROR: Failed to create split directory '%s'" % split_folder print "[Error %d] %s" % (errno, strerror) abort() splits.append((split_folder, fname)) # Move the file from work_folder to split_folder shutil.move(os.path.join(work_folder, fname), os.path.join(split_folder, fname)) file_count = file_count + 1 next_file_in_split = next_file_in_split + 1 print ("Split %d files in '%s' into %d folders." %(file_count, work_folder, current_split)) return splits def make_seq_chunks(a_seq_file, max_size): """Split a file sequence into folders of no more than max_size files each. Returns a list of (folder, starting_file) duples. Sequences have file names that end with a sequence number, like (Foo-001.jpg, Foo-002.jpg, ...). """ (sq_files, sq_name) = seq_file_list(a_seq_file) sq_count = len(sq_files) print "Sequence '%s' contains %d members." % (a_seq_file, sq_count) if sq_count <= max_size: print "No need to split." if (sq_count == 0): return [] else: return [os.path.split(a_seq_file)] else: return split_file_list(a_seq_file, sq_files, sq_name, max_size) def seq_name_from_filename(fname): """Extract the 'sequence name' from a representative filename.""" sm = re.match("(.*?)[-_]?[0-9]+\.", os.path.basename(fname)) if not sm: print ("'%s' doesn't look like the start of a sequence." % fname) return None return sm.group(1) def convert_sequences(base_dir, seqs, frame_rate=DEFAULT_FRAME_RATE, chunk_size=DEFAULT_CHUNK_FILES): """Generates QuickTime movies for a set of image sequences. base_dir: Full path to the common parent directory. seqs: List of duples describing image sequences. Each duple contains the FOLDER NAME of the sequence and the STARTING IMAGE file name: (folder, file) Names are unqualified, i.e. relative to their parent directories. frameRate: Numeric frame rate in frames per second. chunk_size: Maximum number of frames to be processed in a single movie. """ for (sq_dir, sq_file) in seqs: src = "%s\\%s\\%s" % (base_dir, sq_dir, sq_file) if not file_exists(src): print "WARN: '%s' is not readable. Skipping." % src continue # Possibly split the image sequence into chunks. splits = make_seq_chunks(src, chunk_size) # Iterate over the chunks. for (split_folder, split_file) in splits: sq_name = seq_name_from_filename(sq_file) if (sq_name): dst = ("%s\\%s_%s.mov" % (base_dir, sq_name, os.path.basename(split_folder))) dst = uniqueify(dst) print time.strftime("%Y-%m-%d %X Converting:") print (" Folder %s\n Start %s\n Dest %s" % (split_folder, split_file, dst)) qt_movie_from_stills(src, dst, frame_rate) print time.strftime("Complete at %Y-%m-%d %X") def main(argv): """Main Program""" usage("This is the seq_to_qt module.\n" + "For batch conversion, edit 'seq_batch_convert.py'. It calls the functions here.") if __name__ == "__main__": main(sys.argv)
seq_batch_convert.py
#! /usr/bin/env python """ Convert a batch of image file sequences to QuickTime movies. Edit this file and list your sequences below. Author: Chip ChapinFor More Info: http://cchapin.blogspot.com """ import seq_to_qt DEFAULT_BASE_PATH = "L:\\Graphics\\Images\\SX110\\MTV-TL01" DEFAULT_SEQUENCE_FILE = "MTV-TL01_0001.JPG" DEFAULT_CHUNK_SIZE = 7000 def usage(): print "Usage: seq_batch_convert.py " exit(2) def main(argv): """Main Program""" # Step 1: Base directory. This is the parent of your # sequence directories. base_path = DEFAULT_BASE_PATH # Step 2: List your image sequences. # Each sequence is a duple containing the # FOLDER NAME and the STARTING IMAGE of the sequence # Example: #sequences = [("Test01-1", "MTV-TL01_0001.JPG"), # ("Test01-2", "MTV-TL01_0076.JPG"), # ("Test01-3", "MTV-TL01_0151.JPG")] sequences = [("2009-12-15b", "MTV-TL01_7001.JPG"), ("2009-12-19b", "MTV-TL01_7001.JPG"), ("2009-12-22b", "MTV-TL01_7001.JPG"), ("2009-12-23b", "MTV-TL01_7001.JPG"), ("2009-12-24b", "MTV-TL01_7001.JPG")] # Step 3: How many frames per second should your movie run at? frame_rate = 60 # Step 4: Movie chunk size-- how large a movie (how many frames) # can QuickTime process on your computer? Try the default. # If QuickTime aborts during the export phase, make it smaller. #chunk_size = 75 # Low value for testing. Normally use DEFAULT_CHUNK_SIZE chunk_size = DEFAULT_CHUNK_SIZE # Now do the conversion. seq_to_qt.convert_sequences(base_path, sequences, frame_rate, chunk_size) if __name__ == "__main__": import sys main(sys.argv)
qt_movie_from_stills.py
#! /usr/bin/env python """ Create a QuickTime movie from a sequence of still images on Windows. Usage: python qt_movie_from_stills.py sourcepath destpath [framerate] Use FULLY-QUALIFIED Windows file paths. Relative paths don't work well inside QuickTime -- it is likely to fail with no error report from QT. You must have the PyWin32 COM interface installed, see http://sourceforge.net/projects/pywin32/ Author: Chip ChapinFor More Info: http://cchapin.blogspot.com Acknowledgements: I originally wrote this as a JScript+WSH script based on a post by Luc-Eric Rousseau (XSIBlog 2006, http://www.xsi-blog.com/archives/103). His was in turn based on sample code by John Cromie that accompanied his book "QuickTime for .NET and COM Developers" (Elsevier 2006, http://www.skylark.ie/qt4.net/samplecode.asp). Last Update: 2010-01-31 TODO: Some sort of progress indicator during the initial rendering stage. """ import seq_to_qt import os def usage(message=None): """Print usage message and exit.""" if message: print message print "Usage: qt_movie_from_stills.py sourcepath destpath [framerate]" print "Hint: Avoid relative paths, use fully-qualified Windows paths" print "(e.g. C:\\a\\b\\c.jpg)." sys.exit(2) def main(argv): """Main Program""" if (len(argv) < 3): usage() sourcePath = argv[1] destPath = argv[2] if (len(argv) >= 4): frameRate = argv[3] else: frameRate = seq_to_qt.DEFAULT_FRAME_RATE print "FrameRate: " + frameRate if not seq_to_qt.file_exists(sourcePath): usage("Missing source file '" + sourcePath +"'") if seq_to_qt.file_exists(destPath): # .mov files take a long time to create. Avoid # overwriting one by mistake. usage("Will not overwrite destination file '" + destPath + "'") if not seq_to_qt.check_file_folder(destPath): usage("Unusable destination file path '" + destPath +"'") seq_to_qt.qt_movie_from_stills(sourcePath, destPath, frameRate) if __name__ == "__main__": import sys main(sys.argv)
make_seq_chunks.py
#! /usr/bin/env python """ Author: Chip ChapinLast Updated: 2010-01-31 Split a sequence of files into smaller chunks. I use this for dividing still image sequences into pieces that QuickTime can handle. See http://cchapin.blogspot.com/ """ import seq_to_qt import os def usage(message=None): """Print usage message and exit.""" if message: print message print "Usage: make_seq_chunks.py sequenceFile maxSize" print ( "\nSequences have sequentially numbered file names like\n" + " Foo-001.jpg, Foo-002.jpg...\n" + "sequenceFile -- Path to any file in the sequence.\n" + "maxSize -- Maximum number of sequence files to put in a folder."); sys.exit(2) def main(argv): """Main Program""" if (len(argv) != 3): usage("Expected two arguments") a_seq_file = os.path.abspath(argv[1]) max_size = float(argv[2]) if not os.path.isfile(a_seq_file): usage("'%s' is not a file." % a_seq_file) seq_to_qt.make_seq_chunks(a_seq_file, max_size) if __name__ == "__main__": import sys main(sys.argv)

This work by Chip Chapin is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Permissions beyond the scope of this license may be available at http://cchapin.blogspot.com/p/permissions-and-attributions.html.
Labels:
Python,
QuickTime,
Software Development,
TimeLapse,
Video
Subscribe to:
Posts (Atom)














