|
|
|
|
@ -29,7 +29,6 @@
|
|
|
|
|
# |
|
|
|
|
# and partially modified. The code in question is licensed under MIT license. |
|
|
|
|
|
|
|
|
|
import fractions |
|
|
|
|
import logging |
|
|
|
|
import os |
|
|
|
|
import sys |
|
|
|
|
@ -41,17 +40,12 @@ import pilkit.processors
|
|
|
|
|
from PIL import Image as PILImage |
|
|
|
|
from PIL import ImageFile, ImageOps, IptcImagePlugin |
|
|
|
|
from PIL.ExifTags import GPSTAGS, TAGS |
|
|
|
|
from PIL.TiffImagePlugin import IFDRational |
|
|
|
|
from pilkit.processors import Transpose |
|
|
|
|
from pilkit.utils import save_image |
|
|
|
|
|
|
|
|
|
from . import signals, utils |
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
# Pillow 7.2+ |
|
|
|
|
from PIL.TiffImagePlugin import IFDRational |
|
|
|
|
except ImportError: |
|
|
|
|
IFDRational = None |
|
|
|
|
|
|
|
|
|
# Force loading of truncated files |
|
|
|
|
ImageFile.LOAD_TRUNCATED_IMAGES = True |
|
|
|
|
|
|
|
|
|
@ -316,14 +310,9 @@ def get_image_metadata(filename):
|
|
|
|
|
def dms_to_degrees(v): |
|
|
|
|
"""Convert degree/minute/second to decimal degrees.""" |
|
|
|
|
|
|
|
|
|
if IFDRational and isinstance(v[0], IFDRational): |
|
|
|
|
d = float(v[0]) |
|
|
|
|
m = float(v[1]) |
|
|
|
|
s = float(v[2]) |
|
|
|
|
else: |
|
|
|
|
d = float(v[0][0]) / float(v[0][1]) |
|
|
|
|
m = float(v[1][0]) / float(v[1][1]) |
|
|
|
|
s = float(v[2][0]) / float(v[2][1]) |
|
|
|
|
d = float(v[0]) |
|
|
|
|
m = float(v[1]) |
|
|
|
|
s = float(v[2]) |
|
|
|
|
return d + (m / 60.0) + (s / 3600.0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -341,32 +330,21 @@ def get_exif_tags(data, datetime_format="%c"):
|
|
|
|
|
if "FNumber" in data: |
|
|
|
|
fnumber = data["FNumber"] |
|
|
|
|
try: |
|
|
|
|
if IFDRational and isinstance(fnumber, IFDRational): |
|
|
|
|
simple["fstop"] = float(fnumber) |
|
|
|
|
else: |
|
|
|
|
simple["fstop"] = float(fnumber[0]) / fnumber[1] |
|
|
|
|
simple["fstop"] = float(fnumber) |
|
|
|
|
except Exception: |
|
|
|
|
logger.debug("Skipped invalid FNumber: %r", fnumber, exc_info=True) |
|
|
|
|
|
|
|
|
|
if "FocalLength" in data: |
|
|
|
|
focal = data["FocalLength"] |
|
|
|
|
try: |
|
|
|
|
if IFDRational and isinstance(focal, IFDRational): |
|
|
|
|
simple["focal"] = round(float(focal)) |
|
|
|
|
else: |
|
|
|
|
simple["focal"] = round(float(focal[0]) / focal[1]) |
|
|
|
|
simple["focal"] = round(float(focal)) |
|
|
|
|
except Exception: |
|
|
|
|
logger.debug("Skipped invalid FocalLength: %r", focal, exc_info=True) |
|
|
|
|
|
|
|
|
|
if "ExposureTime" in data: |
|
|
|
|
exptime = data["ExposureTime"] |
|
|
|
|
if IFDRational and isinstance(exptime, IFDRational): |
|
|
|
|
if isinstance(exptime, IFDRational): |
|
|
|
|
simple["exposure"] = f"{exptime.numerator}/{exptime.denominator}" |
|
|
|
|
elif isinstance(exptime, tuple): |
|
|
|
|
try: |
|
|
|
|
simple["exposure"] = str(fractions.Fraction(exptime[0], exptime[1])) |
|
|
|
|
except ZeroDivisionError: |
|
|
|
|
logger.info("Invalid ExposureTime: %r", exptime) |
|
|
|
|
elif isinstance(exptime, int): |
|
|
|
|
simple["exposure"] = str(exptime) |
|
|
|
|
else: |
|
|
|
|
|