-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Writing GPS EXIF information #6657
Copy link
Copy link
Closed
Description
What did you do?
I am trying to geo-reference images and for this I want to add GPS Exif data to the images.
While reading and writing using Pillow seems to work fine, the written GPS Exif Data seems to be invalid and is not accessible in other programs such as the Windows File Property overview.
What did you expect to happen?
When I write GPS Exif that it is also readable for other tools.
What actually happened?
GPS Exif is not available in other programs such as Windows File Properties (left image created with Pillow; right image created with camera) :

What are your OS, Python and Pillow versions?
- OS: Windows 10
- Python: 3.9
- Pillow: 9.2.0
Sample code
import math
from typing import Tuple
from PIL import Image
from PIL.ExifTags import GPSTAGS
def truncate(f: float, n: int = 0) -> float:
"""
Method for truncating a float value to n digits without rounding
:param f: to be truncated
:param n: number of digits
:return: truncated float
"""
return math.floor(f * 10 ** n) / 10 ** n
def get_dms_from_decimal(decimal: float) -> Tuple[float, float, float]:
"""
Convert decimal value to DMS (degrees, minutes, seconds) tuple
:param decimal: to be converted
:return:
"""
degrees = truncate(decimal, 0)
minutes_whole = (decimal - degrees) * 60
minutes = truncate(minutes_whole, 0)
seconds = (minutes_whole - minutes) * 60
return degrees, minutes, seconds
def get_decimal_from_dms(dms: Tuple[float, float, float], ref: str = "N") -> float:
"""
Method for converting a DMS (degrees, minutes, seconds) tuple to a decimal value
:param dms: to be converted
:param ref: compass direction (N, E, S, W)
:return: decimal representation
"""
degrees = dms[0]
minutes = dms[1] / 60.0
seconds = dms[2] / 3600.0
if ref in ['S', 'W']:
degrees = -degrees
minutes = -minutes
seconds = -seconds
return degrees + minutes + seconds
class GpsExifWriter:
"""
Class allowing to write GPS exif data to an image
"""
def get_gps(self, image_path: str) -> Tuple[float, float, float]:
"""
Read gps EXIF information form image
:param image_path: from which EXIF should be read
:return: (lat, lng, alt) tuple
"""
image = Image.open(image_path)
exif = image.getexif()
gps_info = exif.get_ifd(34853)
gps_exif = {
GPSTAGS.get(key, key): value
for key, value in gps_info.items()
}
gps_latitude = gps_exif.get("GPSLatitude")
gps_longitude = gps_exif.get("GPSLongitude")
if gps_longitude is None or gps_latitude is None:
raise Exception("No GPS information available in image")
gps_latitude_ref = gps_exif.get("GPSLatitudeRef") or "N"
gps_longitude_ref = gps_exif.get("GPSLongitudeRef") or "E"
lat = get_decimal_from_dms(gps_latitude, gps_latitude_ref)
lng = get_decimal_from_dms(gps_longitude, gps_longitude_ref)
alt = gps_exif.get("GPSAltitude") or 0
return lat, lng, alt
def write_gps(self, image_path: str, lng: float, lat: float, alt: float, camera_manufacturer: str, camera: str, ) -> None:
"""
Method for writing GPS exif information to an image
:param image_path: to which exif should be added
:param lng: longitude
:param lat: latitude
:param alt: altitude
:param camera_manufacturer: Manufacturer of the camera
:param camera: Camera used
:return: None
"""
image = Image.open(image_path)
exif = image.getexif()
gps_value = {0: b'\x02\x03\x00\x00', 1: 'N', 2: get_dms_from_decimal(lat), 3: 'E', 4: get_dms_from_decimal(lng), 5: b'\x00', 6: alt, 9: 'A', 18: 'WGS-84\x00'}
# TODO writing probably not 100% correct: Reading of written data is possible, but e.g. windows explorer does not show up GPS information
exif[34853] = gps_value
exif[271] = camera_manufacturer
exif[272] = camera
image.save(image_path, exif=exif)
if __name__ == '__main__':
image_path = r"C:\public\some_image.jpg"
writer = GpsExifWriter()
lat = 48.370075472222226
lng = 14.5132905
alt = 563.874
writer.write_gps(image_path, lng, lat, alt, "Manufacturer", "Camera")
gps = writer.get_gps(image_path)
if lat == gps[0] and lng == gps[1] and alt == gps[2]:
print("Re-Read successful")
else:
print("Differences in read and written GPS")Reactions are currently unavailable