ImageMagick, Applescript, and Automator

This week, I wanted to use the GoPro camera to grab timelapse shots of traffic on a neighborhood street. I also wanted to share the timelapse video online with neighbors. In this post, I’ll describe how I brewed up some AppleScript and bash to get this done.

I knew how to set up a GoPro to grab shots every 30 seconds, but my circumstances required the camera to be set up askew. Pics were rotated 90 degrees (counterclockwise) from their real orientation. Also, the field of view was larger than necessary. While I knew how to create a timelapse movie with QuickTime Pro 7 (paid upgrade), using QuickTime required some preprocessing of the images (rotating and cropping) to make the video I’d envisioned. And after making a timelapse video by hand, I realized it would be nice to have a timestamp on each frame so viewers would know when the shots were taken.

Here’s what one of the original pics looks liks.


An example frame from the timelapse.


I’m an Apple nerd because OS X has Darwin underpinnings. My pre-OS X computer days were linux-based. So I’m not afraid of the command line, and I knew cropping and rotating could be done using the command line tool ImageMagick, which I had installed using macports.

I knew I could rotate my images by 90 degrees clockwise with the command

convert -rotate "90" input.JPG output.JPG

and I knew that I could crop it the way I wanted with the command

convert -crop -400-1320 input.JPG output.JPG.

The two commands can be combined into one

convert -rotate "90" -crop -400-1320 input.JPG output.JPG

with the transformations happening left to right. (This is important because rotations and crops are not commutative operations, nerds.)

After testing ImageMagick on the images, I knew that I could transform the images in the way I wanted, image-by-image. But each timelapse video would consist of about 200 images. I wanted to do this in batch. It seemed like I could do so with a shell script that looked like this:

for file in *.JPG
    do
        convert -rotate "90" -crop -400-1320 "$file" "crop-$file"
    done

ImageMagick also allows for putting a watermark on an image with a command that looks sometime like this:

convert -fill white  -undercolor '#00000080'  -gravity South -annotate +0+5  "TEXT" input.JPG output.JPG

where “TEXT” is the timestamp. I knew from Saghoian and Cheeseman’s AppleScript 1–2–3 that image metadata can easily be accessed using AppleScript like so:

set this_file to choose file without invisibles
try
    tell application "Image Events"
        launch
        set this_image to open this_file
        tell this_image
            set the stamp to the value of metadata tag "creation"
        end tell
        close this_image
    end tell
    display dialog stamp
on error error_message
    display dialog error_message
end try

So I wondered if my whole solution could be brewed in AppleScript. I knew that you could call command line functions from within AppleScript like so

do shell script "COMMAND"

but I didn’t know if I could put all this together. The only way to find out is to try.

After much trial and error, I came up with the script that’s shown below. Below the script, I’ll comment on a couple things that were difficult for me.

set this_file to choose file without invisibles
set file_path to quoted form of the POSIX path of this_file
set filename to do shell script "basename " & file_path as string
set dirname to do shell script "dirname " & file_path as string
tell application "Image Events"
  launch
 set this_image to open this_file
   tell this_image
        set the stamp to the value of metadata tag "creation"
  end tell
   close this_image
end tell
set A to -400
set B to -1320
try
 do shell script "/opt/local/bin/convert -rotate '0' -crop +" & A & "+" & B & " " & file_path & " " & quoted form of (dirname & "/tmp-" & filename)
 do shell script "/opt/local/bin/convert " & dirname & "/tmp-" & filename & " -fill white  -pointsize 150 -undercolor '#00000080'  -gravity South -annotate +0+5 " & quoted form of stamp & " " & dirname & "/new-" & filename
  do shell script "rm " & dirname & "/tmp-" & filename
on error error_message
    display dialog error_message
end try

This is the timelapse movie that the script allowed me to create.



A couple things that I learned when writing this script:

  • Because I’m using command line tools, I need to use POSIX paths for my files.
  • AppleScript uses /bin/sh to run command line tools; I use /bin/bash to run command line tools. Sometimes this difference is important (e.g., the shells might have different PATH variables set).
  • Passing variables (e.g., filenames) from AppleScript to the command line tools was pretty easy once I got the hang of concatenation and quoting.
  • Just because your script works when run in ScriptEditor does not mean it will run in Automator. My path has folder names with spaces and special characters. (What can I say?) Working with those as quoted strings in ScriptEditor worked great, but Automator doesn’t like that one bit. This means that I need to be careful where I use this script as a workflow or folder action.

To allow this to run as part of an Automator workflow, I replaced the first line with

on run {input, parameters}
 set this_file to item 1 of input
    ...
 end run

Next task: tweak the script to be able to batch process a set of files. But this should be easy. In the meantime, I’ll create a folder action to get that done.

UPDATE: The tweak for batch processing files is easy in the context of Automator. When an Automator workflow runs, it passes a list called inputs to an AppleScript. When my list of inputs is longer than one (i.e., when I’m working on more than one file at a time), I need to iterate through that list. I can do so by adding this line

repeat with this_file in input

to the top of my script, and close that loop with

end repeat

at the bottom of the script. Easy peasy.