|
|
|
|
@ -6,50 +6,9 @@ import filetype
|
|
|
|
|
from os import path, walk
|
|
|
|
|
from sys import argv, stderr
|
|
|
|
|
from shutil import move
|
|
|
|
|
from fractions import Fraction
|
|
|
|
|
import sqlite3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def decimal_from_rational64u(dms: str, ref):
|
|
|
|
|
"""Convert coordinates from rational64u EXIF uses to represent
|
|
|
|
|
degrees, minutes, and seconds of a point to decimal format
|
|
|
|
|
Take into account reference cardinal direction and
|
|
|
|
|
turn [S]outh and [W]est coordinates negative
|
|
|
|
|
|
|
|
|
|
General formula is
|
|
|
|
|
dec_degree = degreesNumerator / degreesDenominator
|
|
|
|
|
+ minutesNumerator / minutesDenominator / 60
|
|
|
|
|
+ secondsNumerator / secondsDenominator / 3600
|
|
|
|
|
|
|
|
|
|
https://en.wikipedia.org/wiki/Geographic_coordinate_conversion
|
|
|
|
|
https://gis.stackexchange.com/questions/136925/how-to-parse-exif-gps-information-to-lat-lng-decimal-numbers
|
|
|
|
|
|
|
|
|
|
>>> decimal_from_rational64u(dms="42/1, 18/1, 2914/100", "S")
|
|
|
|
|
-42.30809
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# 1 split by comma
|
|
|
|
|
# 2 turn each into Fraction
|
|
|
|
|
# 3 zip fractions with their respective (degrees, minutes, seconds) denominator
|
|
|
|
|
# 4 divide the fraction
|
|
|
|
|
# 5 sum up the result
|
|
|
|
|
# 6 convert to decimal
|
|
|
|
|
# 7 round to 5th decimal point
|
|
|
|
|
dec_coordinates = round(
|
|
|
|
|
float(
|
|
|
|
|
sum(
|
|
|
|
|
a / b
|
|
|
|
|
for (a, b) in zip((Fraction(f) for f in dms.split(",")), (1, 60, 3600))
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
5,
|
|
|
|
|
)
|
|
|
|
|
if ref in ("S", "W"):
|
|
|
|
|
dec_coordinates = -dec_coordinates
|
|
|
|
|
|
|
|
|
|
return dec_coordinates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_pictures(source: str, dest_shrunk: str, dest_original: str):
|
|
|
|
|
"""Process images from the base directory in the first command line argument.
|
|
|
|
|
Place the resized copies to dest_shrunk and
|
|
|
|
|
@ -73,14 +32,8 @@ def process_pictures(source: str, dest_shrunk: str, dest_original: str):
|
|
|
|
|
(k[5:], v) for k, v in image.metadata.items() if k.startswith("exif:")
|
|
|
|
|
)
|
|
|
|
|
with image.clone() as cloned:
|
|
|
|
|
# adjust an image so that its orientation is suitable for viewing
|
|
|
|
|
# (i.e. top-left orientation) by checking EXIF data
|
|
|
|
|
cloned.auto_orient()
|
|
|
|
|
# strip an image of all profiles and comments
|
|
|
|
|
cloned.strip() # Q: may damage icc, do we allow that or use smh else?
|
|
|
|
|
# resize the shorter side to be no more than 1000px
|
|
|
|
|
# https://legacy.imagemagick.org/discourse-server/viewtopic.php?p=44329#p44329
|
|
|
|
|
cloned.transform(resize="1000^>") # Q: what do we want here?
|
|
|
|
|
cloned.transform(resize="50%") # Q: what do we want here?
|
|
|
|
|
# move them to the processed folder
|
|
|
|
|
cloned.save(filename=path.join(dest_shrunk, filename))
|
|
|
|
|
|
|
|
|
|
@ -94,12 +47,10 @@ def process_pictures(source: str, dest_shrunk: str, dest_original: str):
|
|
|
|
|
"ResizedImage": path.join(dest_shrunk, filename),
|
|
|
|
|
"OriginalImage": path.join(dest_original, filename),
|
|
|
|
|
"DateTimeOriginal": exif["DateTimeOriginal"], # Q: normalize it?
|
|
|
|
|
"GPSLatitude": decimal_from_rational64u(
|
|
|
|
|
exif["GPSLatitude"], exif["GPSLatitudeRef"]
|
|
|
|
|
),
|
|
|
|
|
"GPSLongitude": decimal_from_rational64u(
|
|
|
|
|
exif["GPSLongitude"], exif["GPSLongitudeRef"]
|
|
|
|
|
),
|
|
|
|
|
"GPSLatitude": exif["GPSLatitude"],
|
|
|
|
|
"GPSLatitudeRef": exif["GPSLatitudeRef"],
|
|
|
|
|
"GPSLongitude": exif["GPSLongitude"],
|
|
|
|
|
"GPSLongitudeRef": exif["GPSLongitudeRef"],
|
|
|
|
|
}
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
print(f"Image '{filename}' has no valid exif")
|
|
|
|
|
@ -121,14 +72,18 @@ def update_database(pic_info: dict, db_location: str):
|
|
|
|
|
"""INSERT INTO images(resizedpath,
|
|
|
|
|
origpath,
|
|
|
|
|
date,
|
|
|
|
|
GPSLatitude,
|
|
|
|
|
GPSLongitude)
|
|
|
|
|
GPSLatitude,
|
|
|
|
|
GPSLatitudeRef,
|
|
|
|
|
GPSLongitude,
|
|
|
|
|
GPSLongitudeRef)
|
|
|
|
|
|
|
|
|
|
VALUES (:ResizedImage,
|
|
|
|
|
:OriginalImage,
|
|
|
|
|
:DateTimeOriginal,
|
|
|
|
|
:GPSLatitude,
|
|
|
|
|
:GPSLongitude)
|
|
|
|
|
:GPSLatitudeRef,
|
|
|
|
|
:GPSLongitude,
|
|
|
|
|
:GPSLongitudeRef)
|
|
|
|
|
""",
|
|
|
|
|
pic_info,
|
|
|
|
|
)
|
|
|
|
|
@ -159,8 +114,10 @@ def check_database(database_path: str):
|
|
|
|
|
resizedpath TEXT NOT NULL,
|
|
|
|
|
origpath TEXT NOT NULL,
|
|
|
|
|
date TEXT,
|
|
|
|
|
GPSLatitude REAL,
|
|
|
|
|
GPSLongitude REAL
|
|
|
|
|
GPSLatitude TEXT,
|
|
|
|
|
GPSLatitudeRef TEXT,
|
|
|
|
|
GPSLongitude TEXT,
|
|
|
|
|
GPSLongitudeRef TEXT
|
|
|
|
|
)"""
|
|
|
|
|
)
|
|
|
|
|
con.commit()
|
|
|
|
|
|