Skip to content

Commit e367fbe

Browse files
committed
added alititude to GPS data as well as other metadata like camera model, focal length, ISO, F-stop, and capture date
1 parent ea15221 commit e367fbe

1 file changed

Lines changed: 53 additions & 11 deletions

File tree

GEMstack/scripts/geotag.py

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numpy as np
77
import math
88
import os
9+
from datetime import datetime
910

1011
def rad_to_deg(rad):
1112
"""Convert radians to degrees"""
@@ -41,8 +42,8 @@ def interpolate_position(gnss_times, gnss_data, target_time):
4142
# Convert to degrees
4243
return rad_to_deg(lat_rad), rad_to_deg(lon_rad)
4344

44-
def convert_gps_to_exif_format(latitude, longitude):
45-
"""Convert GPS coordinates to EXIF format"""
45+
def convert_gps_to_exif_format(latitude, longitude, altitude):
46+
"""Convert GPS coordinates and altitude to EXIF format"""
4647
def decimal_to_dms(decimal):
4748
decimal = float(decimal)
4849
degrees = int(decimal)
@@ -55,7 +56,12 @@ def decimal_to_dms(decimal):
5556
lat_ref = 'N' if latitude >= 0 else 'S'
5657
lon_ref = 'E' if longitude >= 0 else 'W'
5758

58-
return lat_dms, lon_dms, lat_ref, lon_ref
59+
# Convert altitude to EXIF format (rational number)
60+
alt_ref = 0 if altitude >= 0 else 1 # 0 = above sea level, 1 = below sea level
61+
altitude = abs(altitude)
62+
alt_ratio = (int(altitude * 100), 100) # Multiply by 100 for 2 decimal precision
63+
64+
return lat_dms, lon_dms, lat_ref, lon_ref, alt_ratio, alt_ref
5965

6066
# Create images directory if it doesn't exist
6167
os.makedirs('images', exist_ok=True)
@@ -95,18 +101,54 @@ def decimal_to_dms(decimal):
95101
image_time = t.to_sec()
96102
lat, lon = interpolate_position(gnss_times, gnss_messages, image_time)
97103

104+
# Get altitude from GNSS message (you'll need to interpolate this as well)
105+
altitude = gnss_messages[np.searchsorted(gnss_times, image_time)].height
106+
98107
# Convert GPS coordinates to EXIF format
99-
lat_dms, lon_dms, lat_ref, lon_ref = convert_gps_to_exif_format(lat, lon)
108+
lat_dms, lon_dms, lat_ref, lon_ref, alt_ratio, alt_ref = convert_gps_to_exif_format(lat, lon, altitude)
109+
110+
timestamp = datetime.fromtimestamp(t.to_sec())
100111

101-
# Create EXIF data
102112
exif_dict = {
103-
"GPS": {
104-
piexif.GPSIFD.GPSLatitudeRef: lat_ref.encode(),
105-
piexif.GPSIFD.GPSLatitude: lat_dms,
106-
piexif.GPSIFD.GPSLongitudeRef: lon_ref.encode(),
107-
piexif.GPSIFD.GPSLongitude: lon_dms
108-
}
113+
"0th": {
114+
piexif.ImageIFD.Make: "FLIR".encode(),
115+
piexif.ImageIFD.Model: "Blackfly S BFS-U3-16S2C".encode(),
116+
piexif.ImageIFD.Software: "ROS".encode(),
117+
piexif.ImageIFD.DateTime: timestamp.strftime("%Y:%m:%d %H:%M:%S").encode(),
118+
piexif.ImageIFD.ImageDescription: "Captured with ROS and FLIR Blackfly S".encode(),
119+
piexif.ImageIFD.XResolution: (msg.width, 1),
120+
piexif.ImageIFD.YResolution: (msg.height, 1),
121+
piexif.ImageIFD.ResolutionUnit: 2, # inches
122+
},
123+
"Exif": {
124+
piexif.ExifIFD.DateTimeOriginal: timestamp.strftime("%Y:%m:%d %H:%M:%S").encode(),
125+
piexif.ExifIFD.DateTimeDigitized: timestamp.strftime("%Y:%m:%d %H:%M:%S").encode(),
126+
piexif.ExifIFD.ExposureTime: (1, 100), # 1/100 second
127+
piexif.ExifIFD.FNumber: (16, 10), # f/1.6
128+
piexif.ExifIFD.ExposureProgram: 1, # Manual
129+
piexif.ExifIFD.ISOSpeedRatings: 100,
130+
piexif.ExifIFD.ExifVersion: b'0230',
131+
piexif.ExifIFD.ComponentsConfiguration: b'\x01\x02\x03\x00', # RGB
132+
piexif.ExifIFD.FocalLength: (16, 1), # 16mm
133+
piexif.ExifIFD.ColorSpace: 1, # sRGB
134+
piexif.ExifIFD.PixelXDimension: msg.width,
135+
piexif.ExifIFD.PixelYDimension: msg.height,
136+
piexif.ExifIFD.ExposureMode: 1, # Manual exposure
137+
piexif.ExifIFD.WhiteBalance: 1, # Manual white balance
138+
piexif.ExifIFD.SceneCaptureType: 0, # Standard
139+
},
140+
"GPS": {
141+
piexif.GPSIFD.GPSLatitudeRef: lat_ref.encode(),
142+
piexif.GPSIFD.GPSLatitude: lat_dms,
143+
piexif.GPSIFD.GPSLongitudeRef: lon_ref.encode(),
144+
piexif.GPSIFD.GPSLongitude: lon_dms,
145+
piexif.GPSIFD.GPSAltitudeRef: alt_ref,
146+
piexif.GPSIFD.GPSAltitude: alt_ratio,
147+
piexif.GPSIFD.GPSTimeStamp: tuple(map(lambda x: (int(x), 1), timestamp.strftime("%H:%M:%S").split(":"))),
148+
piexif.GPSIFD.GPSDateStamp: timestamp.strftime("%Y:%m:%d").encode(),
149+
piexif.GPSIFD.GPSVersionID: (2, 3, 0, 0),
109150
}
151+
}
110152

111153
# Convert to bytes
112154
exif_bytes = piexif.dump(exif_dict)

0 commit comments

Comments
 (0)