Photo De-Duplication
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
dedup/coordinates.py

131 lines
3.7 KiB

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
import os
import math
"""
Copyright (c) 2025 by Robert Strutts
License: MIT
"""
def get_exif_data(image_path):
"""Get EXIF data from image file"""
try:
with Image.open(image_path) as img:
exif_data = img._getexif()
if exif_data is not None:
return {TAGS.get(tag, tag): value for tag, value in exif_data.items()}
except (AttributeError, IOError, KeyError, IndexError) as e:
print(f"Error reading EXIF data: {e}")
return None
def get_camera_info(exif):
if not exif:
return None
camera_info = {
'Make': exif.get('Make', 'Unknown'),
'Model': exif.get('Model', 'Unknown')
}
# Some cameras store model in 'Camera Model Name' instead
if camera_info['Model'] == 'Unknown':
camera_info['Model'] = exif.get('Camera Model Name', 'Unknown')
return camera_info
def get_gps_info(exif_data):
"""Extract GPS information from EXIF data"""
if not exif_data or 'GPSInfo' not in exif_data:
return None
gps_info = {}
for key in exif_data['GPSInfo'].keys():
decode = GPSTAGS.get(key, key)
gps_info[decode] = exif_data['GPSInfo'][key]
return gps_info
def convert_to_degrees(value):
"""Convert GPS coordinates stored in EXIF to degrees in decimal format"""
d, m, s = value
return d + (m / 60.0) + (s / 3600.0)
def get_lat_lon(gps_info):
"""Get latitude and longitude from GPSInfo dictionary"""
if not gps_info:
return None, None
try:
lat_ref = gps_info.get('GPSLatitudeRef', 'N')
lat = gps_info.get('GPSLatitude')
lon_ref = gps_info.get('GPSLongitudeRef', 'E')
lon = gps_info.get('GPSLongitude')
if lat and lon:
lat_degrees = convert_to_degrees(lat)
if lat_ref == 'S':
lat_degrees = -lat_degrees
lon_degrees = convert_to_degrees(lon)
if lon_ref == 'W':
lon_degrees = -lon_degrees
return lat_degrees, lon_degrees
except (TypeError, AttributeError) as e:
print(f"Error converting coordinates: {e}")
return None, None
import math
def haversine_distance_feet(coord1, coord2):
"""
Calculate the Haversine distance between two coordinates in feet.
Returns:
float: distance in feet
"""
# Earth radius in meters
R = 6371000
lat1, lon1 = math.radians(coord1[0]), math.radians(coord1[1])
lat2, lon2 = math.radians(coord2[0]), math.radians(coord2[1])
dlat = lat2 - lat1
dlon = lon2 - lon1
a = (math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
# Distance in meters
distance_meters = R * c
# Convert to feet
return distance_meters * 3.28084
"""
Example points:
point1 = (40.7484, -73.9857) # Empire State Building coordinates (approximately)
point2 = (40.748405, -73.985685) # about 10 feet northeast of point1
"""
def get_coordinates_from_image(image_path):
"""Main function to get coordinates from image file"""
exif_data = get_exif_data(image_path)
if not exif_data:
print("No EXIF data found in image.")
return None
gps_info = get_gps_info(exif_data)
if not gps_info:
print("No GPS information found in EXIF data.")
return None
camera_info = get_camera_info(exif_data)
latitude, longitude = get_lat_lon(gps_info)
if latitude is not None and longitude is not None:
return (camera_info, latitude, longitude)
else:
print("Could not extract valid coordinates.")
return None