image rotation through metadata

I’ve got a fancy camera that writes what’s called EXIF (EXchangeable Image Format) metadata into the image about, among many other things, the proper orientation of the image. That’s pretty nice since it means that all the images have the same dimensions (width by height) when written to disk, but unless your rendering software respects the metadata and spins the pixels accordingly when writing them out to a monitor, looking at your images can leave you with a real crick in your neck. Jump to the bottom of this post for solution code, but read on for entertainment.


Apparently this is an open problem at time of writing in browserworld. The metadata is served along with the rest of the image data, but that metadata must be explicitly referenced for img tags by making use of a new experimental css property: image-orientation: from-image. Support for that tag is unsurprisingly spotty: Firefox respects it, but Chrome doesn’t appear to. (I don’t know or care about other browsers — does that make me a bad person?) I’d assume there are open questions too about specifying that style along with width or height attributes. However, when rendering an image directly by url (i.e. not referenced from html but simply as an address) Chrome does spin the pixels the right way.

All of this made my photoblogging life harder. At first I was prepared to use this excellent photo editor called RIOT (Radical Image Optimization Tool) — a labor of love from a Romanian programmer that I already use to downscale hi-res images for general consumption — to manually rotate the images. But that was going to get very obnoxious very quickly for any large project. Obviously there had to be a way to code this.

For better or worse my gut instinct is to first attempt a PowerShell solution for any bulk file manipulation task since I’m a Windows kid, and it turns out that System.Drawing.Image does expose the EXIF data, but not in any discoverable way. Image objects have (among other stuff) two properties of interest: PropertyIdList, and PropertyIdItems. In typical MSFT overengineering, that first list is basically useless since it’s just magic numbers. But the second list is hardly better, since it’s just those magic numbers along with their magic values and other esoteric magic number values like ‘type’.

Astoundingly the documentation for this list doesn’t link to a demystification of its meaning, but after a little googling and I found a link from a stackoverflow question to the MSDN page for a wholly different library that explains the magic. It turns out the orientation data I want is locked up inside PropertyId 0x112, which is 274 in decimal. Of course, the explanation of the meaning of that property’s value was also nearly incomprehensible, so I just checked against some pictures I had which I knew to be of varying orientations — by rendering them in browsers that don’t auto-spin, of course, since Windows natively does it for you — and figured it out. Another odd fact: the value is a two-int array where the second value is always 0 and the first value is the one you want. PowerShell, amirite?

The transformation was pretty simple but equally bizarre: that Image class has a fun method called RotateFlip that takes a RotateFlipType enum argument describing the thing to do. You simply have to read the breakdown to believe it — I guess the problem being solved here is preventing non-right-angle rotations, though the CSS spec to respect this property handles that issue by rounding down — but luckily in PowerShell you can pass in a string literal that’s just the name of the enum. One last detail that was significant in my implementation was that the rotations are clockwise, and since I assumed they would be anticlockwise like they are in mathland, I got an auto-inversion for free — that is, my anticlockwise read of the orientation was the same value as the clockwise rotation I needed to set everything upright.

The Image class also has an in-built Save function to write my edits back to disk. Another catch: all the JPEG squeezing RIOT gave me was undone by whatever ungodly decompression Image does to generate a bitmap in memory, so I had to pass the final product back through RIOT to get a decently web-optimized image. But at last, my portraits are portraits, and even my upside-down landscapes are landscapes! Phew. Here’s the code:

# map: esoteric msft const -> degree of anticlockwise rotation
$rotCodes = @{
    1=0
    6=90
    3=180
    8=270
}

[Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$imgpath = "c:\my photos\djtrump_hifive.jpg" # are you jelly?
$img = [System.Drawing.Image]::FromFile($imgpath)
# 274 == 0x112 == PropertyTagOrientation
$prop = $img.PropertyItems | ? { $_.Id -eq 274 }
$ori = $prop.Value[0]
$rot = $rotCodes[[int]$ori] # the cast was necessary
# only bother if there's a nontrivial rotation
if($rot -ne 0)
{
    $rotEnum = "Rotate$($rot)FlipNone" # this is real life
    $img.RotateFlip($rotEnum)
    $img.Save($imgpath) # is this really a jpg?
}

Leave a Reply

Your email address will not be published. Required fields are marked *