Tuesday, January 08, 2008

Python + PIL: Make Geotagged photos from EXIF Headers

For my django app, i wanted to take geotagged images and display them on google maps. Geotagged images store GPS data in the EXIF headers of the image, so we'll want to grab this data and extract the lattitude and longitude so we can display where the image was taken in our mapping software.

Using the PIL library we can grab the GPS data out of the image and format it in a way to easily display on a google map. PIL really does all the work for us, all we have to do is convert the Lat/Lng from DDD°, MM', SS.S" format stored in image to DD.DDDDD° format that google uses

Hidden in the Exif data we see this:

34853: {0: (2, 0, 0, 0), 1: 'N', 2: ((40, 1), (37, 1), (279, 10)), 3: 'W', 4: ((74, 1), (8, 1), (373, 10)), 5: 0, 6: (0, 1), 7: ((18, 1), (58, 1), (19, 1)), 9: 'A', 18: 'WGS-84'}


We can then use the following python to extract the data, convert and save it to our photo object
from PIL import Image
from PIL.ExifTags import TAGS

i = Image.open('/pics/DSC07018.JPG')
info = i._getexif()

ret = {}
for tag,value in info.items():
decoded = TAGS.get(tag, tag)
ret[decoded] = value

Nsec = ret['GPSInfo'][2][2][0] / float(ret['GPSInfo'][2][2][1])
Nmin = ret['GPSInfo'][2][1][0] / float(ret['GPSInfo'][2][1][1])
Ndeg = ret['GPSInfo'][2][0][0] / float(ret['GPSInfo'][2][0][1])
Wsec = ret['GPSInfo'][4][2][0] / float(ret['GPSInfo'][4][2][1])
Wmin = ret['GPSInfo'][4][1][0] / float(ret['GPSInfo'][4][1][1])
Wdeg = ret['GPSInfo'][4][0][0] / float(ret['GPSInfo'][4][0][1])

if ret['GPSInfo'][1] == 'N':
Nmult = 1
else:
Nmult = -1

if ret['GPSInfo'][1] == 'E':
Wmult = 1
else:
Wmult = -1

Lat = Nmult * (Ndeg + (Nmin + Nsec/60.0)/60.0)
Lng = Wmult * (Wdeg + (Wmin + Wsec/60.0)/60.0)

Now that you have the Lat and Lng you can save to your model or class to easily display in your web app.

photo= GMapPhoto()
photo.image = i
photo.lattitude = Lat
photo.longitude = Lng
photo.save()