Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add dkyazzentwatwa/chatgpt-skills --skill "image-metadata-tool"
Install specific skill from multi-skill repository
# Description
Extract EXIF metadata from images including GPS coordinates, camera settings, and timestamps. Map photo locations and strip metadata for privacy.
# 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)
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.