| name | image-editor |
| description | Resize, crop, rotate, convert, watermark, and optimize images using Pillow (Python), Sharp (Node.js), or ImageMagick. Triggers on requests like "resize these images to 800px wide", "convert PNG to JPEG", "add watermark", "strip EXIF data", or "create thumbnails". |
| license | Apache-2.0 |
| compatibility | {"clients":["openai-codex","gemini-cli","opencode"]} |
| metadata | {"owner":"codex","domain":"image-editor","maturity":"draft","risk":"low","tags":["image","pillow","sharp","imagemagick","resize","format-conversion"]} |
Purpose
Manipulate images programmatically using Pillow (Python), Sharp (Node.js), or ImageMagick (CLI) — resize, crop, rotate, convert between formats, add watermarks, manage EXIF metadata, optimize file size, and batch-process image collections. This skill covers the full lifecycle of image transformation in automated pipelines.
When to use
- Resize, crop, or rotate images for web display, thumbnails, or print
- Convert between image formats (PNG→JPEG, WEBP→PNG, SVG rasterization, HEIC→JPEG)
- Add watermarks, overlays, or text annotations to images
- Strip EXIF data for privacy, preserve it for archival, or extract metadata
- Optimize image file size for web delivery or storage constraints
- Batch process multiple images with consistent transformations
- Generate thumbnails for galleries, catalogs, or document previews
When NOT to use
- AI-powered image generation or editing (Stable Diffusion, DALL-E, Midjourney) — use creative generation skills instead
- Design mockup creation (Figma, Sketch, UI/UX design tools) — this skill manipulates pixels, not creates designs
- Image classification, object detection, or computer vision analysis — use ML/AI vision skills
- OCR on images embedded in PDFs — use
image-heavy-pdfs instead
- Creating PDF documents containing images — use
pdf-generation instead
- Video processing or frame extraction — use video-specific tools
- 3D image manipulation or depth map generation — use specialized 3D tools
Procedure
-
Identify the input images
- Determine source: file path(s), URL(s), or byte stream(s)
- Check format: JPEG, PNG, GIF, WEBP, TIFF, BMP, SVG, HEIC, AVIF
- Record original dimensions, color space, and file size
- For URLs: download to temporary location first
-
Select the processing library based on runtime
- Python: Use Pillow (
pip install Pillow). Best for most operations.
- Node.js: Use Sharp (
npm install sharp). Fastest for web-optimized outputs.
- CLI/shell: Use ImageMagick (
convert, mogrify). Best for batch scripts.
-
Load the image with validation
- Pillow:
from PIL import Image; img = Image.open(path); img.verify() then reload
- Sharp:
const img = sharp(path); const metadata = await img.metadata();
- ImageMagick:
identify -verbose input.jpg to validate before processing
- Check: dimensions must be > 0, no truncated files, supported codec
-
Apply resize operations
- Calculate target dimensions maintaining aspect ratio:
new_height = int(target_width * original_height / original_width)
- Pillow:
img.resize((width, height), Image.LANCZOS)
- Sharp:
img.resize(width, height, { fit: 'inside', withoutEnlargement: true })
- If target > original: warn about upscaling quality loss, prefer original
- Never distort aspect ratio unless explicitly requested
-
Apply crop operations
- Define crop box as
(left, top, right, bottom) in pixels
- Validate:
right <= img.width and bottom <= img.height
- Center crop formula:
left = (width - crop_width) // 2, top = (height - crop_height) // 2
- Pillow:
img.crop((left, top, right, bottom))
- Sharp:
img.extract({ left, top, width: right-left, height: bottom-top })
-
Apply rotation
Output contract
The following outputs must be produced:
-
Processed image file(s)
- Correct format as specified (JPEG, PNG, WEBP, etc.)
- Exact target dimensions (within 1 pixel tolerance)
- Quality level as specified or using defaults from procedure
-
Processing log (text or JSON)
- For each image: input path, output path
- Input dimensions (width × height) and output dimensions
- Input file size and output file size (bytes)
- Operations applied (resize, crop, rotate, format conversion, watermark, EXIF strip)
-
Error report
- List of failed images with specific error reason:
- "Corrupt/truncated file"
- "Unsupported format: {format}"
- "Missing input file: {path}"
- "Permission denied: {path}"
- "Out of memory: image too large ({dimensions})"
- Suggested recovery action for each error type
-
Metadata summary
- EXIF status for each output:
stripped, preserved, or modified
- If preserved: note which fields were retained (dimensions, timestamp, camera)
- GPS data status: explicitly note if removed for privacy
References
Next steps
- For OCR on images in PDFs: use
image-heavy-pdfs
- To assemble images into a PDF document: use
pdf-generation
- To capture screenshots for later processing: use
screenshot
- For AI-powered image generation: use appropriate creative generation skills
- For image analysis and computer vision: use ML/AI vision skills
Failure handling
Corrupt or unreadable input
- Detection:
Image.open(path) raises OSError, SyntaxError, or UnidentifiedImageError
- Action: Log error with specific type ("truncated file at byte {offset}", "unsupported codec: {codec}")
- Recovery: Skip to next image in batch; for single image, request alternative source file
- Prevention: Use
img.verify() before processing
Processing library not installed
- Detection:
ModuleNotFoundError for Pillow, command not found for ImageMagick
- Action: Output exact install command and halt immediately:
- Pillow:
pip install Pillow
- Sharp:
npm install sharp
- ImageMagick:
sudo apt-get install imagemagick (Ubuntu) or brew install imagemagick (macOS)
- Recovery: Install dependency and retry
Output path not writable
- Detection:
PermissionError or OSError: [Errno 13] on save
- Action:
- Attempt writing to system temp directory:
/tmp/{filename} (Unix) or %TEMP%\{filename} (Windows)
- Report the original path and permission issue to user
- Do not proceed with batch until permission issue resolved or alternative path provided
Insufficient memory for large images
- Detection:
MemoryError or system swap exhaustion; images > 100 megapixels (e.g., 10000 × 10000)
- Action:
- For Pillow: use
ImageFile.LOAD_TRUNCATED_IMAGES = True and process in tiles
- Reduce in stages: resize to 50% first, then to target
- Use Sharp's streaming mode which processes in chunks
- For ImageMagick: use
-limit memory 256MiB to constrain memory
- Recovery: Log intermediate stage files, clean up on completion
Unexpectedly large output file
- Detection: Output size > 2× input size for same format, or > 500KB for thumbnail
- Action:
- Retry with lower quality settings (JPEG: 85→75→65)
- For PNG: quantize to 256 colors
img.quantize(colors=256)
- Enable additional compression flags
- Log size comparison: "Retry reduced size from {size1} to {size2} bytes"
Aspect ratio distortion
- Detection: Output dimensions don't match calculated proportional values
- Action:
- Reject the operation before save
- Log: "Aspect ratio mismatch: calculated {calc_width}×{calc_height}, got {actual_width}×{actual_height}"
- Recompute with correct ratio:
target_height = int(target_width * orig_height / orig_width)
Alpha channel lost in PNG→JPEG conversion
- Detection: Output has black areas where transparency existed
- Action:
- Before conversion, composite onto white (or user-specified background):
background = Image.new('RGB', img.size, (255, 255, 255))
background.paste(img, mask=img.split()[3])
- Save the composited RGB image
EXIF data preservation failure
- Detection: Output EXIF differs from input despite preservation request
- Action:
- Extract EXIF before any transforms:
exif = img.info.get('exif')
- Re-apply to final image before save:
img.save(output, exif=exif)
- Verify with:
Image.open(output)._getexif()
Batch processing partial failure
- Detection: Some images in batch fail while others succeed
- Action:
- Continue processing remaining images (do not halt entire batch)
- Maintain list of failed files with error reasons
- At completion: report success count, failure count, and specific errors
- Allow user to retry only failed files