---
name: AI Image Creator
slug: ai-image-creator
category: AI Engineering
description: AI Image Creator generates, edits, and analyzes images with AI models via OpenRouter and Cloudflare AI Gateway. Use it when a user asks to create a PNG, design an icon/logo/banner, describe an image, or analyze a video into a text prompt.
github: "https://github.com/centminmod/my-claude-code-setup/tree/master/.claude/skills/ai-image-creator"
language: Python
stars: 2587
forks: 246
install: "npx degit https://github.com/centminmod/my-claude-code-setup/tree/master/.claude/skills/ai-image-creator ~/.claude/skills/ai-image-creator"
installs_to: ~/.claude/skills/ai-image-creator
source_path: .claude/skills/ai-image-creator/SKILL.md
collection_size: 7
category_size: 2451
collection_url: "https://dirskills.com/collections/centminmod/my-claude-code-setup"
added: 2026-08-18T06:57:30.027Z
last_synced: 2026-08-18T06:57:30.027Z
canonical_url: "https://dirskills.com/skills/ai-image-creator"
---

# AI Image Creator

AI Image Creator generates, edits, and analyzes images with AI models via OpenRouter and Cloudflare AI Gateway. Use it when a user asks to create a PNG, design an icon/logo/banner, describe an image, or analyze a video into a text prompt.

**Install:**

```bash
npx degit https://github.com/centminmod/my-claude-code-setup/tree/master/.claude/skills/ai-image-creator ~/.claude/skills/ai-image-creator
```

## README

# AI Image Creator

Generate PNG images via multiple AI models, routed through Cloudflare AI Gateway BYOK or directly via OpenRouter/Google AI Studio.

## Model Selection

When the user mentions a model keyword in their image request, use the corresponding `--model` flag:

| Keyword | Model | Use When User Says |
|---------|-------|--------------------|
| `gemini` | [Google Gemini 3.1 Flash](https://openrouter.ai/google/gemini-3.1-flash-image) (default) | "gemini", "generate an image" (no model specified) |
| `geminipro` | [Google Gemini 3 Pro](https://openrouter.ai/google/gemini-3-pro-image) | "geminipro", "gemini pro", "use gemini pro" |
| `riverflow` | [Sourceful Riverflow v2 Pro](https://openrouter.ai/sourceful/riverflow-v2-pro) | "riverflow", "use riverflow" |
| `flux2` | [FLUX.2 Max](https://openrouter.ai/black-forest-labs/flux.2-max) | "flux2", "flux", "use flux" |
| `seedream` | [ByteDance SeedDream 4.5](https://openrouter.ai/bytedance-seed/seedream-4.5) | "seedream", "use seedream" |
| `gpt5` | [OpenAI GPT-5 Image](https://openrouter.ai/openai/gpt-5-image) | "gpt5", "gpt5 image", "use gpt5" |
| `gpt5.4` | [OpenAI GPT-5.4 Image 2](https://openrouter.ai/openai/gpt-5.4-image-2) | "gpt5.4", "gpt-5.4 image", "use gpt5.4" |

## Instructions

> **Routing check:** If the user asks to **describe, analyze, or explain an existing image** (not generate a new one), skip directly to the **Image Analysis (`--analyze`)** section below. No prompt enhancement or output path needed.
>
> **Video routing:** If the user asks to **describe, analyze, or explain a video** (or wants a text description of a clip to seed/extend a video prompt), skip directly to the **Video Analysis (`--analyze-video`)** section below.

### Step 1: Write Prompt

For long or complex prompts (recommended), write to `${CLAUDE_SKILL_DIR}/tmp/prompt.txt` using the Write tool:

```
Write prompt text to ${CLAUDE_SKILL_DIR}/tmp/prompt.txt
```

For short prompts (under 200 chars, no special characters), pass inline via `--prompt`.

**CRITICAL — Prompt Quality Tips:**
- Be detailed and descriptive. Include style, colors, composition, background, and intended use.
- Good: "A flat-design globe icon with vertical timezone band lines in blue and teal, white background, clean vector style, suitable for a web app at 512x512 pixels"
- Bad: "globe icon"
- Specify "transparent background" or "white background" explicitly.
- For icons, mention the target size (e.g., "512x512", "favicon at 32x32").
- For photos, describe lighting, camera angle, and mood.

### Step 1.5: Prompt Enhancement (Optional — Progressive Disclosure)

Professional prompt patterns are available in 3 reference files. These are **not loaded by default** — only read them when the user's request matches a category or they explicitly ask for enhancement.

**Category Detection** — Match the user's request to a category:

| If request mentions... | Category | Also read |
|----------------------|----------|-----------|
| "product shot", "product photo", "hero image" | `product_hero` | `prompt-core.md` + `prompt-categories.md` § product_hero |
| "lifestyle", "in-use", "in context" | `lifestyle` | `prompt-core.md` + `prompt-categories.md` § lifestyle |
| "instagram", "social media", "tiktok", "pinterest" | `social_media` | `prompt-core.md` + `prompt-platforms.md` + `prompt-categories.md` § social_media |
| "banner", "ad", "email header" | `marketing_banner` | `prompt-core.md` + `prompt-platforms.md` + `prompt-categories.md` § marketing_banner. **Routing hint:** If user has an existing logo and wants multiple standard sizes → use composite mode instead (see `## Composite Banners`). |
| "website", "app", "logo", "ad format", "leaderboard", "skyscraper" | `web_app` | `prompt-core.md` + `prompt-platforms.md` + `prompt-categories.md` § web_app. **Routing hint:** For "logo banners" or "OG images with my logo" where user has existing logo → use `composite-banners.py`. For "design me a new logo" → use `generate-image.py`. |
| "brand kit", "logo banners", "banner sizes", "IAB sizes", "consistent banners" + user has existing logo | `composite` | Read `references/composite-reference.md`, use `composite-banners.py` |
| "icon", "favicon", "app icon" | `icon_logo` | `prompt-core.md` + `prompt-categories.md` § icon_logo |
| "mascot", "character", "illustration", "artwork" | `illustration` | `prompt-core.md` + `prompt-categories.md` § illustration |
| "food", "drink", "recipe", "restaurant" | `food_drink` | `prompt-core.md` + `prompt-categories.md` § food_drink |
| "building", "interior", "room", "architecture" | `architecture` | `prompt-core.md` + `prompt-categories.md` § architecture |
| "chart", "infographic", "data", "diagram" | `infographic` | `prompt-core.md` + `prompt-categories.md` § infographic |
| "t-shirt", "mug design", "poster", "POD", "print-on-demand" | `pod_design` | `prompt-core.md` + `prompt-platforms.md` + `prompt-categories.md` § pod_design |
| "consistent character", "same character/product across frames", "comic strip", "storyboard", "frame set", "start and last frame", "panels", "before/after" | `frame_consistency` | Read `references/consistency-presets.md` — keep people/objects/scenes consistent across a SET of frames (for video first/last frames or stitched comic strips) |
| "describe", "analyze", "what's in this image", "explain image" | `analyze` | Handled by the top **Routing check** — read `references/analyze-reference.md` only for advanced/structured analysis patterns |
| No match / simple request | — | Skip patterns, generate directly |

**When to skip enhancement:**
- User's prompt is already detailed (150+ words with camera/lighting/composition specifics)
- Simple/direct requests ("generate a blue circle on white background")
- User says "no pattern" or provides a fully formed prompt

**When to apply:**
- User says "use product_hero pattern" or "apply social_media pattern" (explicit)
- Request clearly matches a category above (auto-detect)
- User asks for "enhanced prompt" or "professional quality"

**Reference files** (in `references/` directory):
- `prompt-core.md` — Foundational rules: narrative prompting, camera/lens/lighting specs, text rendering rules, model recommendations
- `prompt-platforms.md` — Social media ratios, IAB ad sizes, web dimensions, POD specs — all mapped to `-a`/`-s` flags
- `prompt-categories.md` — 11 category formulas with templates and complete example prompts

### Step 2: Run Generation Script

```bash
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "OUTPUT_PATH" \
  [--provider openrouter|google] \
  [-a "16:9"] \
  [-s "2K"] \
  [-m "model-id"] \
  [-r "ref-image.png"] \
  [-t]
```

With a specific model:
```bash
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "OUTPUT_PATH" \
  -m riverflow \
  -p "A serene mountain lake at sunset"
```

With transparent background (requires ffmpeg + imagemagick):
```bash
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "mascot.png" \
  -t \
  -p "A friendly robot mascot character"
```

With reference image for editing/style transfer (multimodal models only):
```bash
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "edited.png" \
  -r "original.png" \
  -p "Change the background to a sunset scene"
```

Or with inline prompt (default model):
```bash
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "OUTPUT_PATH" \
  -p "A simple blue circle on white background"
```

### Step 3: Clean Up (if temp file used)

```bash
rm -f ${CLAUDE_SKILL_DIR}/tmp/prompt.txt
```

### Step 4: Verify Output

```bash
file OUTPUT_PATH
```

Confirm it shows "PNG image data" and report the file path and size to the user.

### Step 5: Post-Processing (optional)

If the user needs resizing, format conversion, or other manipulation, first detect available image tools, then use them. See **Image Tools** section below.

## Parameters

| Argument | Short | Required | Default | Description |
|----------|-------|----------|---------|-------------|
| `--output` | `-o` | Yes | -- | Output file path (parent dirs auto-created) |
| `--prompt` | `-p` | No | -- | Inline prompt text |
| `--prompt-file` | -- | No | `../tmp/prompt.txt` | Path to prompt file |
| `--provider` | -- | No | `openrouter` | `openrouter` or `google` |
| `--aspect-ratio` | `-a` | No | model default | OpenRouter only: `1:1`, `16:9`, `9:16`, `3:2`, `2:3`, `4:3`, `3:4`, `4:5`, `5:4`, `21:9` |
| `--image-size` | `-s` | No | model default | OpenRouter only: `1K`, `2K`, `4K`. `0.5K` is accepted **only** on the Gemini 3.1 Flash preview build (`-m google/gemini-3.1-flash-image-preview-20260226`); every selectable keyword rejects it |
| `--model` | `-m` | No | `gemini` | Model keyword (`gemini`, `geminipro`, `riverflow`, `flux2`, `seedream`, `gpt5`, `gpt5.4`) or full model ID |
| `--ref` | `-r` | No | -- | Reference image file (repeatable). For editing/style transfer. Multimodal models only (gemini, geminipro, gpt5, gpt5.4) |
| `--analyze` | -- | No | -- | Analyze/describe a reference image (text-only output, no image generated). Requires `-r`. Multimodal models only |
| `--analyze-video` | -- | No | -- | Analyze/describe a video. Pass the video via `-r` (local file or URL). OpenRouter only. Choose a model/preset with `-m` (default `gemini3.5-flash`). Returns **structured JSON** by default |
| `--prose` | -- | No | -- | (`--analyze-video` only) Return free-text prose instead of the default structured JSON |
| `--contact-sheet` | -- | No | -- | (`--analyze-video`, local file only) Extract evenly-spaced keyframes with ffmpeg and save a labeled contact-sheet image to `PATH` — a human ground-truth reference. Skipped for URL sources / if ffmpeg is missing |
| `--verify` | -- | No | -- | (`--analyze-video`, local file only) Second pass that checks the analysis against extracted frames (no video re-sent) and classifies each claim `supported`/`contradicted`/`not_visible`. Adds a `verification` object. Costs one extra model call |
| `--transparent` | `-t` | No | -- | Generate with transparent background. Requires ffmpeg + imagemagick |
| `--costs` | -- | No | -- | Display generation/cost history for this project and exit |
| `--list-models` | -- | No | -- | List available model keywords and exit |

## Environment Variables

| Variable | Required For | Description |
|----------|-------------|-------------|
| `AI_IMG_CREATOR_CF_ACCOUNT_ID` | Gateway mode | Cloudflare account ID |
| `AI_IMG_CREATOR_CF_GATEWAY_ID` | Gateway mode | AI Gateway name |
| `AI_IMG_CREATOR_CF_TOKEN` | Gateway mode | Gateway auth token |
| `AI_IMG_CREATOR_OPENROUTER_KEY` | Direct OpenRouter | OpenRouter API key (`sk-or-...`) |
| `AI_IMG_CREATOR_GEMINI_KEY` | Direct Google | Google AI Studio API key |

Gateway mode activates when all 3 `CF_*` vars are set. Falls back to direct mode if gateway fails.

For first-time setup, see `references/setup-guide.md`.

## Transparent Mode (`-t`)

Generates images with transparent backgrounds using a 3-step pipeline:

1. **Green screen generation** — Prompt is augmented to place subject on solid #00FF00 green
2. **FFmpeg chroma key** — Removes green background + green fringe from edges
3. **ImageMagick auto-crop** — Trims transparent padding

**Requirements:** `brew install ffmpeg imagemagick`

**Use cases:** Game sprites, icons, logos, mascots, marketing assets with transparency.

```bash
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "sprite.png" -t -p "A pixel art treasure chest"
```

## Reference Images (`-r`)

Send existing images alongside text prompts for editing, style transfer, or guided generation. Supports multiple references. **Multimodal models only** (gemini, geminipro, gpt5, gpt5.4) — image-only models (riverflow, flux2, seedream) will error.

```bash
# Edit an existing image
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "edited.png" -r "photo.png" -p "Make the background white"

# Style transfer with multiple references
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  -o "combined.png" -r "style1.png" -r "content.png" -p "Apply the style of the first image to the second"
```

Supported formats: PNG, JPEG, WebP, GIF.

## Image Analysis (`--analyze`)

Describe, analyze, or explain existing images using multimodal AI vision. Returns text-only output (no image generated). **Multimodal models only** (gemini, geminipro, gpt5, gpt5.4).

No `-o` output path needed. No prompt enhancement needed. The script outputs JSON to stdout with the model's analysis in the `analysis` field.

```bash
# Analyze with default prompt (describes subject, style, colors, composition, mood, text)
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze -r "photo.png"

# Analyze with custom prompt
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze -r "photo.png" -p "Describe this image in plain text and also in JSON structured output"

# Analyze with a specific model
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze -r "photo.png" -m gpt5 -p "What text is visible in this image?"

# Analyze multiple images together
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze -r "before.png" -r "after.png" -p "Compare these two images and describe the differences"
```

**JSON output format:**

```json
{"ok": true, "analyze": true, "analysis": "<model text>", "provider": "openrouter", "model": "...", "mode": "gateway", "elapsed_seconds": 3.2, "ref_images": 1}
```

**Incompatible flags:** `--analyze` cannot be combined with `-t`, `-a`, or `-s`. (`-o` is accepted but ignored in analyze mode, which returns text only.)

For advanced analysis prompt patterns (structured output, comparison, targeted analysis), read `references/analyze-reference.md`.

## Video Analysis (`--analyze-video`)

Describe or analyze a **video** using OpenRouter video-input LLMs (no image generated). Use this to turn an existing clip into a description you can feed back as a prompt to **generate or extend** a video (e.g. with the `ai-video-creator` skill).

**Structured JSON is the default.** All 15 video models support strict structured outputs (`response_format` json_schema, verified), so by default `analysis` is a **structured object** with these fields: `summary`, `setting`, `subjects[]` (each with `role`/`appearance`/`confidence`), `shot_timeline[]` (`timestamp`/`action`/`camera`), `camera_techniques[]`, `editing_stylization[]`, `lighting`, `color_palette[]`, `mood`, `uncertain_details[]`, and a distilled `video_generation_prompt`. The `editing_stylization` and `uncertain_details` fields specifically counter the two main failure modes (missed freeze-frame/black-and-white stylization, and confabulated details). Pass `--prose` for a free-text description instead. The envelope's `structured` field is `true` when JSON parsed cleanly.

Pass the video via `-r` — either a **local file** (mp4/mov/webm/mkv/avi; sent as a base64 data URL) or a **URL** (publicly accessible, including YouTube). OpenRouter only; no `-o`, prompt enhancement, or output path needed.

**Model selection (`-m`)** — three presets cover the common cases; or pick any model by keyword (see `--list-models`):

| Preset | Resolves to | When to use |
|--------|-------------|-------------|
| `video-default` (or omit `-m`) | `gemini3.5-flash` (Google Gemini 3.5 Flash) | **Default** — best accuracy + fastest; reads audio. ~11× the cost of the cheap tier |
| `video-cheap` | `qwen3.5-flash` (Qwen3.5 Flash) | Rock-bottom cost for quick scene summaries (or `mimo` for a cheap, more detailed read) |
| `video-quality` | `gemini3-pro` (Google Gemini 3.1 Pro) | Highest-accuracy reading when it matters most |

All 15 video-capable models are selectable by keyword: `qwen3.5-flash`, `seed-1.6-flash`, `seed-2.0-mini`, `mimo`, `qwen3.6-35b`, `qwen3.6-flash`, `step-3.7-flash`, `gemini3-flash-lite`, `seed-2.0-lite`, `seed-1.6`, `qwen3.5-plus`, `minimax-m3`, `qwen3.6-plus`, `gemini3.5-flash`, `gemini3-pro` (cheapest → priciest). Run `--list-models` for IDs and per-1M-token pricing.

> **Bare family names are not keywords.** `-m gemini`, `-m seed`, or `-m qwen` (the *image*-model families) are **not** valid `--analyze-video` selectors and error with "unknown video model". Use a preset (`video-default`/`video-cheap`/`video-quality`) or a full keyword from the list above (e.g. `gemini3.5-flash`, `seed-1.6-flash`).

```bash
# Default model (gemini3.5-flash), structured JSON output
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze-video -r "clip.mp4"

# Free-text prose instead of JSON
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze-video -r "clip.mp4" --prose

# Rock-bottom cost preset
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze-video -r "clip.mp4" -m video-cheap

# Highest-accuracy preset on a YouTube URL with a custom focus
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze-video -r "https://youtu.be/VIDEO_ID" -m video-quality \
  -p "Focus on camera movement and lighting"
```

**JSON output format** (default — `analysis` is a structured object):

```json
{"ok": true, "analyze": true, "analyze_video": true, "structured": true, "analysis": {"summary": "...", "setting": "...", "subjects": [{"role": "protagonist", "appearance": "...", "confidence": "high"}], "shot_timeline": [{"timestamp": "0:00", "action": "...", "camera": "..."}], "camera_techniques": ["..."], "editing_stylization": ["monochrome freeze-frame", "..."], "lighting": "...", "color_palette": ["..."], "mood": "...", "uncertain_details": ["..."], "video_generation_prompt": "..."}, "provider": "openrouter", "model": "google/gemini-3.5-flash", "mode": "gateway", "elapsed_seconds": 16.9, "video_source": "clip.mp4"}
```

With `--prose`, `analysis` is a plain text string and `structured` is `false`.

### Frame grounding (`--contact-sheet`, `--verify`)

The model samples its own frames internally, but it can still slip a confabulation into a
single shot (e.g. a "golden glowing eye" in the final beat that isn't there). Two opt-in,
**local-file-only** aids ground the analysis against real pixels using ffmpeg-extracted
keyframes:

- **`--contact-sheet PATH`** — extracts ~12 evenly-spaced keyframes (always including first
  and last; capped uniform sampling, not scene-detect) and tiles them into one labeled image
  at `PATH`. This is the highest-leverage aid: a human (or you) can eyeball the whole clip at
  a glance to sanity-check the description. Built with ImageMagick `montage` (timestamp
  labels) or, if absent, ffmpeg's `tile` filter. The path is echoed back as `contact_sheet`
  in the JSON envelope.
- **`--verify`** — runs a cheap **second pass** that sends the contact sheet + a few full
  keyframes (with timestamps) and the pass-1 analysis back to the **same model**, and asks it
  to classify each claim `supported` / `contradicted` / `not_visible` strictly from the
  frames. **The video is not re-sent** (that would just re-confabulate from the same pixels),
  and undiscernible details stay `not_visible` rather than being "resolved" into a guess. Adds
  a `verification` object: `{claims[]{claim,verdict,evidence}, corrections[], overall_accuracy}`.

Both are skipped with a warning (never a hard error) for URL/YouTube sources or if ffmpeg is
missing — the analysis itself always proceeds. Extracted frames go to a temp dir that is
cleaned up automatically; only the `--contact-sheet` image is kept.

```bash
# Save a ground-truth contact sheet alongside the analysis, and verify the claims
uv run python ${CLAUDE_SKILL_DIR}/scripts/generate-image.py \
  --analyze-video -r "clip.mp4" \
  --contact-sheet "exports/clip_frames.png" --verify
```

**Notes:**
- **Incompatible flags:** cannot be combined with `--analyze`, `-t`, `-a`, or `-s`, and requires `--provider openrouter`.
- **Large local files** (>20 MB) trigger a warning — base64 payloads can be slow o
