A small Python script that walks a folder, sends each image with your prompt to Gemini or OpenAI, and saves the result hyper-organized: per-run directory, manifest log, side-by-side HTML comparison. Run with --provider both to QA the two engines on the same input.
Clone the repo and install the Python deps. The script lives in scripts/.
# 1. Clone the toolshed git clone https://github.com/banditmediagroup/toolshed.git cd toolshed/scripts # 2. Install deps (use a venv if you don't already) python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt # 3. Set your keys cp .env.example .env # Edit .env, paste in GEMINI_API_KEY and/or OPENAI_API_KEY
The script auto-loads .env if python-dotenv is installed (it's in requirements.txt). You can also export the env vars in your shell instead.
The basics:
# Default: Gemini 2.5 Flash Image python image-batch-prompt.py -i ./photos -p "watercolor painting style" # OpenAI gpt-image-1 python image-batch-prompt.py -i ./photos -p "watercolor painting style" --provider openai # Both, side-by-side, for QA python image-batch-prompt.py -i ./photos -p "watercolor painting style" --provider both # Long prompts: load from a file python image-batch-prompt.py -i ./photos -p @prompts/watercolor.txt # Test the wiring without burning API credits python image-batch-prompt.py -i ./photos -p "..." --dry-run # Override defaults python image-batch-prompt.py -i ./photos -p "..." \ --name spring_campaign \ --limit 5 \ --size 1536x1024 \ --openai-model gpt-image-1
Every run creates a timestamped (or named) directory. Per-provider subdirs hold the results, manifest.json captures the full run state, and an auto-generated index.html shows side-by-side comparison.
output/ └── 20260428_223015/ # run name (timestamp by default) ├── manifest.json # full run config + per-image status ├── index.html # side-by-side HTML, open in browser ├── gemini/ │ ├── photo_001.png │ ├── photo_002.png │ └── ... └── openai/ # only if --provider openai or both ├── photo_001.png └── ...
The manifest is updated after each image, so a crash mid-run preserves everything completed so far. Re-run with the same --name to resume (existing outputs skipped by default; pass --no-skip to reprocess).
| Gemini 2.5 Flash Image | OpenAI gpt-image-1 | |
|---|---|---|
| SDK | google-genai | openai |
| API endpoint | generate_content (multimodal) | images.edit |
| Output sizes | Auto, near 1024x1024 | 1024x1024, 1536x1024, 1024x1536, auto |
| Strength | Strong character/identity preservation, fast, cheap | Better at typography, complex compositions, photorealism |
| Weakness | Occasional refusals on photo-of-people input | Slower, pricier, content policies stricter |
| Pricing (approx) | ~$0.039 / image | ~$0.040 std, ~$0.080 high quality |
-i, --input | Input folder (required). Picks up .jpg, .jpeg, .png, .webp. |
-p, --prompt | Prompt text (required). Prefix with @ to load from a file. |
--provider | gemini (default), openai, or both for side-by-side. |
-o, --output | Output base directory. Default ./output. |
--name | Run name. Default: timestamp YYYYMMDD_HHMMSS. |
--limit N | Process at most N images. Useful for testing on a subset before a big batch. |
--size | OpenAI output size. Default 1024x1024. |
--gemini-model / --openai-model | Override the default model for either provider. |
--dry-run | List inputs without making API calls. Sanity check. |
--no-skip | Reprocess images even when output exists. Default is to skip. |
--no-html-index | Skip the generated index.html. |
--provider both on a small --limit 5 set first, open the generated index.html to compare side-by-side, pick the winner, then run the full batch through that single provider. Catches "this prompt works great in Gemini but mangles type in OpenAI" before you spend the credits.--name picks up exactly where it left off because existing output files are skipped by default. No double-charging.