Embed Image Metadata Using an LLM
A Python tool, run from the command line or a small Flask web UI, that uses an LLM to generate accessibility alt text plus a marketplace-style title, description, and keywords, writing all of it straight into the image file's XMP and IPTC Core fields.
- Python
- llm CLI
- exiftool
- Flask
- Claude
- Ollama
Source on GitHubStarted March 2026MIT
Most alt-text generators hand you a caption and leave it up to you to store it somewhere. This tool goes further: a vision model writes accessibility alt text into the XMP-iptcCore:AltTextAccessibility field by default, and with --iptc it also writes a marketplace-style Title, Description, and Keywords list into the IPTC Core fields. Everything is embedded in the image itself, so it travels with the file into Lightroom, Bridge, a CMS, or a static site build.
There are two ways to run it: a command-line script driven by flags, or a small web app for bulk uploads through a browser.
It was inspired by Dries Buytaert’s “Image Caption” script. Where his version PATCHes captions to a remote API and matches them back to images by album and filename, this one uses exiftool to write directly into the file, no server-side store required.
Method 1: command line
update-images.py takes a folder path and a set of flags:
- Scans a folder of images (
.jpg,.png,.gif,.heic,.webp), sends each one to a vision model through thellmCLI, and writes the result into the XMP alt-text field. Images that already have alt text are skipped unless you pass--force. - Models and prompts live in
models.yaml. The default isclaude-sonnet-4-5, but the same config covers GPT and Pixtral models as well as a dozen local vision models via Ollama, so the whole pipeline can run offline. - An optional
--contextstring (“Cherry blossoms at Japanese Friendship Garden”) nudges the model toward a more accurate caption. --iptcgoes further and also generates a marketplace-style Title, a 3–4 sentence Description, and a long Keywords list, writing them into the IPTC Core fields Adobe Bridge reads.replacements.yamlis a small dictionary of phrases that are always rewritten in the output (mid-century→midcentury), matched case-insensitively with the original capitalization preserved.
Method 2: web interface
webapp.py wraps the same image_processor.py pipeline in a Flask app for bulk uploads through a browser, no flags to remember: pick a model, add context, choose a mode, watch progress live, then download a zip of the tagged images. It’s a deliberately single-user local tool with no auth and in-memory job tracking.
Unit tests cover caption cleaning and the replacement rules.





