Claude Code Plugins

Community-maintained marketplace

Feedback
3
0

Extract EXIF metadata from images including GPS coordinates, camera settings, and timestamps. Map photo locations and strip metadata for privacy.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name image-metadata-tool
description Extract EXIF metadata from images including GPS coordinates, camera settings, and timestamps. Map photo locations and strip metadata for privacy.

Image Metadata Tool

Extract, analyze, and manage EXIF metadata from images with GPS mapping and privacy features.

Features

  • EXIF Extraction: Camera, lens, settings, timestamps
  • GPS Data: Extract coordinates, map locations
  • Metadata Removal: Strip EXIF for privacy
  • Batch Processing: Process multiple images
  • Map Generation: Create location maps from photos
  • Export: JSON, CSV, HTML reports

Quick Start

from image_metadata import ImageMetadata

meta = ImageMetadata()
meta.load("photo.jpg")

# Get all metadata
info = meta.extract()
print(f"Camera: {info['camera']}")
print(f"Date: {info['datetime']}")

# Get GPS coordinates
gps = meta.get_gps()
if gps:
    print(f"Location: {gps['latitude']}, {gps['longitude']}")

CLI Usage

# Extract metadata
python image_metadata.py --input photo.jpg

# Extract with GPS info
python image_metadata.py --input photo.jpg --gps

# Batch extract from folder
python image_metadata.py --input ./photos/ --output metadata.csv

# Generate location map
python image_metadata.py --input ./photos/ --map locations.html

# Strip metadata (create clean copy)
python image_metadata.py --input photo.jpg --strip --output clean_photo.jpg

# Batch strip metadata
python image_metadata.py --input ./photos/ --strip --output ./clean_photos/

# JSON output
python image_metadata.py --input photo.jpg --json

# Get specific fields
python image_metadata.py --input photo.jpg --fields camera,datetime,gps,dimensions

API Reference

ImageMetadata Class

class ImageMetadata:
    def __init__(self)

    # Loading
    def load(self, filepath: str) -> 'ImageMetadata'

    # Extraction
    def extract(self) -> dict
    def get_camera_info(self) -> dict
    def get_datetime(self) -> dict
    def get_gps(self) -> dict
    def get_dimensions(self) -> dict
    def get_all_exif(self) -> dict

    # Privacy
    def strip_metadata(self, output: str, keep_orientation: bool = True) -> str
    def has_location(self) -> bool

    # Batch operations
    def extract_batch(self, folder: str, recursive: bool = False) -> list
    def strip_batch(self, input_folder: str, output_folder: str) -> list

    # Maps
    def generate_map(self, images: list, output: str) -> str

    # Export
    def to_json(self, output: str) -> str
    def to_csv(self, output: str) -> str

Extracted Metadata

Camera Information

camera_info = meta.get_camera_info()

# Returns:
{
    "make": "Canon",
    "model": "EOS R5",
    "lens": "RF 24-70mm F2.8 L IS USM",
    "lens_id": "61",
    "software": "Adobe Photoshop 24.0",
    "serial_number": "012345678901"
}

Capture Settings

settings = meta.extract()["settings"]

# Returns:
{
    "exposure_time": "1/250",
    "f_number": 2.8,
    "iso": 400,
    "focal_length": 50,
    "focal_length_35mm": 50,
    "exposure_program": "Aperture priority",
    "metering_mode": "Pattern",
    "flash": "No flash",
    "white_balance": "Auto"
}

GPS Data

gps = meta.get_gps()

# Returns:
{
    "latitude": 37.7749,
    "longitude": -122.4194,
    "altitude": 10.5,
    "altitude_ref": "Above sea level",
    "timestamp": "2024-01-15 14:30:00",
    "direction": 180.5,
    "speed": 0,
    "maps_url": "https://maps.google.com/maps?q=37.7749,-122.4194"
}

Timestamps

datetime_info = meta.get_datetime()

# Returns:
{
    "original": "2024-01-15 14:30:00",
    "digitized": "2024-01-15 14:30:00",
    "modified": "2024-01-16 10:00:00",
    "timezone": "+00:00"
}

Image Dimensions

dims = meta.get_dimensions()

# Returns:
{
    "width": 8192,
    "height": 5464,
    "megapixels": 44.8,
    "orientation": "Horizontal",
    "resolution_x": 300,
    "resolution_y": 300,
    "resolution_unit": "inch"
}

Full Output

info = meta.extract()

# Returns:
{
    "file": {
        "name": "IMG_1234.jpg",
        "path": "/photos/IMG_1234.jpg",
        "size": 15234567,
        "format": "JPEG"
    },
    "camera": {
        "make": "Canon",
        "model": "EOS R5",
        "lens": "RF 24-70mm F2.8 L IS USM"
    },
    "settings": {
        "exposure_time": "1/250",
        "f_number": 2.8,
        "iso": 400,
        "focal_length": 50
    },
    "datetime": {
        "original": "2024-01-15 14:30:00"
    },
    "gps": {
        "latitude": 37.7749,
        "longitude": -122.4194
    },
    "dimensions": {
        "width": 8192,
        "height": 5464
    }
}

Privacy Features

Strip Metadata

Remove EXIF data for privacy:

# Strip all metadata
meta.load("original.jpg")
meta.strip_metadata("clean.jpg")

# Keep orientation (prevents rotated images)
meta.strip_metadata("clean.jpg", keep_orientation=True)

Check for Location Data

meta.load("photo.jpg")
if meta.has_location():
    print("Warning: Photo contains GPS coordinates!")

Batch Strip

meta.strip_batch("./originals/", "./cleaned/")

GPS Mapping

Generate Location Map

Create an interactive map from geotagged photos:

meta = ImageMetadata()

# Batch extract with GPS
images = meta.extract_batch("./vacation_photos/")

# Filter to only geotagged images
geotagged = [img for img in images if img.get("gps")]

# Generate map
meta.generate_map(geotagged, "photo_map.html")

The map includes:

  • Markers for each photo location
  • Popup with photo thumbnail and metadata
  • Clustering for dense areas

Batch Processing

Extract from Folder

meta = ImageMetadata()

# All images in folder
results = meta.extract_batch("./photos/")

# Recursive (include subfolders)
results = meta.extract_batch("./photos/", recursive=True)

# Export to CSV
df = pd.DataFrame(results)
df.to_csv("metadata.csv", index=False)

Filter by Criteria

results = meta.extract_batch("./photos/")

# Find high ISO photos
high_iso = [r for r in results if r.get("settings", {}).get("iso", 0) > 3200]

# Find photos from specific camera
canon_photos = [r for r in results if "Canon" in r.get("camera", {}).get("make", "")]

# Find photos with GPS
geotagged = [r for r in results if r.get("gps")]

Example Workflows

Photo Organization

meta = ImageMetadata()
results = meta.extract_batch("./camera_import/")

for photo in results:
    date = photo.get("datetime", {}).get("original", "unknown")
    camera = photo.get("camera", {}).get("model", "unknown")
    print(f"{photo['file']['name']}: {date} - {camera}")

Privacy Audit

meta = ImageMetadata()
results = meta.extract_batch("./to_share/")

risky = []
for photo in results:
    if photo.get("gps"):
        risky.append({
            "file": photo["file"]["name"],
            "location": f"{photo['gps']['latitude']}, {photo['gps']['longitude']}"
        })

if risky:
    print(f"Warning: {len(risky)} photos contain location data!")
    for r in risky:
        print(f"  - {r['file']}: {r['location']}")

Travel Photo Map

meta = ImageMetadata()
results = meta.extract_batch("./trip_photos/", recursive=True)

# Generate interactive map
geotagged = [r for r in results if r.get("gps")]
print(f"Found {len(geotagged)} geotagged photos")

meta.generate_map(geotagged, "trip_map.html")

Supported Formats

  • JPEG/JPG (full EXIF support)
  • TIFF (full EXIF support)
  • PNG (limited metadata)
  • HEIC/HEIF (iOS photos)
  • WebP (limited metadata)
  • RAW formats (CR2, NEF, ARW, etc.)

Dependencies

  • pillow>=10.0.0
  • folium>=0.14.0 (for map generation)