Panic in the laboratory! On a Friday…
Just shortly after I entered my office this morning, my colleagues from the R&D lab approached me with a request to write an AppleScript for them. They needed to create image sequences from 3000 still images generated by a device measuring stress values in electroplated deposits and they definitely did not want to do that manually. Especially not on a Friday…
QuickTime Pro allows you to create image sequences…
Well, I knew that you could easily create custom slide shows with QuickTime Pro, but why spent hard-earned $29.99 when you can get the same functionality free of charge by using built-in Mac OS X 10.5 technologies? [Okay, just ignore my hourly rate in this calculation :D]
And here is how: Mac OS X 10.5 already contains PyObjC, which allows to work directly with the excellent QTKit using the Python scripting language. This way I was able to quickly write a Python script that creates an empty QuickTime movie and then adds images to it. Combined with a convenient AppleScript droplet, that calls the Python script located in its own Application bundle, this made for a nice and efficient workflow At least, my colleagues were more than satisfied…
As always, I invite you to download and inspect the AppleScript on your fine Mac:
Sequimago ¢ Create image sequences without QuickTime Pro (ca. 65.6 KB)
Requirements
¢ Mac OS X 10.5 Leopard
¢ QuickTime 7.2.1
Installation & Usage
Download and extract the ZIP archive. Then open the script with a double click or drop a bunch of image files onto its icon. The script will then ask you to specify a file name and location for the new QuickTime movie. After you provided certain settings for your image sequence (e.g. frames per second/seconds per frame), your image sequence will be produced.
Supported Image Formats
jpg, jpeg, gif, png, tiff, tif, psd, pict, bmp, pdf
only the first page of a PDF document is recognized
Notes
Sequimago currently uses the JPEG image format for the image sequence, which results in a smaller file size of the QuickTime movie. But you can easily edit the Python script to use alternative image formats (e.g. TIFF). The Python script is located at: «Sequimago.app/Contents/Resources/crtimgseq.py».
Moreover the script does not yet offer advanced export settings. But you can study this excellent article to modify the Python script yourself.
Important: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! That is because the AppleScript internally relies on a little Python script, which is located inside its Application bundle. Therefor please download the complete script here.
-- author: Martin Michel
-- created: 16.05.2008
-- requires:
-- ¢ Mac OS X 10.5 Leopard
-- ¢ QuickTime 7.2.1
-- This AppleScript will create an image sequence (QuickTime movie) from
-- selected or dropped image files.
--
-- The script will ask you to specify a file name and location for the new
-- QuickTime movie and lets you select specific settings for the image sequence
-- (frames per second/seconds per frame). Moreover you can choose to sort the
-- image files by their file names before creating the QuickTime movie.
--
-- The image sequence itself is created by a Python script using QTKit.
--
-- YOU DO NOT NEED QuickTime Pro to use this script :D
--
-- Supported image formats: jpg, jpeg, gif, png, tiff, tif, psd, pict, bmp, pdf
-- the AppleScript's name
property mytitle : "Sequimago"
-- I am called when the user opens the AppleScript with a double-click
on run
set finderitems to choose file with prompt "Please choose image files for the image sequence:" with multiple selections allowed without showing package contents and invisibles
my main(finderitems)
end run
-- I am called when the user drops Finder items onto the AppleScript's icon
on open finderitems
my main(finderitems)
end open
-- |--|--|--|--|--|--|--|--|--|--|--|--|--|
-- | Main Routine
-- |--|--|--|--|--|--|--|--|--|--|--|--|--|
-- I am the main function controlling the script flow
-- I demand a list of Finder items to process
on main(finderitems)
try
-- searching the given Finder items for image files that can be processes
set imgpaths to my getimgpaths(finderitems)
-- no image files found...
if imgpaths is {} then
set errmsg to "The dropped/selected items did not contain any image files to process." & return & return & "Supported image formats: jpg, jpeg, gif, png, tiff, tif, psd, pict, bmp, pdf"
my dsperrmsg(errmsg, "--")
return
end if
-- asking the user to provide a file path for the new QuickTime movie
set newmoviepath to my getnewmoviepath()
if newmoviepath is missing value then
return
end if
-- asking the user to select certain settings for the image sequence
set imgseqsettings to my getimgseqsettings()
if imgseqsettings is missing value then
return
end if
-- does the user want to sort the image files by their file names?
set sortimgfiles to my getsortimgfiles()
-- creating the image sequence
my crtimgsequence(newmoviepath, imgpaths, sortimgfiles, imgseqsettings)
-- catching unexpected errors
on error errmsg number errnum
my dsperrmsg(errmsg, errnum)
end try
end main
-- I am searching the given list of Finder items for image files
-- that can be used to create an image sequence
-- >>> I return a list of image file paths, that might be empty, if not image files are found
-- Note: I am currently searching for image files by simply looking at the file's name
-- extension. That is not the best approach...File types in the future?
on getimgpaths(finderitems)
set imgpaths to {}
repeat with finderitem in finderitems
set iteminfo to info for finderitem
if not folder of iteminfo then
set suffix to name extension of iteminfo
if suffix is not missing value then
if suffix is in {"jpg", "jpeg", "gif", "png", "tiff", "tif", "psd", "pict", "bmp", "pdf"} then
set imgpaths to imgpaths & (finderitem as Unicode text)
end if
end if
end if
end repeat
return imgpaths
end getimgpaths
-- I am asking the user to specify a file name and location for a new QuickTime movie
-- >>> I return the chosen file path as Unicode text [Mac path, not Posix path] or
-- >>> missing value in case the user cancels the dialog
on getnewmoviepath()
try
set newmoviepath to choose file name default name "imagesequence.mov" with prompt "Please specify a file name and location for the new QuickTime movie:"
return (newmoviepath as Unicode text)
on error
return missing value
end try
end getnewmoviepath
-- I am asking the user to select specific settings for the image sequence
-- (frames per second vs. seconds per frames)
-- >>> I return a special list containing a time code used by QuickTime
-- >>> {time value, time scale} or missing value in case the user cancels the dialog
on getimgseqsettings()
try
tell me
activate
display dialog "Please select a timing mode for the image sequence:" with title mytitle buttons {"Cancel", "Frames per second", "Seconds per frame"} default button 1
end tell
set dlgresult to result
if button returned of dlgresult is "Frames per second" then
set fpsvalue to my gettimingvalue("fps")
if fpsvalue is not missing value then
return {1, fpsvalue}
else
return missing value
end if
else if button returned of dlgresult is "Seconds per frame" then
set spfvalue to my gettimingvalue("spf")
if spfvalue is not missing value then
return {spfvalue, 1}
else
return missing value
end if
end if
on error
return missing value
end try
end getimgseqsettings
-- I am asking the user to enter the number of frames per second or the duration of
-- each frame in seconds based on the given mode ("fps" or "spf")
-- >>> I return a real or missing value in case the user cancels the dialog
on gettimingvalue(mode)
-- getting the system's decimal separator (comma or dot)
set decseparator to my getdecseparator()
if mode is "fps" then
set dlgmsg to "Please enter the number of desired frames per second:" & return & "(decimal separator: »" & decseparator & "«)"
else
set dlgmsg to "Please enter the duration of each frame in seconds:" & return & "(decimal separator: »" & decseparator & "«)"
end if
set defansw to "1" & decseparator & "0"
try
tell me
activate
display dialog dlgmsg default answer defansw buttons {"Cancel", "Enter"} default button 2 with title mytitle
end tell
set dlgresult to result
set usrinput to text returned of dlgresult
-- no input, empty string
if usrinput is "" then
my gettimingvalue(mode)
else
-- can we convert the user input into a real?
try
set fpsvalue to usrinput as real
if fpsvalue is greater than 0 then
return fpsvalue
end if
on error errmsg number errnum
-- no, we cannot...try again
my dsperrmsg(errmsg, errnum)
my getfpsvalue()
end try
end if
on error
return missing value
end try
end gettimingvalue
-- I am asking the user if he wants to sort the image files by their file names
-- before creating the image sequence
-- >>> I return true or false
on getsortimgfiles()
tell me
activate
display dialog "Do you want to sort the image files by their file names before processing them?" buttons {"Yes", "No"} default button 2 with icon note with title mytitle
end tell
set dlgresult to result
if button returned of dlgresult is "No" then
return false
else if button returned of dlgresult is "Yes" then
return true
end if
end getsortimgfiles
-- I am creating an image sequence using the given parameters and a Python script
on crtimgsequence(newmoviepath, imgpaths, sortimgfiles, imgseqsettings)
-- file path of the Python script responsible for creating the image sequence
-- using the QTKit and the newly introduced Scripting Bridge
set mypath to ((path to me) as Unicode text)
set pyscriptpath to POSIX path of (mypath & "Contents:Resources:crtimgseq.py")
-- we need to use a dot as the decimal separator when passing these two real numbers
-- to the Python script on the command line, therefore we search and replace any commas
set timevalue to my searchnreplace(",", ".", (item 1 of imgseqsettings) as Unicode text)
set timescale to my searchnreplace(",", ".", (item 2 of imgseqsettings) as Unicode text)
set newmoviepath to (POSIX path of newmoviepath)
-- creating a large string containing all image file paths, so that we can easily
-- pass them on to the Python script
set strpaths to ""
repeat with imgpath in imgpaths
set strpaths to strpaths & space & quoted form of (POSIX path of imgpath)
end repeat
-- creating the complete command
set command to "python " & quoted form of pyscriptpath & space & quoted form of newmoviepath & space & timevalue & space & timescale & space & sortimgfiles & strpaths
log command
set command to command as «class utf8»
-- there we go...BUMM
do shell script command
end crtimgsequence
-- I am returning the currently used decimal separator
on getdecseparator()
set strnum to 0.1 as Unicode text
set decseparator to character 2 of strnum
return decseparator
end getdecseparator
-- I am returning the text items of the given text (delimited by the given delimiter)
on gettxtitems(txt, delchar)
considering case, diacriticals and punctuation
set olddelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to {delchar}
set txtitems to text items of txt
set AppleScript's text item delimiters to olddelims
end considering
return txtitems
end gettxtitems
-- I am a very old search & replace function
on searchnreplace(searchstr, replacestr, txt)
considering case, diacriticals and punctuation
if txt contains searchstr then
set olddelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to {searchstr}
set txtitems to text items of txt
set AppleScript's text item delimiters to {replacestr}
set txt to txtitems as Unicode text
set AppleScript's text item delimiters to olddelims
end if
end considering
return txt
end searchnreplace
-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
tell me
activate
display dialog "Sorry, an error occured:" & return & return & (errmsg & " (" & errnum & ")") buttons {"OK"} default button 1 with icon stop with title mytitle giving up after 60
end tell
end dsperrmsg