Image Batch Processing skill for Codex

Resize a thousand photos without one sideways. Codex inspects formats, orientation tags and colour profiles before converting, then runs ImageMagick, sharp or Pillow into a separate output folder with auto-orient, sRGB conversion, shrink-only resizing and metadata stripping. HEIC, CMYK and transparency get explicit handling, batches run in parallel, and counts, dimensions and GPS tags are checked afterwards.

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- image-batch-processing

Installs to ~/.agents/skills/image-batch-processing/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $image-batch-processing, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Batch-processes folders of images with ImageMagick 7, sharp (Node) or Pillow (Python) - resize, crop, convert to WebP or AVIF, auto-orient from EXIF, strip or keep metadata, convert colour profiles to sRGB, watermark, and generate thumbnails - in parallel, into a separate output folder, with before/after checks. Use when asked to resize or compress a folder of photos, convert HEIC or PNG to JPEG or WebP, make thumbnails, add a watermark to many images, or fix sideways or washed-out images after a batch job.

Use it when

  • A folder of images needs resizing, compression or format conversion
  • Thumbnails must be generated for many images at once
  • Batch outputs came out rotated, washed out or with black backgrounds
  • Photos must have GPS and camera metadata removed before publishing

Use something else when

How to check the result

Input and output counts match, identify shows every output within the size limit and in sRGB, exiftool finds no GPS tags, and five outputs look right next to their originals.

The mistake it prevents

Stripping metadata before converting to sRGB, so Display P3 or Adobe RGB photos come out dull and off-colour.

~/.agents/skills/image-batch-processing/SKILL.md

--- name: image-batch-processing description: Batch-processes folders of images with ImageMagick 7, sharp (Node) or Pillow (Python) - resize, crop, convert to WebP or AVIF, auto-orient from EXIF,… license: MIT ---

A batch is done right when originals are untouched, every output is upright, sRGB and the requested size and format, file names map one to one back to inputs, private metadata (GPS) is gone unless the user wants it, and a count plus a visual spot check prove it.

Inspect before converting

magick identify -format '%f %wx%h %m %[colorspace] q=%Q orient=%[EXIF:Orientation]\n' in/* | head
exiftool -T -FileName -ImageSize -Orientation -ICC_Profile:ProfileDescription -GPSPosition in/ | head
find in -type f | sed 's/.*\.//' | sort | uniq -c

Note: format mix (HEIC from iPhones, CMYK JPEGs from print, 16-bit PNG/TIFF), orientation tags, embedded profiles (Adobe RGB, Display P3), animated GIF/WebP, and transparency. Each changes the pipeline.

Tool choice

SituationDefault
One-off shell batch, any formatImageMagick 7 (magick, magick mogrify)
Node project, build step, high volumesharp (libvips; fast, low memory)
Python project or per-image logicPillow (+ pillow-heif for HEIC)
HEIC on macOS with nothing installedsips -s format jpeg in.heic --out out.jpg
Web delivery (srcset, CDN) decisions$responsive-images and $image-optimization

Check versions: magick -version (v6 installs only have convert/mogrify; options mostly match but HEIC/AVIF delegates may be missing). Installing any of these needs network and possibly elevated access; ask in a Codex sandbox.

Defaults that are almost always right

  • Output to a new folder; never mogrify in place on the only copy.
  • Auto-orient first, then strip the orientation tag (otherwise viewers rotate twice).
  • Convert to sRGB before stripping profiles; stripping a P3 or Adobe RGB profile without converting makes colours dull or oversaturated.
  • Resize only down (> in ImageMagick, withoutEnlargement in sharp, thumbnail in Pillow).
  • Strip EXIF/GPS for anything published; keep copyright fields if the user asks.
  • Quality starting points: JPEG 80 to 85, WebP 75 to 82, AVIF 45 to 60 (AVIF scales differently).

ImageMagick 7

mkdir -p out
# Resize to fit 1600x1600 (shrink only), sRGB, strip metadata, WebP
magick mogrify -path out -auto-orient -colorspace sRGB -resize '1600x1600>' \
  -strip -quality 80 -format webp in/*.jpg

# Square thumbnails, centre crop
magick mogrify -path thumbs -auto-orient -thumbnail '400x400^' -gravity center \
  -extent 400x400 -strip -quality 82 -format jpg in/*.jpg

# Watermark bottom-right at 30 percent opacity, scaled to 15 percent of image width
for f in in/*.jpg; do
  w=$(magick identify -format '%w' "$f")
  magick "$f" -auto-orient \( logo.png -resize "$((w * 15 / 100))x" -alpha set -channel A \
    -evaluate multiply 0.3 +channel \) -gravity southeast -geometry +24+24 -composite \
    -quality 85 "out/$(basename "$f")"
done

For profile-accurate conversion use -profile sRGB.icc with a real ICC file instead of -colorspace sRGB. For large batches, parallelise per file:

find in -name '*.jpg' -print0 | xargs -0 -P 4 -I{} sh -c \
  'magick "$1" -auto-orient -resize "1600x1600>" -strip -quality 80 "out/$(basename "${1%.*}").webp"' _ {}

GNU parallel works too. Cap memory with -limit memory 1GiB when processing huge scans.

sharp (Node)

import sharp from 'sharp';
import { readdir, mkdir } from 'node:fs/promises';
import path from 'node:path';

const IN = 'in', OUT = 'out';
await mkdir(OUT, { recursive: true });
const files = (await readdir(IN)).filter((f) => /\.(jpe?g|png|tiff?)$/i.test(f));
const limit = 4;                                  // concurrency
const failures = [];
for (let i = 0; i < files.length; i += limit) {
  await Promise.all(files.slice(i, i + limit).map(async (f) => {
    const dst = path.join(OUT, f.replace(/\.\w+$/, '.webp'));
    try {
      await sharp(path.join(IN, f))
        .rotate()                                 // no args = auto-orient from EXIF
        .resize({ width: 1600, height: 1600, fit: 'inside', withoutEnlargement: true })
        .webp({ quality: 80 })
        .toFile(dst);
    } catch (err) { failures.push([f, err.message]); }
  }));
}
console.log(`${files.length - failures.length}/${files.length} converted`, failures);

sharp drops metadata and converts to sRGB by default; call .keepMetadata() or .withMetadata() only when metadata must survive. Prebuilt sharp binaries do not decode HEIC (HEVC licensing); convert HEIC with ImageMagick/libheif or sips first. Animated inputs need sharp(file, { animated: true }).

Pillow (Python)

from pathlib import Path
from PIL import Image, ImageOps

src, dst = Path("in"), Path("out"); dst.mkdir(exist_ok=True)
for p in sorted(src.glob("*")):
    if p.suffix.lower() not in {".jpg", ".jpeg", ".png", ".tif", ".tiff"}:
        continue
    try:
        with Image.open(p) as im:
            im = ImageOps.exif_transpose(im)        # apply orientation
            if im.mode in ("CMYK", "P", "LA", "I;16"):
                im = im.convert("RGBA" if "A" in im.getbands() else "RGB")
            im.thumbnail((1600, 1600), Image.Resampling.LANCZOS)   # shrink only, keeps ratio
            im.save(dst / (p.stem + ".webp"), "WEBP", quality=80, method=6)
    except Exception as exc:
        print(f"FAILED {p.name}: {exc}")

Pillow’s convert("RGB") on CMYK ignores the ICC profile; for print-sourced CMYK files use PIL.ImageCms with the embedded profile or ImageMagick -profile. HEIC: pip install pillow-heif then pillow_heif.register_heif_opener(). AVIF support depends on the Pillow build; check PIL.features.check("avif") before relying on it.

Gotchas

  • -resize 1600x1600 without > enlarges small images and blurs them.
  • PNG with transparency converted to JPEG gets a black background; flatten onto white (-background white -alpha remove) or keep PNG/WebP.
  • Re-encoding JPEGs at quality 95+ makes files bigger with no visible gain; re-encoding at any quality loses detail, so do it once from the original.
  • mogrify -format webp writes name.webp next to inputs unless -path is set.
  • Filenames with spaces or leading dashes break naive loops; quote, use -- or -print0.
  • Same stem with different extensions (a.jpg, a.png) collide when both become a.webp.
  • Animated GIFs processed frame by frame need -coalesce before resizing in ImageMagick.

Verification

ls in | wc -l; ls out | wc -l                                     # counts match
magick identify -format '%f %wx%h %[colorspace]\n' out/* | awk '{print $2}' | sort | uniq -c | head
exiftool -if '$GPSPosition' -p '$FileName' out/ 2>/dev/null | head   # expect nothing
du -sh in out
  • Open 5 outputs side by side with their originals: upright, colours match, no halos.
  • Largest dimension never exceeds the limit; no output is bigger than its input unless the format change explains it.
  • Failures list is empty or each failure is explained (corrupt file, unsupported format).

Report

**Input:** in/ 1,240 files (1,118 JPEG, 97 HEIC, 25 PNG), 9.8 GB
**Output:** out/ 1,240 WebP, max 1600 px, sRGB, metadata stripped, 412 MB
**Command:** magick mogrify -path out -auto-orient -colorspace sRGB -resize '1600x1600>' -strip -quality 80 -format webp
**Checks:** counts match, no GPS tags, 5 visual spot checks OK
**Failed:** 2 (truncated JPEGs: IMG_2231.jpg, IMG_2240.jpg)

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Image Batch Processing"
  short_description: "Resize a thousand photos without one sideways"
  default_prompt: "$image-batch-processing Resize every photo in this folder to max 1600 px and convert them to WebP"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem