Rotate Images Along With Thumbnails In Python Preserving EXIF

Saturday, December 15, 2012

Rotate Images Along With Thumbnails In Python Preserving EXIF

Rotate ImagesThe Task

I need to take photos in iPhone and save it in my PC. The images should have the same orientation as when it was taken.

The Details

I have my own iPhone app which will take the image and send it as raw data through TCP sockets to the python server running on my PC. The python server receives the image and saves it as JPG in the proper location.

The First Problem

iOS always saves the image as landscape even if we take the photo in portrait mode. The actual orientation is embedded in the EXIF header of the image. Apple devices properly reads the EXIF information and will always display the images properly. But most of the image viewers in Windows doesn't care about the field. So, all images will be displayed as landscape in the PC.

The Solution

When I receive the image at the python server, I do the following:
- Check the proper orientation by reading the EXIF using pyexvi2.
- Rotate the image to match the actual orientation using wxpython. This will remove the EXIF header.
- Reset the orientation tag in EXIF so that the image will display as-is everywhere.
- Put back the EXIF information into the rotated image.

This actually makes the image display properly. But...

The Second Problem

The thumbnail inside the EXIF is not rotated. As we are doing the EXIF separately, the thumbnail is not processed by anyone.

The Complete Solution

I am taking the thumbnail out of the EXIF, rotating it and putting it back in the EXIF.

Here is the complete code...

''' Rotate Image ''' import pyexiv2 import wx import cStringIO import os def rotateImage(infile, device): try: # Read Metadata from the image metadata = pyexiv2.metadata.ImageMetadata(infile) metadata.read(); # Let's get the orientation orientation = metadata.__getitem__("Exif.Image.Orientation") orientation = int(str(orientation).split("=")[1][1:-1]) # Extract thumbnail thumb = metadata.exif_thumbnail angle = 0 # Check the orientation field in EXIF and rotate image accordingly if device == "iPhone" or device == "iPad": # Landscape Left : Do nothing if orientation == ORIENTATION_NORMAL: angle = 0 # Portrait Normal : Rotate Right elif orientation == ORIENTATION_LEFT: angle = -90 # Landscape Right : Rotate Right Twice elif orientation == ORIENTATION_DOWN: angle = 180 # Portrait Upside Down : Rotate Left elif orientation == ORIENTATION_RIGHT: angle = 90 # Resetting Exif field to normal print "Resetting exif..." orientation = 1 metadata.__setitem__("Exif.Image.Orientation", orientation) # Rotate if angle != 0: # Just rotating the image based on the angle print "Rotating image..." angle = math.radians(angle) img = wx.Image(infile, wx.BITMAP_TYPE_ANY) img_centre = wx.Point( img.GetWidth()/2, img.GetHeight()/2 ) img = img.Rotate( angle, img_centre, True ) img.SaveFile(infile, wx.BITMAP_TYPE_JPEG) # Create a stream out of the thumbnail and rotate it using wx # Save the rotated image to a temporary file print "Rotating thumbnail..." t = wx.EmptyImage(100, 100) thumbStream = cStringIO.StringIO(thumb.data) t.LoadStream(thumbStream, wx.BITMAP_TYPE_ANY) t_centre = wx.Point( t.GetWidth()/2, t.GetHeight()/2 ) t = t.Rotate( angle, t_centre, True ) t.SaveFile(infile + ".tmp", wx.BITMAP_TYPE_JPEG) thumbStream.close() # Read the rotated thumbnail and put it back in the rotated image thumb.data = open(infile + ".tmp", "rb").read(); # Remove temporary file os.remove(infile + ".tmp") # Write back metadata metadata.write(); except Exception, e: print "Error rotating image... : " + str(e)


Bonus material: Here is how to get the GPS coordinates from an image in Python...

from fractions import Fraction import pyexiv2 try: metadata = pyexiv2.metadata.ImageMetadata(image_file) metadata.read(); thumb = metadata.exif_thumbnail try: latitude = metadata.__getitem__("Exif.GPSInfo.GPSLatitude") latitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef") longitude = metadata.__getitem__("Exif.GPSInfo.GPSLongitude") longitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef") latitude = str(latitude).split("=")[1][1:-1].split(" "); latitude = map(lambda f: str(float(Fraction(f))), latitude) latitude = latitude[0] + u"\u00b0" + latitude[1] + "'" + latitude[2] + '"' + " " + str(latitudeRef).split("=")[1][1:-1] longitude = str(longitude).split("=")[1][1:-1].split(" "); longitude = map(lambda f: str(float(Fraction(f))), longitude) longitude = longitude[0] + u"\u00b0" + longitude[1] + "'" + longitude[2] + '"' + " " + str(longitudeRef).split("=")[1][1:-1] latitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLatitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef").value]); longitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLongitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef").value]); print "--- GPS ---" print "Coordinates: " + latitude + ", " + longitude print "Coordinates: " + str(latitude_value) + ", " + str(longitude_value) print "--- GPS ---" except Exception, e: print "No GPS Information!" #print e # Check for thumbnail if(thumb.data == ""): print "No thumbnail!" except Exception, e: print "Error processing image..." print e;

Did this work for you? Comments?

 See also...  » Rounded Button Border Using Gimp

» Writing Better Comments in Code

» Photo Funia

» Fixing bricked iPhone 3GS on iOS 5.1.1 without restoring

» Adding Automatically Updating Chapter Name to Header in Office Word


ATOzTOA : Latest Headlines


0 comments:

Post a Comment