Skip to content

Instantly share code, notes, and snippets.

@erans
Created May 20, 2011 21:16
Show Gist options
  • Save erans/983821 to your computer and use it in GitHub Desktop.
Save erans/983821 to your computer and use it in GitHub Desktop.
Get Latitude and Longitude from EXIF using PIL
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
def get_exif_data(image):
"""Returns a dictionary from the exif data of an PIL Image item. Also converts the GPS Tags"""
exif_data = {}
info = image._getexif()
if info:
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
if decoded == "GPSInfo":
gps_data = {}
for t in value:
sub_decoded = GPSTAGS.get(t, t)
gps_data[sub_decoded] = value[t]
exif_data[decoded] = gps_data
else:
exif_data[decoded] = value
return exif_data
def _get_if_exist(data, key):
if key in data:
return data[key]
return None
def _convert_to_degress(value):
"""Helper function to convert the GPS coordinates stored in the EXIF to degress in float format"""
d0 = value[0][0]
d1 = value[0][1]
d = float(d0) / float(d1)
m0 = value[1][0]
m1 = value[1][1]
m = float(m0) / float(m1)
s0 = value[2][0]
s1 = value[2][1]
s = float(s0) / float(s1)
return d + (m / 60.0) + (s / 3600.0)
def get_lat_lon(exif_data):
"""Returns the latitude and longitude, if available, from the provided exif_data (obtained through get_exif_data above)"""
lat = None
lon = None
if "GPSInfo" in exif_data:
gps_info = exif_data["GPSInfo"]
gps_latitude = _get_if_exist(gps_info, "GPSLatitude")
gps_latitude_ref = _get_if_exist(gps_info, 'GPSLatitudeRef')
gps_longitude = _get_if_exist(gps_info, 'GPSLongitude')
gps_longitude_ref = _get_if_exist(gps_info, 'GPSLongitudeRef')
if gps_latitude and gps_latitude_ref and gps_longitude and gps_longitude_ref:
lat = _convert_to_degress(gps_latitude)
if gps_latitude_ref != "N":
lat = 0 - lat
lon = _convert_to_degress(gps_longitude)
if gps_longitude_ref != "E":
lon = 0 - lon
return lat, lon
################
# Example ######
################
if __name__ == "__main__":
image = ... # load an image through PIL's Image object
exif_data = get_exif_data(image)
print get_lat_lon(exif_data)
@TomGoBravo
Copy link

Lines 17-19 need to be right indented one level right.

@erans
Copy link
Author

erans commented Oct 15, 2011 via email

@brendanberg
Copy link

The _getexif attribute may not exist if you've opened a PNG or GIF file. The following might be a better alternative to line 7:

info = getattr(image, '_getexif', lambda: None)()

@oherych
Copy link

oherych commented Mar 21, 2013

lines 23-27:
you can replace by:

return data.get(key, None)

@moshekaplan
Copy link

@DieselDraft: Or just data.get(key)

I forked this (and made some minor changes): https://gist.github.com/moshekaplan/5330395

@matze
Copy link

matze commented Aug 1, 2013

@erans: What is the licensing status of this piece of code? I am currently adding EXIF support to a static photo gallery generator and one of the requested features is of course support for geotagged photos. Now, that you (and the others) have done everything that's necessary for that, I would like to integrate that. But without any written permission it's just not possible.

@erans
Copy link
Author

erans commented Aug 8, 2013

@matze I will add licensing later today but it's basically MIT do whatever you want just add attribution :-)

@matze
Copy link

matze commented Aug 14, 2013

Thanks for the clarification. You will be of course properly attributed and credited!

@postatum
Copy link

Thanks. This helped me a lot even though I'm using exifread. If someone is curious, here is version of _convert_to_degress when using exifread:

def _convert_to_degress(value):
    deg = value[0]
    deg = float(deg.num) / float(deg.den)

    minute = value[1]
    minute = float(minute.num) / float(minute.den)

    sec = value[2]
    sec = float(sec.num) / float(sec.den)

    return deg + (minute / 60.0) + (sec / 3600.0)

@valgur
Copy link

valgur commented Nov 27, 2015

Pillow currently has a bug with GPS EXIF information (as of version 3.0.0): python-pillow/Pillow#1477.
Downgraded to 2.9.0 can be used as a temporary workaround.

@valgur
Copy link

valgur commented Nov 27, 2015

I created a fork that accepts a list of images as input and prints the GPS lat, lon, altitude, heading, speed and timestamp values in a TSV fromat: https://gist.github.com/valgur/2fbed04680864fab1bfc

@chahalamit009
Copy link

how can i use it to process the images on my django server

@maxbellec
Copy link

you can do much shorter and clearer, see my gist in 25 lines of code : https://gist.github.com/maxbellec/dbb60d136565e3c4b805931f5aad2c6d

@BigglesZX
Copy link

This was super useful, thank you.

@TheChapu
Copy link

TheChapu commented Mar 2, 2017

line 74
image = Image.open("image.jpg")

@nurlanf
Copy link

nurlanf commented Feb 21, 2019

Great piece of code

@step135
Copy link

step135 commented Jan 30, 2021

As now from PIL 7.0.0 the GPS data are stored in IFDRational, how do you get values of its numerator and denominator?

>>> f.denominator
<property object at 0x7f44e320a130>

@broox
Copy link

broox commented Aug 8, 2022

@step135 - GPS data can be found at get_ifd(0x8825) using Pillow.

This is what I do...

def get_exif(image):
    data = dict()
    exif = image.getexif()
    if exif:
        # Basic exif (camera make/model, etc)
        for key, val in exif.items():
            data[ExifTags.TAGS[key]] = val

        # Aperture, shutter, flash, lens, tz offset, etc
        ifd = exif.get_ifd(0x8769)
        for key, val in ifd.items():
            data[ExifTags.TAGS[key]] = val

        # GPS Info
        ifd = exif.get_ifd(0x8825)
        for key, val in ifd.items():
            data[ExifTags.GPSTAGS[key]] = val

    return data

@erans
Copy link
Author

erans commented Aug 8, 2022

It's good to know things are much better than they were back in 2011 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment