Create image sequences from still images without QuickTime Pro

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 :wink: 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

Thansk a ton! I incorporated some of your script in to a Photoshop script I’ve been playing around with.

This really helped me a ton!

Sorry to bother, but for me, I can’t use it. I use snow leopard and when I use it everything seems to go fine until I open it and it is less then a billionth of a second. No error pops up, nothing. :mad: Help! :frowning:

Hi,

I am also on Snow Leopard and the script works on my MacBook Pro (Intel). Maybe you need to open it in Script Editor, compile it and then save it again?

Best regards,

Martin

Hi, your script is great but it seems to stop before the end of my image sequence.

My image sequence is 2000 frames long but the QT file is only 565 frames after it compiles it.

I tried it with both sorting enabled and disabled and had the same result.

I tried it on a different image sequence which were smaller frames and it worked great and did 3000 frames.

I tried another large sized tiff sequence and I get the same issue, it stops at around frame 565.

I then tried the same sequence but this time I used compressed TIff files instead of uncompressed, thinking it could be a file size issue but it still stops at 565.

The files are 1920x1080 Tiff files, and the one that worked were 720x480 Tiff files.

After more investigation, it must be a memory issue as I tried some files that were 1280x720 and it stopped at 1298 out of 3000.

I have uploaded a 25mb zip file with 2700 tiff files if you would like to test it at your end.

http://dl.dropbox.com/u/4031669/TiffSeq.zip

Thanks.

Phil

Hi Phil,

I had a hard time fixing this bug, but finally found a solution.

First I rewrote the underlying tool in Obj-C, because I thought that PyObjC might have some memory release problems. But also the Obj-C tool always had a significant performance hit when it reached 600 processed images. I tried many things, even splitting up the process in creating different movies, but nothing helped.

Then I stumbled across this thread and immediately the problem was gone. Just adding an autorelease pool to the loop adding the images fixed the problem. Now the whole process seems to run a lot faster, with much less memory usage and also all 2698 images are processed.

I created a beta version of the updated Sequimago AppleScript, which you can download here. Please try it and let me know what you think:

Sequimago 1.1b

It should work on Mac OS X 10.4 or higher, but I could only test it on Mac OS X 10.6.3.

Best regards and thanks a ton for the sample images!

Martin

Hi Martin,

erst einmal vielen Dank für das tolle Skript! Hat meine 2800 Fotos gerade super gewandelt und mir viel geld gespart! ;o)

Ein was ist mir noch aufgefallen, je länger das Skrit läuft, desto langsamer “wandelt” es die Bilder. Liegt das an QuickTime und der wachsenden *.mov Größe, oder gibt es noch ein Memory-Leak?

Vielen Dank für das Skript! :o)
LG André

Hallo André,

gut zu wissen, daß Menschen das Skript tatsächlich gut gebrauchen können :smiley:

Hast Du die Version 1.1b verwendet, die man hier findet?

Dort sollte der Fehler eigentlich behoben sein…

Viele Grüße aus dem regenreichen Berlin

Martin

Love the script. I wonder if you could add an option for some simple transitions.

Hi,

Just a simple question : I’ve tried to modify the script to load all pictures in a selected folder :

on run
	set Afolder to choose folder with prompt "Please select directory."
	tell application "Finder"
		set finderitems to every item in Afolder
	end tell
	my main(finderitems)
end run

But it doesn’t work. Any idea why ?

Hi Martin, all

what an excellent program, many thanks! I’m trying to use it from a shell script (I had a look at how you’re calling the imgsequence binary) but I’m not really sure what’s happening here. I can make a movie without problems as long as I specify both -ts and -tv and both are 1… however, 1fps is not really what I want. When I only specify fps (so -ts 1 for 1 fps), the resulting video is corrupt. Same goes for if I only specify -tv. Specifying anything other than 1 for -tv or -ts results in a corrupted video file as well. Do you have any recommendations on how to call this to create a 10fps movie?

Many thanks

  • Balt

Edit: This is how I call imgsequence for a 1fps movie which works:

imgsequence -mov imagesequence.mov -sort 0 -ts 1 -tv 1 -imgs ‘east1369017271.jpg|east1369017391.jpg’

Hi Balt,

I just downloaded Sequimago 1.1b and used the imgsequence binary from the package contents like follows to successfully create a 10fps movie:

/Users/martin/Downloads/Sequimago\ 1.1b.1/Sequimago.app/Contents/Resources/imgsequence -mov /Users/martin/Desktop/test.mov -sort 0 -ts 10.0 -tv 1.0 -imgs “/Users/martin/Desktop/Neuer Ordner/0.JPG|/Users/martin/Desktop/Neuer Ordner/1.JPG|/Users/martin/Desktop/Neuer Ordner/2.JPG|/Users/martin/Desktop/Neuer Ordner/3.JPG|/Users/martin/Desktop/Neuer Ordner/4.JPG|/Users/martin/Desktop/Neuer Ordner/5.JPG|/Users/martin/Desktop/Neuer Ordner/6.JPG|/Users/martin/Desktop/Neuer Ordner/7.JPG|/Users/martin/Desktop/Neuer Ordner/8.JPG|/Users/martin/Desktop/Neuer Ordner/9.JPG|/Users/martin/Desktop/Neuer Ordner/10.JPG|/Users/martin/Desktop/Neuer Ordner/11.JPG|/Users/martin/Desktop/Neuer Ordner/12.JPG|/Users/martin/Desktop/Neuer Ordner/13.JPG|/Users/martin/Desktop/Neuer Ordner/14.JPG|/Users/martin/Desktop/Neuer Ordner/15.JPG|/Users/martin/Desktop/Neuer Ordner/16.JPG|/Users/martin/Desktop/Neuer Ordner/17.JPG”

Do your images all have the same size?

I used the imgsequence binary on a MacBook Pro 2.4 GHz Intel Core 2 Duo running Mac OS X 10.8.3

Best regards from Berlin,

Martin

Hi Martin,

thanks for picking up on this.

All my images are the same indeed, just a series of frames from a webcam. I have to use the arguments the other way around than you show. To make a 10fps movie: -ts 1.0 -tv 10.0

Cheers from Sydney

  • Balt

Hi Martin,

This is an awesome application! Thanks so much!!

Tim