Gemini Image Generation
Generate images from text prompts using Google's Gemini API, optionally with uploaded reference photos.
Requirements
Workflow
1. Text-only generation
python3 ~/.claude/skills/image-from-gemini/scripts/generate.py \
"your detailed image prompt here" \
-o /path/to/output.png
2. Photo-to-poster generation
Upload one or more reference photos. Gemini will incorporate the people into the generated design.
# Single photo
python3 ~/.claude/skills/image-from-gemini/scripts/generate.py \
-i /path/to/person.jpg \
"Square Instagram post. Place the person from the photo as the hero..." \
-o /path/to/output.png
# Multiple photos
python3 ~/.claude/skills/image-from-gemini/scripts/generate.py \
-i /path/to/person1.jpg \
-i /path/to/person2.jpg \
-i /path/to/person3.jpg \
"Instagram story with all uploaded people as instructors..." \
-o /path/to/output.png
3. Display and iterate
After generation, use the Read tool to display the image inline. Adjust the prompt and regenerate as needed.
4. Batch generation (multiple styles)
For generating multiple variants, use Python directly to avoid repeated shell calls:
python3 << 'EOF'
import base64, json, os, urllib.request
api_key = os.environ.get("GEMINI_API_KEY")
model = "gemini-3.1-flash-image-preview"
url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent?key={api_key}"
with open("photo.jpg", "rb") as f:
img_b64 = base64.b64encode(f.read()).decode()
prompts = {
"v1-style": "prompt for style 1...",
"v2-style": "prompt for style 2...",
}
for name, prompt in prompts.items():
payload = {
"contents": [{"parts": [
{"inlineData": {"mimeType": "image/jpeg", "data": img_b64}},
{"text": prompt}
]}],
"generationConfig": {"responseModalities": ["TEXT", "IMAGE"]}
}
data = json.dumps(payload).encode()
req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})
resp = urllib.request.urlopen(req, timeout=180)
result = json.loads(resp.read())
for candidate in result.get("candidates", []):
for part in candidate.get("content", {}).get("parts", []):
if "inlineData" in part:
img_data = base64.b64decode(part["inlineData"]["data"])
with open(f"output-{name}.png", "wb") as f:
f.write(img_data)
break
EOF
Photo-to-poster prompting tips
When uploading photos of real people for event posters:
- Say "Place the person from the photo" — Gemini understands the reference
- Say "remove the original background" — Gemini will cut out the person
- Use
rembg (Python) beforehand for cleaner cutouts if needed: uvx --with "rembg[cpu,cli]" rembg i input.jpg output.png
- Specify exact text: spell out every word, every number, every address
- Repeat critical text in the prompt (addresses, URLs) to avoid hallucination
- Say "DO NOT include any logo" if you plan to overlay it later with HTML
- For multiple photos, upload all and number them: "Photo 1: Name1, Photo 2: Name2"
Style presets
Proven styles for event/dance posters:
| Style |
Description |
Best for |
| Neon |
Dark bg, neon pink/cyan streaks, nightclub vibe |
Club events, parties |
| Cuban |
Cuban flag colors, distressed texture, palm shadows, Havana aesthetic |
Latin dance, cultural events |
| Minimal |
Black bg, gold circle frame, Swiss typography |
Premium/luxury feel |
| Fire |
Flames, sparks, dramatic lighting from below |
High-energy concerts, competitions |
| Warm bokeh |
Burgundy/maroon, golden bokeh lights |
Warm, inviting classes |
General prompting tips
- Be specific about layout, colors, fonts, and mood
- For posters with text: spell out every word exactly as it should appear
- Mention dimensions: "Square 1080x1080" or "Portrait 1080x1920"
- Gemini avoids contractions — use "will not" instead of "won't"
- For dark backgrounds, specify hex colors (e.g., "#1E1B2E")
Available models
| Model |
Best for |
gemini-3.1-flash-image-preview |
Fast, good quality (default) |
gemini-3-pro-image-preview |
Higher quality, slower |
Troubleshooting
- "API key not found" — Set
export GEMINI_API_KEY=your-key in ~/.zshrc
- 404 model not found — List models:
curl -s "https://generativelanguage.googleapis.com/v1beta/models?key=$GEMINI_API_KEY" | python3 -c "import json,sys; [print(m['name']) for m in json.load(sys.stdin)['models'] if 'image' in m['name']]"
- Argument list too long — The script uses
urllib internally, not curl. If using the API directly, avoid subprocess with large base64 payloads.
- 500 Internal Server Error — Image may be too small or corrupted. Use the original full-resolution photo, not a tiny crop.
- No image returned — Rephrase or simplify the prompt. Gemini may refuse certain compositions.
1---2name: image-from-gemini3description: Generate images using Google Gemini API. Use when the user asks to generate, create, or make an image, illustration, poster design, mockup, thumbnail, or any visual asset using AI. Supports uploading reference photos for poster/flyer generation with real people. Also trigger when the user mentions Gemini image generation or asks to visualize something as an image.4---56# Gemini Image Generation78Generate images from text prompts using Google's Gemini API, optionally with uploaded reference photos.910## Requirements1112- `GEMINI_API_KEY` environment variable (or set in `~/.zshrc`)13- `python3` (standard on macOS/Linux)14- Get a key at https://aistudio.google.com/apikey1516## Workflow1718### 1. Text-only generation1920```bash21python3 ~/.claude/skills/image-from-gemini/scripts/generate.py \22 "your detailed image prompt here" \23 -o /path/to/output.png24```2526### 2. Photo-to-poster generation2728Upload one or more reference photos. Gemini will incorporate the people into the generated design.2930```bash31# Single photo32python3 ~/.claude/skills/image-from-gemini/scripts/generate.py \33 -i /path/to/person.jpg \34 "Square Instagram post. Place the person from the photo as the hero..." \35 -o /path/to/output.png3637# Multiple photos38python3 ~/.claude/skills/image-from-gemini/scripts/generate.py \39 -i /path/to/person1.jpg \40 -i /path/to/person2.jpg \41 -i /path/to/person3.jpg \42 "Instagram story with all uploaded people as instructors..." \43 -o /path/to/output.png44```4546### 3. Display and iterate4748After generation, use the **Read tool** to display the image inline. Adjust the prompt and regenerate as needed.4950### 4. Batch generation (multiple styles)5152For generating multiple variants, use Python directly to avoid repeated shell calls:5354```python55python3 << 'EOF'56import base64, json, os, urllib.request5758api_key = os.environ.get("GEMINI_API_KEY")59model = "gemini-3.1-flash-image-preview"60url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent?key={api_key}"6162with open("photo.jpg", "rb") as f:63 img_b64 = base64.b64encode(f.read()).decode()6465prompts = {66 "v1-style": "prompt for style 1...",67 "v2-style": "prompt for style 2...",68}6970for name, prompt in prompts.items():71 payload = {72 "contents": [{"parts": [73 {"inlineData": {"mimeType": "image/jpeg", "data": img_b64}},74 {"text": prompt}75 ]}],76 "generationConfig": {"responseModalities": ["TEXT", "IMAGE"]}77 }78 data = json.dumps(payload).encode()79 req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})80 resp = urllib.request.urlopen(req, timeout=180)81 result = json.loads(resp.read())82 for candidate in result.get("candidates", []):83 for part in candidate.get("content", {}).get("parts", []):84 if "inlineData" in part:85 img_data = base64.b64decode(part["inlineData"]["data"])86 with open(f"output-{name}.png", "wb") as f:87 f.write(img_data)88 break89EOF90```9192## Photo-to-poster prompting tips9394When uploading photos of real people for event posters:9596- Say "Place the person from the photo" — Gemini understands the reference97- Say "remove the original background" — Gemini will cut out the person98- Use `rembg` (Python) beforehand for cleaner cutouts if needed: `uvx --with "rembg[cpu,cli]" rembg i input.jpg output.png`99- Specify exact text: spell out every word, every number, every address100- Repeat critical text in the prompt (addresses, URLs) to avoid hallucination101- Say "DO NOT include any logo" if you plan to overlay it later with HTML102- For multiple photos, upload all and number them: "Photo 1: Name1, Photo 2: Name2"103104## Style presets105106Proven styles for event/dance posters:107108| Style | Description | Best for |109|-------|------------|----------|110| **Neon** | Dark bg, neon pink/cyan streaks, nightclub vibe | Club events, parties |111| **Cuban** | Cuban flag colors, distressed texture, palm shadows, Havana aesthetic | Latin dance, cultural events |112| **Minimal** | Black bg, gold circle frame, Swiss typography | Premium/luxury feel |113| **Fire** | Flames, sparks, dramatic lighting from below | High-energy concerts, competitions |114| **Warm bokeh** | Burgundy/maroon, golden bokeh lights | Warm, inviting classes |115116## General prompting tips117118- Be specific about layout, colors, fonts, and mood119- For posters with text: spell out every word exactly as it should appear120- Mention dimensions: "Square 1080x1080" or "Portrait 1080x1920"121- Gemini avoids contractions — use "will not" instead of "won't"122- For dark backgrounds, specify hex colors (e.g., "#1E1B2E")123124## Available models125126| Model | Best for |127|-------|----------|128| `gemini-3.1-flash-image-preview` | Fast, good quality (default) |129| `gemini-3-pro-image-preview` | Higher quality, slower |130131## Troubleshooting132133- **"API key not found"** — Set `export GEMINI_API_KEY=your-key` in `~/.zshrc`134- **404 model not found** — List models: `curl -s "https://generativelanguage.googleapis.com/v1beta/models?key=$GEMINI_API_KEY" | python3 -c "import json,sys; [print(m['name']) for m in json.load(sys.stdin)['models'] if 'image' in m['name']]"`135- **Argument list too long** — The script uses `urllib` internally, not `curl`. If using the API directly, avoid `subprocess` with large base64 payloads.136- **500 Internal Server Error** — Image may be too small or corrupted. Use the original full-resolution photo, not a tiny crop.137- **No image returned** — Rephrase or simplify the prompt. Gemini may refuse certain compositions.