Rotating photos from mobile devices uploaded to Google App Engine

I’m currently working on a Google App Engine application that allows users to upload photos from their mobile device and then view these photos in a slideshow.

So far, so good, until I realized that some of the uploaded images where sometimes rotated.

What the… ?

This is where I learnt about the concept of EXIF metadata. Long story short: each photo on your mobile device is stored as a jpeg file which contains the photo itself, but also a bunch of extra metadata, like the photo orientation.

This photo orientation depends on your mobile device orientation when the photo is taken. Using the iphone as an example, you basically have 4 possible positions:

  • If the iphone “home” button points downwards when the photo is taken, the photo is rotated 90° counter clockwise (“turned to the left” if you prefer)
  • If the iphone “home” button points upwards when the photo is taken, the photo is rotated 90° clockwise (“turned to the right” if you prefer)
  • If the iphone “home” button points to the left when the photo is taken,  the photo is rotated 180°.
  • If the iphone “home” button points to the right when the photo is taken,  the photo is not rotated (I guess this is how the iphone is supposed to be held when taking pictures).

What I ended up doing is:

  1. User uploads a photo
  2. GAE application determines what the photo orientation is and rotates it accordingly
  3. GAE application stores the rotated photo in the datastore

How to determine the photo orientation

As previously explained, you need to access the photo EXIF metadata and get the value of the “orientation” property.

Luckily, there is a great open-source project that can help us extract this value: it’s called metadata-extractor.

Here is how I used this library:


private int getEXIFOrientation(byte[] bytes) {
int orientation = -1; // default = unknown
 ByteArrayInputStream bis = null;
 try {
 bis = new ByteArrayInputStream(bytes);
 Metadata metadata = ImageMetadataReader.readMetadata(new BufferedInputStream(bis), false);
 ExifIFD0Directory exifDir = metadata.getDirectory(ExifIFD0Directory.class);
 if (exifDir != null) {
 orientation = exifDir.getInt(274); // 274 is the EXIF orientation standard code
 }
 } catch (Exception e) {
 log.warning("Couldn't extract EXIF orientation from image");
 } finally {
 if (bis != null) try {
 bis.close();
 } catch (IOException e) {
 // nothing
 }
 }
 return orientation;
}

How do I know that 274 is the internal code for the orientation EXIF tag ? Because of this link.

The orientation property can have 8 different values, but only 4 of them (in bold) are used:

  • 1: image is Normal-> that’s the orientation value you get when the iphone home button is on the right
  • 2: image is flipped horizontally
  • 3: image is rotated 180° -> that’s the orientation value you get when the iphone home button is on the left
  • 4: image is flipped vertically
  • 5: image is rotated 90° CCW and flipped vertically
  • 6: image is rotated 90° CCW -> that’s the orientation value you get when the iphone home button is upwards
  • 7: image is rotated 90° CW and flipped vertically
  • 8: image is rotated 90° CW -> that’s the orientation value you get when the iphone home button is downwards

How to rotate the image

That’s the easy part: we just use the GAE Image service, more precisely: ImagesServiceFactory.makeRotate().

  • orientation = 1 -> don’t rotate
  • orientation = 3 -> rotate 180° clockwise
  • orientation = 6 -> rotate 90° clockwise
  • orientation = 8 -> rotate -90° clockwise (or 90° counter clockwise if you prefer).

So we end up with:


private byte[] rotateImage(byte[] bytes) {
byte[] result = bytes;
int orientation = getEXIFOrientation(bytes);
int degrees = -1;
if (orientation == 3) degrees = 180;
else if (orientation == 6) degrees = 90;
else if (orientation == 8) degrees = -90;
if (degrees != -1) {
Image img = ImagesServiceFactory.makeImage(bytes);
Transform rotation = ImagesServiceFactory.makeRotate(degrees);
// GAE changes output format from JPEG to PNG while rotating the image if the expected output format is not given:
OutputSettings settings = new OutputSettings(ImagesService.OutputEncoding.JPEG);
settings.setQuality(1);
img = ImagesServiceFactory.getImagesService().applyTransform(rotation, img, settings);
result = img.getImageData();
}
return result;
}

Laurent KUBASKI

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: