Hacks: Articles about programming in Python, Perl, PHP, and whatever else I happen to feel like hacking at.

Image dimensions and orientation in Mac OS X Python

Jerry Stratton, October 20, 2011

Often you’ll want to get the height and width of attachments that are images. I was able to find two command-line programs on Mac OS X that will provide an image’s dimensions: /usr/bin/sips and /usr/bin/mdls. “SIPS” stands for Scriptable Image Processing System, where “mdls” is for listing metadata.

In my tests, sips was more accurate: the metadata appears to not get filled out immediately. It’s fast enough for humans, but not fast enough for scripts. The height and width metadata were empty when using mdls in the script, whereas sips was always able to provide it.

The “isImageSaved” method just checks to make sure that the attachment has been saved and it is in fact an image. The “dimensions” method returns width, height if it can find it, using “/usr/bin/sips -g pixelHeight -g pixelWidth image path”. It uses a regular expression to parse the response from sips. Pretty basic stuff.

At the bottom of the script, in the “for message in unPublishedMessages:” loop, add an attachments loop:

[toggle code]

if post.replyTo:

print 'In reply to', post.replyTo

if post.attachments:

print 'Attachments:'

for attachment in post.attachments:

print "\t", attachment.path, attachment.dimensions()

print

Not too bad. You could use this to do further things with the attachments, depending on what kind of files they are, how big they are, etc.

Image orientation

Things were going great until I sent a couple of photos from the iPad. Viewing them in a web browser, about half of the photos were sideways! They displayed “correctly” in Preview, and the Finder icons were also correct, but both Safari and Firefox displayed them sideways in the web page my script generated.

Looking at the more info:general pane in Preview’s inspector, the offending images had an “Orientation” field and the non-offending images did not. The value of the Orientation field was “6 (Rotated 90° CCW)”. Looking around, I could find other images with an Orientation field of “1 (Normal)”.

Unfortunately, sips didn’t list any orientation data; it listed the width and height as the browser displayed the images, not as Preview displayed the images.

However, there was a likely candidate in mdls: for the ones rotated 90 degrees, it listed a kMDItemOrientation or 1, and for the ones with “normal” orientation, it listed a kMDItemOrientation of 0. According to Apple’s MDItem reference, zero means landscape and one means portrait. Sometimes it will be set and the image will already be rotated correctly to it; other times it will be set and the image is not rotated correctly.

I’m guessing that I can check by comparing the height and width of the image to the orientation; if it’s portrait and the width is greater than the height, it needs to be rotated1. And sips can rotate images, so that part is easy.

Again, if the attachment hasn’t been saved or isn’t an image, it returns. Otherwise, it gets the height and width so that it can verify that the image is landscape and/or portrait.

It uses /usr/bin/mdls to get the kMDItemOrientation, but it has to wait: I’ve found that if I don’t have it sleep for a second, kMDItemOrientation will be empty. If the orientation doesn’t match the height and width, it rotates the image by 90 degrees using sips.

Then, it sleeps for another second, because about one in three times an immediate call to attachment.dimensions() after a rotation will result in the old values for height and width.

Modify the attachment loop to call the “orient” method:

[toggle code]

for attachment in post.attachments:

attachment.orient()

print "\t", attachment.path, attachment.dimensions()

I don’t call orient automatically in the attachment object2, because you’ll only want to re-orient an attached image if your purpose for this is online display or non-GUI display. If you just want to look at them in Preview, don’t re-orient them. I couldn’t find any way to modify kMDItemOrientation, which means that after sips rotates the image, the value of kMDItemOrientation is incorrect—and while web browsers will now display the image correctly, Preview will display it sideways.

It doesn’t appear that PNG images ever need rotating; so rather than incur the one-second delay, I only check JPEG files for mismatched orientation. It makes sense that JPEG images will need virtual rotation where PNG images don’t: PNG is a non-lossy format, but every time you save a JPEG the image’s quality deteriorates. Rotating a JPEG image means decoding the JPEG, rotating the resulting pixels, and then re-encoding the JPEG; at the re-encoding stage, image quality will drop. Rotate an image enough and you will see the effects.

My guess is that Mac OS X/iOS uses the orientation metadata field to avoid this deterioration. Thus, there’s no reason for this complexity for PNGs: it just goes ahead and actually rotates the image. If I’m wrong, and you run across non-JPEG images that need rotation, just remove the “if self.fileFormat in ['jpeg', 'jpg']:” and de-indent that section.

No, PNGs also need rotating on occasion. Not sure what I was seeing there. I’ve removed the limitation to only rotate jpeg images.

I don’t remember how I ran across Guitar World’s Lick of the Day app. I might have been looking around for MusicNotes.com’s app, which I ran across while looking for sheet music of America, the Beautiful. There was surprisingly little good, simple versions I could use for guitar, for free. MusicNotes.com put the full first page of their version online, and it worked, so after a few weeks of internal whining about the lack of a good free version,…

This script takes a height and a width in whatever units you wish, an existing set of pixel dimensions for an image file, and calculates which pixel dimension should be cropped to match the poster size.

Blogroll

Keep in touch

About Mimsy

Comments?

The undiscovered comment form, whose bourn no poster returns.

Your comment

Your name

Your email

Your web page

Your location

Your email, URL, and location are optional—but I won’t be able to contact you if you don’t leave a working email. Your email does not get displayed, your URL and location do. Your name is required but may vary as the needs of the day demand, or you can just use the anonymous Hark Thrice name. You can use the following tags: <em>, <a>, <blockquote>. Use them wisely and post intelligently. Comments may take some time to approve, especially if I’m stuck in a Mexican jail.