I can get the EXIF data from an image using PIL, but how can I get the date and time that the photo was taken?
I create a function (get_exif
) that can get attributes easier.
your_date_time: str = get_exif(Path('test.jpg'), 'DateTimeOriginal')
# your_date_time = get_exif(Path('test.jpg'), 0x9003) # same as above
from pathlib import Path
from typing import Union, List, Tuple
import PIL.Image
import PIL.ExifTags
def get_exif(file_path: Path,
search_list: Union[int, str, List, Tuple] = None,
ignore_error=True
) -> Union[int, PIL.Image.Exif, List]:
"""
:param file_path: image path
:param search_list: if you want to get some property, then you can pass the id or name, it will return by order.
:param ignore_error:
:return:
int: -1 FileNotFoundError, or exif is None
PIL.Image.Exif: when the `search_list` is None, return the whole Exif
"""
tag_by_id: dict = PIL.ExifTags.TAGS
try:
im: PIL.Image.Image = PIL.Image.open(str(file_path))
except FileNotFoundError:
if ignore_error:
return -1
else:
raise FileNotFoundError(file_path)
exif: PIL.Image.Exif = im.getexif()
if not exif:
if ignore_error:
return -1
else:
raise ValueError("exif is None")
if search_list is None:
return exif
tag_by_name = {tag_by_id[dec_value]: exif[dec_value] for dec_value in exif if dec_value in tag_by_id}
result_list = []
if not isinstance(search_list, (list, tuple)):
search_list = [search_list]
for key in search_list:
if isinstance(key, int):
result_list.append(exif.get(key, None))
continue
try:
dec_value = int(key, 16)
result_list.append(exif.get(dec_value, None))
continue
except ValueError:
...
result_list.append(tag_by_name.get(key, None))
return result_list if len(result_list) > 1 else result_list[0]
import unittest
class ExifTests(unittest.TestCase):
def test(self):
exif: PIL.Image.Exif = get_exif(Path('test.jpg')) # same as the ``PIL.Image.open().getexif()``
# get specify attribute only
date_t_ori1 = get_exif(Path('test.jpg'), 0x9003)
date_t_ori2 = get_exif(Path('test.jpg'), '0x9003', ) # hex string is ok too.
date_t_ori3 = get_exif(Path('test.jpg'), 'DateTimeOriginal') # Give name is also working.
self.assertTrue(date_t_ori1 == date_t_ori2 == date_t_ori3)
# You can get multiple values at once. If the key does not exist, it returns None.
date_t_ori4, img_length, _, __ = get_exif(Path('test.jpg'),
(36867, 'ImageLength', 'NoteExitName', -12345))
# Occurring error return -1 (by default ignore_error=True)
self.assertEqual(-1, get_exif(Path('not exist.jpg'))) # FileNotFoundError
self.assertEqual(-1, get_exif(Path('no_exif_tag.jpg'))) # ValueError
self.assertRaises(FileNotFoundError, get_exif, Path('not exist.jpg'), ignore_error=False)
self.assertRaises(ValueError, get_exif, Path('no_exif_tag.jpg'), ignore_error=False)
This website is good, but if you want to get the full list, you should reference PIL.ExifTags.py
, see below,
# ExifTags.py
# Maps EXIF tags to tag names.
TAGS = {
# possibly incomplete
0x000B: "ProcessingSoftware",
0x00FE: "NewSubfileType",
0x00FF: "SubfileType",
0x0100: "ImageWidth",
0x0101: "ImageLength",
0x0102: "BitsPerSample",
...
0xA430: "CameraOwnerName", # <-- The website does not record it. (The website record last tag is A420.)
0xA431: "BodySerialNumber",
0xA432: "LensSpecification",
0xA433: "LensMake",
0xA434: "LensModel",
}
# Maps EXIF GPS tags to tag names.
GPSTAGS = {
0: "GPSVersionID",
1: "GPSLatitudeRef",
2: "GPSLatitude",
3: "GPSLongitudeRef",
4: "GPSLongitude",
5: "GPSAltitudeRef",
6: "GPSAltitude",
7: "GPSTimeStamp",
...
}