In Python, how do I read the exif data for an image?

212,816

Solution 1

You can use the _getexif() protected method of a PIL Image.

import PIL.Image
img = PIL.Image.open('img.jpg')
exif_data = img._getexif()

This should give you a dictionary indexed by EXIF numeric tags. If you want the dictionary indexed by the actual EXIF tag name strings, try something like:

import PIL.ExifTags
exif = {
    PIL.ExifTags.TAGS[k]: v
    for k, v in img._getexif().items()
    if k in PIL.ExifTags.TAGS
}

Solution 2

For Python3.x and starting Pillow==6.0.0, Image objects now provide a "public"/official getexif() method that returns a <class 'PIL.Image.Exif'> instance or None if the image has no EXIF data.

From Pillow 6.0.0 release notes:

getexif() has been added, which returns an Exif instance. Values can be retrieved and set like a dictionary. When saving JPEG, PNG or WEBP, the instance can be passed as an exif argument to include any changes in the output image.

As stated, you can iterate over the key-value pairs of the Exif instance like a regular dictionary. The keys are 16-bit integers that can be mapped to their string names using the ExifTags.TAGS module.

from PIL import Image, ExifTags

img = Image.open("sample.jpg")
img_exif = img.getexif()
print(type(img_exif))
# <class 'PIL.Image.Exif'>

if img_exif is None:
    print('Sorry, image has no exif data.')
else:
    for key, val in img_exif.items():
        if key in ExifTags.TAGS:
            print(f'{ExifTags.TAGS[key]}:{val}')
            # ExifVersion:b'0230'
            # ...
            # FocalLength:(2300, 100)
            # ColorSpace:1
            # ...
            # Model:'X-T2'
            # Make:'FUJIFILM'
            # LensSpecification:(18.0, 55.0, 2.8, 4.0)
            # ...
            # DateTime:'2019:12:01 21:30:07'
            # ...

Tested with Python 3.8.8 and Pillow==8.1.0.

Solution 3

You can also use the ExifRead module:

import exifread
# Open image file for reading (binary mode)
f = open(path_name, 'rb')

# Return Exif tags
tags = exifread.process_file(f)

Solution 4

I use this:

import os,sys
from PIL import Image
from PIL.ExifTags import TAGS

for (k,v) in Image.open(sys.argv[1])._getexif().items():
        print('%s = %s' % (TAGS.get(k), v))

or to get a specific field:

def get_field (exif,field) :
  for (k,v) in exif.items():
     if TAGS.get(k) == field:
        return v

exif = image._getexif()
print get_field(exif,'ExposureTime')

Solution 5

import sys
import PIL
import PIL.Image as PILimage
from PIL import ImageDraw, ImageFont, ImageEnhance
from PIL.ExifTags import TAGS, GPSTAGS



class Worker(object):
    def __init__(self, img):
        self.img = img
        self.exif_data = self.get_exif_data()
        self.lat = self.get_lat()
        self.lon = self.get_lon()
        self.date =self.get_date_time()
        super(Worker, self).__init__()

    @staticmethod
    def get_if_exist(data, key):
        if key in data:
            return data[key]
        return None

    @staticmethod
    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_exif_data(self):
        """Returns a dictionary from the exif data of an PIL Image item. Also
        converts the GPS Tags"""
        exif_data = {}
        info = self.img._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_lat(self):
        """Returns the latitude and longitude, if available, from the 
        provided exif_data (obtained through get_exif_data above)"""
        # print(exif_data)
        if 'GPSInfo' in self.exif_data:
            gps_info = self.exif_data["GPSInfo"]
            gps_latitude = self.get_if_exist(gps_info, "GPSLatitude")
            gps_latitude_ref = self.get_if_exist(gps_info, 'GPSLatitudeRef')
            if gps_latitude and gps_latitude_ref:
                lat = self.convert_to_degress(gps_latitude)
                if gps_latitude_ref != "N":
                    lat = 0 - lat
                lat = str(f"{lat:.{5}f}")
                return lat
        else:
            return None

    def get_lon(self):
        """Returns the latitude and longitude, if available, from the 
        provided exif_data (obtained through get_exif_data above)"""
        # print(exif_data)
        if 'GPSInfo' in self.exif_data:
            gps_info = self.exif_data["GPSInfo"]
            gps_longitude = self.get_if_exist(gps_info, 'GPSLongitude')
            gps_longitude_ref = self.get_if_exist(gps_info, 'GPSLongitudeRef')
            if gps_longitude and gps_longitude_ref:
                lon = self.convert_to_degress(gps_longitude)
                if gps_longitude_ref != "E":
                    lon = 0 - lon
                lon = str(f"{lon:.{5}f}")
                return lon
        else:
            return None

    def get_date_time(self):
        if 'DateTime' in self.exif_data:
            date_and_time = self.exif_data['DateTime']
            return date_and_time 

if __name__ == '__main__':
    try:
        img = PILimage.open(sys.argv[1])
        image = Worker(img)
        lat = image.lat
        lon = image.lon
        date = image.date
        print(date, lat, lon)

    except Exception as e:
        print(e)
Share:
212,816

Related videos on Youtube

TIMEX
Author by

TIMEX

Updated on April 05, 2021

Comments

  • TIMEX
    TIMEX about 3 years

    I'm using PIL. How do I turn the EXIF data of a picture into a dictionary?

  • Santosh Kumar
    Santosh Kumar over 10 years
    Any Python 3 alternative?
  • 2rs2ts
    2rs2ts over 10 years
    Python 2.7.2: AttributeError: 'module' object has no attribute 'ExifTags'
  • Florian Brucker
    Florian Brucker over 10 years
    @2rs2ts: Try import ExifTags (without the PIL prefix).
  • Ben
    Ben over 10 years
    Better, you can reverse TAGS with name2tagnum = dict((name, num) for num, name in TAGS.iteritems()) and then do name2tagnum['ExposureTime'].
  • Mzzl
    Mzzl over 10 years
    For python3 use Pillow. It is a fork of PIL, which is still being developed, and has a python3 compatible version
  • A.J.
    A.J. about 10 years
    Can you test this on this Question, download the images, and try to get the ImageDescription. stackoverflow.com/questions/22173902/…
  • A.J.
    A.J. about 10 years
    Can you test this on this Question, download the images, and try to get the ImageDescription. stackoverflow.com/questions/22173902/…
  • bfontaine
    bfontaine over 9 years
    See this comment if you use PNG/GIF images: gist.github.com/erans/983821#comment-377080
  • Matteo
    Matteo over 8 years
    This code actually won't work, cause import PIL won't magically import anything by itself. Read stackoverflow.com/questions/11911480/… for clarification.
  • tnq177
    tnq177 over 8 years
    @Clayton for both the images, exifread returns empty dictionary. But I tested on my photos and it works just fine.
  • Deus777
    Deus777 about 7 years
  • SPRBRN
    SPRBRN about 6 years
    For Python 3, change exif.iteritems() to exif.items()
  • Mike S
    Mike S about 6 years
    Can someone explain that bit of Python-fu in the exif = { part? I don't understand the syntax. Perhaps a link to where it would be explained will be helpful. I'm particularly befuddled by PIL.ExifTags.TAGS[k]: v. Where do k and v come from? Also, how come that if statement doesn't require a colon and pass statement, at least?
  • Hey
    Hey over 5 years
    @MikeS I'm late, but this is a dictionary comprehension.
  • Param Kapur
    Param Kapur about 5 years
    This doesn't work with python 3.x and _get_exif is a protected method and shouldn't be used.
  • Åsmund
    Åsmund almost 5 years
    info['parsed_exif'] requires Pillow 6.0 or newer. info['exif'] is available in 5.4, but this is a raw bytestring.
  • ZF007
    ZF007 about 4 years
    There is no info['parsed_exif'] in version 7.0.0; only info['exif'].
  • G M
    G M about 4 years
    It doesn't work for me, I can see only the exif data using the .info method in binary
  • Momchill
    Momchill about 4 years
    I also receive an empty dictionary for a set of images. Can anyone comment of why this is the case? What kind of images does exifread.process_file() work with?
  • Gino Mempin
    Gino Mempin over 3 years
    @Momchill It depends on the image file. Some images are generated without EXIF data. If it is empty programmatically, try opening the image file in a photo editing software to check if it actually has EXIF data.
  • guhur
    guhur over 3 years
    We should not use _getexif which is a private method. Instead Pillow's method getexif is more appropriate.
  • jtlz2
    jtlz2 about 3 years
    @ParamKapur Really?
  • khaz
    khaz about 2 years
    After necessary imports and loading the image: print("\n".join([(ExifTags.TAGS[k] + f": {v}") for (k, v) in img00.getexif().items() if k in ExifTags.TAGS]))
  • Jonatas CD
    Jonatas CD almost 2 years
    I just used ExifRead for an image from a Canon 5D classic and it worked nicely - outputted way more info then ExifTags from PIL