# Iterate over a folder with images # print the image gps coordinates # filter for images within a certain gps coordinates box # Use pillow to read image metadata import os import argparse import shutil from PIL import Image def parse_arguments(): parser = argparse.ArgumentParser(description="Filter images within a GPS coordinates box") parser.add_argument("--input_folder", type=str, required=True, help="Path to the input folder containing images") parser.add_argument("--output_folder", type=str, required=True, help="Path to the output folder to save filtered images") gps_box_help = "Bounding box defined by min and max latitude and longitude" \ " Swiss Alps region: --gps_box 46.0 8.0 48.0 9.0" \ " Zurich area: --gps_box 47.2 8.3 47.6 8.7" \ " Bernese Alps: --gps_box 46.4 7.8 46.8 8.3" parser.add_argument("--gps_box", type=float, nargs=4, required=True, metavar=('LAT_MIN', 'LON_MIN', 'LAT_MAX', 'LON_MAX'), help=gps_box_help) parser.add_argument("--dry_run", action="store_true", help="Enable dry run mode") # Add help text for gps_box parser.add_argument("--verbose", action="store_true", help="Enable verbose output") return parser.parse_args() def get_gps_coordinates(image): """Extract GPS coordinates from image EXIF data""" exifdata = image.getexif() # Check if GPS info is available if 34853 not in exifdata: return None, None # Use get_ifd() to access the actual GPS data dictionary try: gps_info = exifdata.get_ifd(34853) except (KeyError, TypeError): return None, None # Check if gps_info is actually a dictionary with GPS data if not isinstance(gps_info, dict) or len(gps_info) == 0: return None, None def convert_to_degrees(value): """Convert GPS coordinates to decimal degrees""" try: if isinstance(value, (list, tuple)) and len(value) >= 3: d, m, s = value[0], value[1], value[2] # Handle rational numbers (tuples like (40, 1) meaning 40/1) if isinstance(d, tuple) and len(d) == 2: d = d[0] / d[1] if isinstance(m, tuple) and len(m) == 2: m = m[0] / m[1] if isinstance(s, tuple) and len(s) == 2: s = s[0] / s[1] return float(d) + (float(m) / 60.0) + (float(s) / 3600.0) return None except (ValueError, TypeError, IndexError, ZeroDivisionError): return None lat = None lon = None try: # Get latitude if 2 in gps_info and 1 in gps_info: # GPSLatitude and GPSLatitudeRef lat = convert_to_degrees(gps_info[2]) if lat is not None and gps_info[1] == 'S': lat = -lat # Get longitude if 4 in gps_info and 3 in gps_info: # GPSLongitude and GPSLongitudeRef lon = convert_to_degrees(gps_info[4]) if lon is not None and gps_info[3] == 'W': lon = -lon except (TypeError, KeyError): # Handle cases where gps_info doesn't support 'in' operator or key access return None, None return lat, lon def is_within_gps_box(image, gps_box): # Get the image gps coordinates from the image metadata latitude, longitude = get_gps_coordinates(image) if latitude is None or longitude is None: print("No GPS data found in image") return False print(f"Image GPS coordinates: {latitude}, {longitude}") lat_min, lon_min, lat_max, lon_max = gps_box return lat_min <= latitude <= lat_max and lon_min <= longitude <= lon_max def main(): args = parse_arguments() if not os.path.exists(args.output_folder): os.makedirs(args.output_folder) for filename in os.listdir(args.input_folder): if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')): image_path = os.path.join(args.input_folder, filename) # Load the image metadata image = Image.open(image_path) if image is None: print(f"Failed to load image: {image_path}") continue if is_within_gps_box(image, args.gps_box): if args.dry_run: print(f"Dry run: Image {filename} is within the GPS box") lat, lon = get_gps_coordinates(image) print(f"coordinates: {lat}, {lon}") continue output_path = os.path.join(args.output_folder, filename) shutil.copy2(image_path, output_path) print(f"Saved filtered image: {output_path}") else: print(f"Image {filename} is outside the bounding box") if __name__ == "__main__": main()