Loading

I once viewed a video from John Hammond on how metadata is saved within an image file. I started to ponder if I could create a python application to retrieve three fundamental values from the metadata if they are present. There are many more values saved in the metadata of an image, and I wanted to attempt to retrieve three. So have you ever wondered how your smartphone knows precisely where you took a photo and when you took it? The answer lies in the image metadata embedded in the file. This metadata contains information about the image, such as its dimensions, color profile, and camera settings. But it can also include information about the location and time of the photo and other data such as the camera model and manufacturer.

This article will explore how to extract the GPS coordinates and data taken from an image file using the Python Imaging Library (PIL) and the ExifTags module. We will reference the code in the prompt and discuss each function.

The first function we encounter is get_exif_data. This function takes an image file path as its argument and returns a dictionary of human-readable tags for the image metadata. It accomplishes this by using the PIL Image module’s _getexif() method, which produces the raw EXIF data for the image. The function then loops through each tag in the EXIF data and converts it to a human-readable form. If the tag is a GPSInfo tag, it calls the get_gps_info function to convert the raw GPS data to a dictionary of human-readable tags.

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

def get_exif_data(image_path):
    """Extract EXIF metadata from the image file."""
    with Image.open(image_path) as img:
        # Get the raw EXIF data
        exif_data = img._getexif()
        if not exif_data:
            return None

        # Convert the raw EXIF data into a dictionary of human-readable tags
        exif_tags = {}
        for tag_id, value in exif_data.items():
            tag = TAGS.get(tag_id, tag_id)
            if tag == 'GPSInfo':
                value = get_gps_info(value)
            exif_tags[tag] = value

        return exif_tags

The get_gps_info function takes the raw GPS data from the EXIF metadata and converts it to a dictionary of human-readable tags. It accomplishes this by looping through each tag in the GPSInfo data and converting its tag ID to a human-readable tag using the GPSTAGS dictionary.

def get_gps_info(gps_info):
    """Convert raw GPS metadata into a dictionary of human-readable tags."""
    gps_tags = {}
    for tag_id, value in gps_info.items():
        tag = GPSTAGS.get(tag_id, tag_id)
        gps_tags[tag] = value
    return gps_tags

The get_gps_data function takes the dictionary of human-readable tags produced by get_exif_data and extracts the GPS latitude and longitude data. It gets the GPSInfo tag from the dictionary, converts the latitude and longitude values from degrees, minutes, seconds format to decimal format using the convert_gps_coords function, and returns the latitude and longitude values.

def get_gps_data(exif_data):
    """Extract the latitude and longitude data from the GPS metadata."""
    gps_info = exif_data.get('GPSInfo')
    if not gps_info:
        return None

    latitude = convert_gps_coords(gps_info['GPSLatitude'], gps_info['GPSLatitudeRef'])
    longitude = convert_gps_coords(gps_info['GPSLongitude'], gps_info['GPSLongitudeRef'])

    return latitude, longitude

The convert_gps_coords function takes the GPS coordinates in degrees, minutes, seconds format and converts them to decimal format. It does this by adding the degrees, minutes, and seconds together, dividing by 60 for minutes and 3600

The function get_gps_data is responsible for extracting the latitude and longitude data from the GPS metadata. The function first checks if the GPS metadata exists by checking if gps_info is None. If the metadata exists, the function extracts the latitude and longitude coordinates using the convert_gps_coords function, which converts the coordinates from degrees, minutes, and seconds to decimals. The function then returns a tuple of the latitude and longitude coordinates.

Finally, decimal_degrees_to_dms converts decimal degree coordinates back to the degrees, minutes, and seconds format. This function takes a decimal degree coordinate as input and returns a tuple of the degrees, minutes, and seconds.

The main section of the code checks if the exif_data dictionary is not empty, indicating that metadata is available for the image. If metadata exists, the code extracts the date taken, latitude, and longitude coordinates from the metadata using the get_exif_data and get_gps_data functions. The code then prints out the date taken, latitude, and longitude coordinates in decimal and degrees, minutes, and seconds formats.

This code provides a robust solution for extracting metadata from an image file, including GPS data. This can be useful for various applications, such as analyzing the location and time of captured images in wildlife research, social media analysis, and surveillance.

Here is the complete code for reference:

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

def get_exif_data(image_path):
    """Extract EXIF metadata from the image file."""
    with Image.open(image_path) as img:
        # Get the raw EXIF data
        exif_data = img._getexif()
        if not exif_data:
            return None

        # Convert the raw EXIF data into a dictionary of human-readable tags
        exif_tags = {}
        for tag_id, value in exif_data.items():
            tag = TAGS.get(tag_id, tag_id)
            if tag == 'GPSInfo':
                value = get_gps_info(value)
            exif_tags[tag] = value

        return exif_tags

def get_gps_info(gps_info):
    """Convert raw GPS metadata into a dictionary of human-readable tags."""
    gps_tags = {}
    for tag_id, value in gps_info.items():
        tag = GPSTAGS.get(tag_id, tag_id)
        gps_tags[tag] = value
    return gps_tags

def get_gps_data(exif_data):
    """Extract the latitude and longitude data from the GPS metadata."""
    gps_info = exif_data.get('GPSInfo')
    if not gps_info:
        return None

    latitude = convert_gps_coords(gps_info['GPSLatitude'], gps_info['GPSLatitudeRef'])
    longitude = convert_gps_coords(gps_info['GPSLongitude'], gps_info['GPSLongitudeRef'])

    return latitude, longitude

def convert_gps_coords(coords, ref):
    """Convert GPS coordinates from (degrees, minutes, seconds) format to decimal format."""
    decimal_coords = float(coords[0]) + (float(coords[1]) / 60) + (float(coords[2]) / 3600)
    if ref in ('S', 'W'):
        decimal_coords = -decimal_coords
    return decimal_coords

def decimal_degrees_to_dms(decimal_degrees):
    degrees = int(decimal_degrees)
    decimal_minutes = abs(decimal_degrees - degrees) * 60
    minutes = int(decimal_minutes)
    seconds = (decimal_minutes - minutes) * 60
    return degrees, minutes, seconds

if __name__ == '__main__':
    # use any jpg image.
    image_path = 'test.jpg'
    exif_data = get_exif_data(image_path)
    if exif_data:
        date_taken = exif_data.get('DateTimeOriginal')
        latitude, longitude = get_gps_data(exif_data)
        print(f'Date taken: {date_taken}')
        print(f'Latitude: {latitude}')
        degrees, minutes, seconds,  = decimal_degrees_to_dms(latitude)
        print(f"Latitude: {degrees}° {minutes}' {seconds:.2f}\"")
        print(f'Longitude: {longitude}')
        degrees, minutes, seconds = decimal_degrees_to_dms(longitude)
        print(f"Longitude: {degrees}° {minutes}' {seconds:.2f}\"")
    else:
        print('No EXIF metadata found in image file.')

The get_exif_data() Function

The get_exif_data() function is the main function in this code, responsible for extracting the EXIF metadata from an image file. The function uses the PIL library to open the image file and get the raw EXIF data. It then converts this raw data into a dictionary of human-readable tags using the TAGS dictionary from the PIL.ExifTags module.

If the image file contains no EXIF data, the function returns None. Otherwise, it iterates through the tags in the dictionary, looking for the GPSInfo tag. If this tag is found, the function calls the get_gps_info() function to convert the raw GPS metadata into a dictionary of human-readable tags. Finally, the function returns the dictionary of EXIF tags.

The get_gps_info() Function

The get_gps_info() function is called by get_exif_data() to convert the raw GPS metadata into a dictionary of human-readable tags. It uses the GPSTAGS dictionary from the PIL.ExifTags module to convert the GPS metadata tags into human-readable form.

The get_gps_data() Function

The get_gps_data() function is called by the main function get_exif_data() to extract the latitude and longitude data from the GPS metadata. It first checks to see if the image file contains GPS metadata. If it does, the function calls the convert_gps_coords() function to convert the GPS coordinates from (degrees, minutes, seconds) format to decimal format.

The function then returns a tuple containing the latitude and longitude values.

The convert_gps_coords() Function

The convert_gps_coords() function is called by get_gps_data() to convert GPS coordinates from (degrees, minutes, seconds) format to decimal format. It takes two arguments: the coordinates and a reference value that indicates whether the coordinates represent north or south latitude or east or west longitude.

The function converts the coordinates to decimal format by adding the degrees, minutes, and seconds values and dividing by the appropriate factor. If the reference value is ‘S’ or ‘W’, it negates the result. The function then returns the decimal value.

The decimal_degrees_to_dms() Function

The decimal_degrees_to_dms() function is called by the main function get_exif_data() to convert the latitude and longitude values from decimal format to (degrees, minutes, seconds) format. It takes a single argument, the decimal value to be converted.

The function calculates the degrees value by rounding the decimal value to the nearest integer. It then calculates the decimal minutes value by subtracting the degree value from the decimal value and multiplying by 60. It rounds the decimal minutes value to the nearest integer to get the minutes value. Finally, it calculates the decimal seconds value by subtracting the degrees and minutes values from the decimal value and multiplying by 3600. It rounds the decimal seconds value to two decimal places and returns the degrees, minutes, and seconds values as a tuple.

Conclusion

In conclusion, extracting metadata from an image file can be done using various programming languages and tools. In this article, we explored how to use Python and the Pillow library to extract GPS, date and other metadata from a JPEG image. The process involved opening the image file with Pillow and accessing its EXIF data using the _getexif() method. The EXIF data was then converted into a dictionary of human-readable tags for ease of use.

We also explored extracting the GPS metadata from the EXIF data and converting it into decimal format for mapping applications. The extracted GPS metadata included the latitude and longitude coordinates and information on their reference directions.

The code presented in this article provides a great starting point for anyone interested in working with image metadata using Python. It is easily modifiable to extract other types of metadata as well. Other Python libraries such as pyexif and exifread offer different methods of working with image metadata.

Add Comment

Your email address will not be published. Required fields are marked *