Home

Why photo rotation is broken on the iPhone 4 and how to fix it (using Paperclip on Heroku)

Apple made an annoying change to the way the iPhone camera works in iOS4. If you’ve ever been emailed a photo from an iPhone 4, you’ve experienced this rage guy situation:

Photo looks fine in Gmail's preview..

But when you click 'View' it's rotated incorrectly

What’s going on?

Exchangeable image file format (EXIF)

EXIF is a standard method for storing metadata in image files. What’s metadata? Data about the photo (as opposed to the data that makes up the photo itself) — example pieces of metadata stored in EXIF are:

  • When the photo was taken
  • Where the photo was taken (if your camera has GPS)
  • The camera model
  • (Relevant to this iPhone issue) how programs viewing the photo should rotate it for display

Before EXIF, if you wanted to store the date a photo was taken with the photo, you’d have to print it on the photo itself (ugly!):

EXIF allows you to store this kind of data without affecting the way a photo looks (kind of like writing a date on the back of a print)

What’s causing the problem?

Here’s what happens:

  1. The iPhone camera sensor's "up" is the right edge of the phone (when held with the home button at the bottom).

    When the iPhone takes a photo, it writes the photo data with the default sensor orientation (i.e., as if the photo had been taken with the phone rotated such that the home button is on the right.)

  2. The iPhone saves the real orientation as EXIF data with the photo
  3. Software that's EXIF-aware (the iPhone's "Photos" app, Gmail's photo thumbnailing logic) rotates the photo correctly for display, whereas software that's not EXIF-aware (Firefox) doesn't

Why doesn’t the iPhone write the photo data with the correct orientation? In fact, it seems like there shouldn’t even be the option to specify rotation in EXIF! I.e., if there were only one way of specifying a photo’s orientation (the orientation of the photo data itself), consumers wouldn’t have to worry about whether their photo viewing software knew about EXIF.

The way to fix this (and this is what the previous iPhone OS did) is to rotate the actual photo data when it comes off the sensor; unfortunately this operation is computationally expensive, and so I assume Apple stopped doing it so the iPhone would save photos faster (perhaps this is one reason the iOS4 camera app is so much more responsive)

How do I fix this in my application?

Here’s how I fixed this problem for InstaGal, which uses Paperclip for attachments and is hosted on Heroku.

The basic idea is to use the EXIF orientation information to rotate the actual photo before saving. Fortunately, the ImageMagick library (which Paperclip uses) has a convenience “auto-orient” method that saves you the trouble of manually reading the EXIF data and making the appropriate rotation.

How do we get Paperclip to use ImageMagick’s auto-orientation? Google around and you’ll see recommendations to add :convert_options => { :all => '-auto-orient' } to your Paperclip configuration like so:

has_attached_file :image, :storage => :s3,
                  :styles => { :medium => "600x600>", :small => "320x320>", :thumb => "100x100#" },
                  :convert_options => { :all => '-auto-orient' },
                  :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
                  :path => "/:style/:id_:filename"

This supposedly passes the “auto-orient” option to ImageMagick before it creates your thumbnails, but it didn’t work for me on Heroku, and messing with ImageMagick command line options is a bit hacky.

Instead, we’ll use RMagick (the Ruby version of ImageMagick) in a separate image processor.

First, create the file config/initializers/auto_orient.rb:

module Paperclip
  class AutoOrient < Paperclip::Processor 
    require 'RMagick' # Make sure to update your gem file

    def initialize(file, options = {}, *args)
      @file = file
    end

    def make( *args )
      img = Magick::Image.read("#{File.expand_path(@file.path)}")[0]
      img.auto_orient!

      temp = Tempfile.new(@file.path.split('/').last)
      img.write(temp.path)
      return temp
    end
  end
end

Now, update your Paperclip settings:

has_attached_file :image, :storage => :s3,
                  :styles => { :original => '5000x5000>', :medium => "600x600>", :small => "320x320>", :thumb => "100x100#" },
                  :processors => [:auto_orient, :thumbnail],
                  :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
                  :path => "/:style/:id_filename"

Note:

  1. The auto_orient processor runs before the thumbnail processor
  2. I’ve created a style called “original” so that my originals get auto-oriented as well (this is good practice anyway to prevent people from attaching arbitrarily large images)

And that’s it! Paperclip will automatically correct the orientation of your attached photos before resizing and saving them.

Posted November 16th, 2010