-
Notifications
You must be signed in to change notification settings - Fork 1
/
exif.py
110 lines (92 loc) · 3.22 KB
/
exif.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""
Photo Exif extraction functions
"""
from dataclasses import dataclass
from fractions import Fraction
from PIL import Image
from PIL.ExifTags import TAGS
def format_shutter_speed(shutter_speed: str) -> str:
"""
Convert a decimal value to a fraction display.
Used to display shutter speed values.
"""
try:
fraction = Fraction(shutter_speed).limit_denominator()
if fraction >= 1:
# return f"{fraction.numerator}/{fraction.denominator}"
return f"{fraction.numerator}"
else:
return f"1/{int(1/float(shutter_speed))}"
except (ValueError, ZeroDivisionError):
return shutter_speed
def format_focal_length(focal_length: str) -> str:
"""
Round Focal Length
"""
try:
focal_length_dp = focal_length[::-1].find('.') #https://docs.python.org/dev/library/stdtypes.html#str.find
if focal_length_dp >= 2: # Checks if the focal length has 2 decimal places or greater eg 24.878mm
return round(float(focal_length), 2)
else:
return round(float(focal_length)) # Rounds focal length to nearest whole number if a single decimal point is present eg 24.0mm is 24mm
except ValueError:
return focal_length
@dataclass
class ExifItem:
tag: str
data: str
formatter = {
'Make': 'Shot on {dataval}',
'FocalLength': '{dataval}mm',
'FNumber': 'f/{dataval}',
'ISOSpeedRatings': 'ISO{dataval}',
'ExposureTime': '{dataval} sec'
}
def __str__(self) -> str:
if self.data is None or self.data == '':
return ''
fmt_data = str(self.data).strip()
# Deal with any special case data formatting
if self.tag == 'ExposureTime':
fmt_data = format_shutter_speed(fmt_data)
# Deal with any special case data formatting
if self.tag == 'FocalLength':
fmt_data = format_focal_length(fmt_data)
# Apply the string template formatting defined in self.formatter
if self.tag in self.formatter:
return self.formatter[self.tag].format(dataval=fmt_data)
return fmt_data
def get_exif(img: Image) -> dict:
"""Load the exif data from an image.
Args:
img (Image): Pillow image object.
Returns:
dict: dictionary with exif data
"""
exif_data = img._getexif()
exif_dict = {
'Make': '',
'Model': '',
'LensMake': '',
'LensModel': '',
'FNumber': '',
'FocalLength': '',
'ISOSpeedRatings': '',
'ExposureTime': ''
}
if exif_data:
# Iterate through the EXIF data and store it in the dictionary
for tag_id in exif_data:
tag = TAGS.get(tag_id, tag_id)
data = exif_data.get(tag_id)
if isinstance(data, bytes):
try:
data = data.decode()
except UnicodeDecodeError as e:
# print(f'Error decoding tag {tag}', e)
# Expect decoding errors, ust ignore as we don't need the exif these happen on.
pass
exif_dict[tag] = ExifItem(tag, data)
# Print the EXIF data dictionary
# print(exif_dict)
return exif_dict