mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2025-01-06 06:46:11 +00:00
521 lines
16 KiB
Python
521 lines
16 KiB
Python
|
import PIL
|
||
|
from PIL import Image
|
||
|
from PIL.ExifTags import TAGS, GPSTAGS
|
||
|
from datetime import datetime
|
||
|
|
||
|
|
||
|
class metadata:
|
||
|
def yoink(filename):
|
||
|
exif = metadata.getFile(filename)
|
||
|
|
||
|
if exif:
|
||
|
formatted = metadata.format(exif)
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
return metadata.deleteEmpty(formatted)
|
||
|
|
||
|
def deleteEmpty(dict):
|
||
|
new_dict = {}
|
||
|
|
||
|
for section in dict:
|
||
|
tmp = {}
|
||
|
for value in dict[section]:
|
||
|
if dict[section][value]['raw'] != None:
|
||
|
if isinstance(dict[section][value]['raw'], PIL.TiffImagePlugin.IFDRational):
|
||
|
dict[section][value]['raw'] = dict[section][value]['raw'].__float__()
|
||
|
elif isinstance(dict[section][value]['raw'], bytes):
|
||
|
dict[section][value]['raw'] = dict[section][value]['raw'].decode('utf-8')
|
||
|
|
||
|
tmp[value] = dict[section][value]
|
||
|
|
||
|
|
||
|
if len(tmp) > 0:
|
||
|
new_dict[section] = tmp
|
||
|
|
||
|
return new_dict
|
||
|
|
||
|
def getFile(filename):
|
||
|
try:
|
||
|
file = Image.open(filename)
|
||
|
raw = file._getexif()
|
||
|
exif = {}
|
||
|
|
||
|
for tag, value in TAGS.items():
|
||
|
|
||
|
if tag in raw:
|
||
|
data = raw[tag]
|
||
|
else:
|
||
|
data = None
|
||
|
|
||
|
exif[value] = {"tag": tag, "raw": data}
|
||
|
|
||
|
file.close()
|
||
|
|
||
|
return exif
|
||
|
except Exception as e:
|
||
|
return None
|
||
|
|
||
|
def format(raw):
|
||
|
exif = {}
|
||
|
|
||
|
exif['Photographer'] = {
|
||
|
'Artist': {
|
||
|
'type': 'text',
|
||
|
'raw': raw["Artist"]["raw"]
|
||
|
},
|
||
|
'Comment': {
|
||
|
'type': 'text',
|
||
|
'raw': raw["UserComment"]["raw"]
|
||
|
},
|
||
|
'Description': {
|
||
|
'type': 'text',
|
||
|
'raw': raw["ImageDescription"]["raw"]
|
||
|
},
|
||
|
'Date Digitized': {
|
||
|
'type': 'date',
|
||
|
'raw': raw["DateTimeDigitized"]["raw"],
|
||
|
'formatted': metadata.date(raw["DateTimeDigitized"]["raw"])
|
||
|
},
|
||
|
'Copyright': {
|
||
|
'type': 'text',
|
||
|
'raw': raw["Copyright"]["raw"]
|
||
|
},
|
||
|
}
|
||
|
exif['Camera'] = {
|
||
|
'Model': {
|
||
|
'type': 'text',
|
||
|
'raw': raw['Model']['raw']
|
||
|
},
|
||
|
'Make': {
|
||
|
'type': 'text',
|
||
|
'raw': raw['Make']['raw']
|
||
|
},
|
||
|
'Lense Model': {
|
||
|
'type': 'text',
|
||
|
'raw': raw['LensModel']['raw'],
|
||
|
},
|
||
|
'Lense Spec': {
|
||
|
'type': 'text',
|
||
|
'raw': raw['LensSpecification']['raw'],
|
||
|
'formatted': metadata.lensSpecification(raw['LensSpecification']['raw'])
|
||
|
},
|
||
|
'Component Config': {
|
||
|
'type': 'text',
|
||
|
'raw': raw['ComponentsConfiguration']['raw'],
|
||
|
'formatted': metadata.componentsConfiguration(raw['ComponentsConfiguration']['raw'])
|
||
|
},
|
||
|
'Date Processed': {
|
||
|
'type': 'date',
|
||
|
'raw': raw['DateTime']['raw'],
|
||
|
'formatted': metadata.date(raw['DateTime']['raw'])
|
||
|
},
|
||
|
}
|
||
|
exif['Software'] = {
|
||
|
'Software': {
|
||
|
'type': 'text',
|
||
|
'raw': raw['Software']['raw']
|
||
|
},
|
||
|
'Colour Space': {
|
||
|
'type': 'number',
|
||
|
'raw': raw['ColorSpace']['raw'],
|
||
|
'formatted': metadata.colorSpace(raw['ColorSpace']['raw'])
|
||
|
},
|
||
|
'Compression': {
|
||
|
'type': 'number',
|
||
|
'raw': raw['Compression']['raw'],
|
||
|
'formatted': metadata.compression(raw['Compression']['raw'])
|
||
|
},
|
||
|
}
|
||
|
exif['Photo'] = {
|
||
|
'FNumber': {
|
||
|
'type': 'fnumber',
|
||
|
'raw': raw["FNumber"]["raw"],
|
||
|
'formatted': metadata.fnumber(raw["FNumber"]["raw"])
|
||
|
},
|
||
|
'Focal Length': {
|
||
|
'type': 'focal',
|
||
|
'raw': raw["FocalLength"]["raw"]
|
||
|
},
|
||
|
'Focal Length - Film': {
|
||
|
'type': 'focal',
|
||
|
'raw': raw["FocalLengthIn35mmFilm"]["raw"]
|
||
|
},
|
||
|
'Max Aperture': {
|
||
|
'type': 'fnumber',
|
||
|
'raw': raw["MaxApertureValue"]["raw"],
|
||
|
'formatted': metadata.fnumber(raw["MaxApertureValue"]["raw"])
|
||
|
},
|
||
|
'Aperture': {
|
||
|
'type': 'fnumber',
|
||
|
'raw': raw["ApertureValue"]["raw"],
|
||
|
'formatted': metadata.fnumber(raw["ApertureValue"]["raw"])
|
||
|
},
|
||
|
'Shutter Speed': {
|
||
|
'type': 'shutter',
|
||
|
'raw': raw["ShutterSpeedValue"]["raw"],
|
||
|
'formatted': metadata.shutter(raw["ShutterSpeedValue"]["raw"])
|
||
|
},
|
||
|
'ISO Speed Ratings': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["ISOSpeedRatings"]["raw"],
|
||
|
'formatted': metadata.iso(raw["ISOSpeedRatings"]["raw"])
|
||
|
},
|
||
|
'ISO Speed': {
|
||
|
'type': 'iso',
|
||
|
'raw': raw["ISOSpeed"]["raw"],
|
||
|
'formatted': metadata.iso(raw["ISOSpeed"]["raw"])
|
||
|
},
|
||
|
'Sensitivity Type': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["SensitivityType"]["raw"],
|
||
|
'formatted': metadata.sensitivityType(raw["SensitivityType"]["raw"])
|
||
|
},
|
||
|
'Exposure Bias': {
|
||
|
'type': 'ev',
|
||
|
'raw': raw["ExposureBiasValue"]["raw"],
|
||
|
'formatted': metadata.ev(raw["ExposureBiasValue"]["raw"])
|
||
|
},
|
||
|
'Exposure Time': {
|
||
|
'type': 'shutter',
|
||
|
'raw': raw["ExposureTime"]["raw"],
|
||
|
'formatted': metadata.shutter(raw["ExposureTime"]["raw"])
|
||
|
},
|
||
|
'Exposure Mode': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["ExposureMode"]["raw"],
|
||
|
'formatted': metadata.exposureMode(raw["ExposureMode"]["raw"])
|
||
|
},
|
||
|
'Exposure Program': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["ExposureProgram"]["raw"],
|
||
|
'formatted': metadata.exposureProgram(raw["ExposureProgram"]["raw"])
|
||
|
},
|
||
|
'White Balance': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["WhiteBalance"]["raw"],
|
||
|
'formatted': metadata.whiteBalance(raw["WhiteBalance"]["raw"])
|
||
|
},
|
||
|
'Flash': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["Flash"]["raw"],
|
||
|
'formatted': metadata.flash(raw["Flash"]["raw"])
|
||
|
},
|
||
|
'Metering Mode': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["MeteringMode"]["raw"],
|
||
|
'formatted': metadata.meteringMode(raw["MeteringMode"]["raw"])
|
||
|
},
|
||
|
'Light Source': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["LightSource"]["raw"],
|
||
|
'formatted': metadata.lightSource(raw["LightSource"]["raw"])
|
||
|
},
|
||
|
'Scene Capture Type': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["SceneCaptureType"]["raw"],
|
||
|
'formatted': metadata.sceneCaptureType(raw["SceneCaptureType"]["raw"])
|
||
|
},
|
||
|
'Scene Type': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["SceneType"]["raw"],
|
||
|
'formatted': metadata.sceneType(raw["SceneType"]["raw"])
|
||
|
},
|
||
|
}
|
||
|
exif['File'] = {
|
||
|
'Width': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["ImageWidth"]["raw"]
|
||
|
},
|
||
|
'Height': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["ImageLength"]["raw"]
|
||
|
},
|
||
|
'Orientation': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["Orientation"]["raw"],
|
||
|
'formatted': metadata.orientation(raw["Orientation"]["raw"])
|
||
|
},
|
||
|
'Xresolution': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["XResolution"]["raw"]
|
||
|
},
|
||
|
'Yresolution': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["YResolution"]["raw"]
|
||
|
},
|
||
|
'Resolution Units': {
|
||
|
'type': 'number',
|
||
|
'raw': raw["ResolutionUnit"]["raw"],
|
||
|
'formatted': metadata.resolutionUnit(raw["ResolutionUnit"]["raw"])
|
||
|
},
|
||
|
}
|
||
|
|
||
|
return exif
|
||
|
|
||
|
def date(date):
|
||
|
date_format = '%Y:%m:%d %H:%M:%S'
|
||
|
|
||
|
if date:
|
||
|
return str(datetime.strptime(date, date_format))
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def fnumber(value):
|
||
|
if value != None:
|
||
|
return 'f/' + str(value)
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def iso(value):
|
||
|
if value != None:
|
||
|
return 'ISO ' + str(value)
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def shutter(value):
|
||
|
if value != None:
|
||
|
return str(value) + 's'
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def focal(value):
|
||
|
if value != None:
|
||
|
return str(value[0] / value[1]) + 'mm'
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def ev(value):
|
||
|
if value != None:
|
||
|
return str(value) + 'EV'
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def colorSpace(value):
|
||
|
types = {
|
||
|
1: 'sRGB',
|
||
|
65535: 'Uncalibrated',
|
||
|
0: 'Reserved'
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def flash(value):
|
||
|
types = {
|
||
|
0: 'Flash did not fire',
|
||
|
1: 'Flash fired',
|
||
|
5: 'Strobe return light not detected',
|
||
|
7: 'Strobe return light detected',
|
||
|
9: 'Flash fired, compulsory flash mode',
|
||
|
13: 'Flash fired, compulsory flash mode, return light not detected',
|
||
|
15: 'Flash fired, compulsory flash mode, return light detected',
|
||
|
16: 'Flash did not fire, compulsory flash mode',
|
||
|
24: 'Flash did not fire, auto mode',
|
||
|
25: 'Flash fired, auto mode',
|
||
|
29: 'Flash fired, auto mode, return light not detected',
|
||
|
31: 'Flash fired, auto mode, return light detected',
|
||
|
32: 'No flash function',
|
||
|
65: 'Flash fired, red-eye reduction mode',
|
||
|
69: 'Flash fired, red-eye reduction mode, return light not detected',
|
||
|
71: 'Flash fired, red-eye reduction mode, return light detected',
|
||
|
73: 'Flash fired, compulsory flash mode, red-eye reduction mode',
|
||
|
77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
|
||
|
79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
|
||
|
89: 'Flash fired, auto mode, red-eye reduction mode',
|
||
|
93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
|
||
|
95: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def exposureProgram(value):
|
||
|
types = {
|
||
|
0: 'Not defined',
|
||
|
1: 'Manual',
|
||
|
2: 'Normal program',
|
||
|
3: 'Aperture priority',
|
||
|
4: 'Shutter priority',
|
||
|
5: 'Creative program',
|
||
|
6: 'Action program',
|
||
|
7: 'Portrait mode',
|
||
|
8: 'Landscape mode'
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def meteringMode(value):
|
||
|
types = {
|
||
|
0: 'Unknown',
|
||
|
1: 'Average',
|
||
|
2: 'Center-Weighted Average',
|
||
|
3: 'Spot',
|
||
|
4: 'Multi-Spot',
|
||
|
5: 'Pattern',
|
||
|
6: 'Partial',
|
||
|
255: 'Other'
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def resolutionUnit(value):
|
||
|
types = {
|
||
|
1: 'No absolute unit of measurement',
|
||
|
2: 'Inch',
|
||
|
3: 'Centimeter'
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def lightSource(value):
|
||
|
types = {
|
||
|
0: 'Unknown',
|
||
|
1: 'Daylight',
|
||
|
2: 'Fluorescent',
|
||
|
3: 'Tungsten (incandescent light)',
|
||
|
4: 'Flash',
|
||
|
9: 'Fine weather',
|
||
|
10: 'Cloudy weather',
|
||
|
11: 'Shade',
|
||
|
12: 'Daylight fluorescent (D 5700 - 7100K)',
|
||
|
13: 'Day white fluorescent (N 4600 - 5400K)',
|
||
|
14: 'Cool white fluorescent (W 3900 - 4500K)',
|
||
|
15: 'White fluorescent (WW 3200 - 3700K)',
|
||
|
17: 'Standard light A',
|
||
|
18: 'Standard light B',
|
||
|
19: 'Standard light C',
|
||
|
20: 'D55',
|
||
|
21: 'D65',
|
||
|
22: 'D75',
|
||
|
23: 'D50',
|
||
|
24: 'ISO studio tungsten',
|
||
|
255: 'Other light source',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def sceneCaptureType(value):
|
||
|
types = {
|
||
|
0: 'Standard',
|
||
|
1: 'Landscape',
|
||
|
2: 'Portrait',
|
||
|
3: 'Night scene',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def sceneType(value):
|
||
|
if value:
|
||
|
return 'Directly photographed image'
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def whiteBalance(value):
|
||
|
types = {
|
||
|
0: 'Auto white balance',
|
||
|
1: 'Manual white balance',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def exposureMode(value):
|
||
|
types = {
|
||
|
0: 'Auto exposure',
|
||
|
1: 'Manual exposure',
|
||
|
2: 'Auto bracket',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def sensitivityType(value):
|
||
|
types = {
|
||
|
0: 'Unknown',
|
||
|
1: 'Standard Output Sensitivity',
|
||
|
2: 'Recommended Exposure Index',
|
||
|
3: 'ISO Speed',
|
||
|
4: 'Standard Output Sensitivity and Recommended Exposure Index',
|
||
|
5: 'Standard Output Sensitivity and ISO Speed',
|
||
|
6: 'Recommended Exposure Index and ISO Speed',
|
||
|
7: 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def lensSpecification(value):
|
||
|
if value:
|
||
|
return str(value[0] / value[1]) + 'mm - ' + str(value[2] / value[3]) + 'mm'
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def compression(value):
|
||
|
types = {
|
||
|
1: 'Uncompressed',
|
||
|
6: 'JPEG compression',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def orientation(value):
|
||
|
types = {
|
||
|
1: 'Horizontal (normal)',
|
||
|
2: 'Mirror horizontal',
|
||
|
3: 'Rotate 180',
|
||
|
4: 'Mirror vertical',
|
||
|
5: 'Mirror horizontal and rotate 270 CW',
|
||
|
6: 'Rotate 90 CW',
|
||
|
7: 'Mirror horizontal and rotate 90 CW',
|
||
|
8: 'Rotate 270 CW',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return types[int(value)]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
def componentsConfiguration(value):
|
||
|
types = {
|
||
|
0: '',
|
||
|
1: 'Y',
|
||
|
2: 'Cb',
|
||
|
3: 'Cr',
|
||
|
4: 'R',
|
||
|
5: 'G',
|
||
|
6: 'B',
|
||
|
}
|
||
|
|
||
|
try:
|
||
|
return ''.join([types[int(x)] for x in value])
|
||
|
except:
|
||
|
return None
|