feat: gemini-image skill with Python API scripts
Replace CLI-based approach with Gemini Python API (google-genai). Scripts: generate_image.py, edit_image.py. Requires GEMINI_API_KEY. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9ff8454daf
commit
92670a6687
|
|
@ -1,8 +1,31 @@
|
||||||
---
|
---
|
||||||
name: gemini-image
|
name: gemini-image
|
||||||
description: Use when user asks to generate, create, or produce images.
|
description: Use when user asks to generate, create, edit, or produce images.
|
||||||
---
|
---
|
||||||
Generate images via Gemini CLI.
|
# Gemini Image Generation
|
||||||
1. `gemini -p "generate an image of: <prompt>" -o <output-path>`
|
|
||||||
2. Formats: PNG, JPG, WEBP
|
Uses Gemini Python API via scripts in this skill's `scripts/` directory.
|
||||||
3. Ask for output path if not specified
|
|
||||||
|
## Setup (once)
|
||||||
|
```bash
|
||||||
|
pip install -r ~/.claude/skills/gemini-image/requirements.txt
|
||||||
|
```
|
||||||
|
Requires `GEMINI_API_KEY` environment variable.
|
||||||
|
|
||||||
|
## Generate image from text
|
||||||
|
```bash
|
||||||
|
python ~/.claude/skills/gemini-image/scripts/generate_image.py "<prompt>" <output.jpg>
|
||||||
|
```
|
||||||
|
Options: `--model gemini-3-pro-image-preview` (higher quality), `--aspect 16:9`, `--size 2K`
|
||||||
|
|
||||||
|
## Edit existing image
|
||||||
|
```bash
|
||||||
|
python ~/.claude/skills/gemini-image/scripts/edit_image.py <input.jpg> "<instruction>" <output.jpg>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important
|
||||||
|
- Output format is **JPEG by default** — always use `.jpg` extension
|
||||||
|
- Models: `gemini-2.5-flash-image` (default, fast), `gemini-3-pro-image-preview` (higher quality)
|
||||||
|
- Aspect ratios: 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9
|
||||||
|
- Sizes: 1K (default), 2K, 4K (4K only with pro model)
|
||||||
|
- Ask user for output path if not specified
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
google-genai>=1.0.0
|
||||||
|
Pillow>=10.0.0
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Edit existing images using Gemini API.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python edit_image.py input.jpg "edit instruction" output.jpg [--model MODEL]
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
GEMINI_API_KEY - Required API key
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
from google import genai
|
||||||
|
from google.genai import types
|
||||||
|
|
||||||
|
|
||||||
|
def edit_image(
|
||||||
|
input_path: str,
|
||||||
|
instruction: str,
|
||||||
|
output_path: str,
|
||||||
|
model: str = "gemini-2.5-flash-image",
|
||||||
|
aspect_ratio: str | None = None,
|
||||||
|
image_size: str | None = None,
|
||||||
|
) -> str | None:
|
||||||
|
api_key = os.environ.get("GEMINI_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
raise EnvironmentError("GEMINI_API_KEY environment variable not set")
|
||||||
|
|
||||||
|
if not os.path.exists(input_path):
|
||||||
|
raise FileNotFoundError(f"Input image not found: {input_path}")
|
||||||
|
|
||||||
|
client = genai.Client(api_key=api_key)
|
||||||
|
input_image = Image.open(input_path)
|
||||||
|
|
||||||
|
config_kwargs = {"response_modalities": ["TEXT", "IMAGE"]}
|
||||||
|
|
||||||
|
image_config_kwargs = {}
|
||||||
|
if aspect_ratio:
|
||||||
|
image_config_kwargs["aspect_ratio"] = aspect_ratio
|
||||||
|
if image_size:
|
||||||
|
image_config_kwargs["image_size"] = image_size
|
||||||
|
|
||||||
|
if image_config_kwargs:
|
||||||
|
config_kwargs["image_config"] = types.ImageConfig(**image_config_kwargs)
|
||||||
|
|
||||||
|
config = types.GenerateContentConfig(**config_kwargs)
|
||||||
|
|
||||||
|
response = client.models.generate_content(
|
||||||
|
model=model,
|
||||||
|
contents=[instruction, input_image],
|
||||||
|
config=config,
|
||||||
|
)
|
||||||
|
|
||||||
|
text_response = None
|
||||||
|
image_saved = False
|
||||||
|
|
||||||
|
for part in response.parts:
|
||||||
|
if part.text is not None:
|
||||||
|
text_response = part.text
|
||||||
|
elif part.inline_data is not None:
|
||||||
|
image = part.as_image()
|
||||||
|
image.save(output_path)
|
||||||
|
image_saved = True
|
||||||
|
|
||||||
|
if not image_saved:
|
||||||
|
raise RuntimeError("No image was generated.")
|
||||||
|
|
||||||
|
return text_response
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Edit images using Gemini API")
|
||||||
|
parser.add_argument("input", help="Input image path")
|
||||||
|
parser.add_argument("instruction", help="Edit instruction")
|
||||||
|
parser.add_argument("output", help="Output file path (.jpg recommended)")
|
||||||
|
parser.add_argument("--model", "-m", default="gemini-2.5-flash-image",
|
||||||
|
choices=["gemini-2.5-flash-image", "gemini-3-pro-image-preview"])
|
||||||
|
parser.add_argument("--aspect", "-a",
|
||||||
|
choices=["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"])
|
||||||
|
parser.add_argument("--size", "-s", choices=["1K", "2K", "4K"])
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = edit_image(args.input, args.instruction, args.output, args.model, args.aspect, args.size)
|
||||||
|
print(f"Edited image saved to: {args.output}")
|
||||||
|
if text:
|
||||||
|
print(f"Model response: {text}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate images from text prompts using Gemini API.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python generate_image.py "prompt" output.jpg [--model MODEL] [--aspect RATIO] [--size SIZE]
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
GEMINI_API_KEY - Required API key
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from google import genai
|
||||||
|
from google.genai import types
|
||||||
|
|
||||||
|
|
||||||
|
def generate_image(
|
||||||
|
prompt: str,
|
||||||
|
output_path: str,
|
||||||
|
model: str = "gemini-2.5-flash-image",
|
||||||
|
aspect_ratio: str | None = None,
|
||||||
|
image_size: str | None = None,
|
||||||
|
) -> str | None:
|
||||||
|
api_key = os.environ.get("GEMINI_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
raise EnvironmentError("GEMINI_API_KEY environment variable not set")
|
||||||
|
|
||||||
|
client = genai.Client(api_key=api_key)
|
||||||
|
|
||||||
|
config_kwargs = {"response_modalities": ["TEXT", "IMAGE"]}
|
||||||
|
|
||||||
|
image_config_kwargs = {}
|
||||||
|
if aspect_ratio:
|
||||||
|
image_config_kwargs["aspect_ratio"] = aspect_ratio
|
||||||
|
if image_size:
|
||||||
|
image_config_kwargs["image_size"] = image_size
|
||||||
|
|
||||||
|
if image_config_kwargs:
|
||||||
|
config_kwargs["image_config"] = types.ImageConfig(**image_config_kwargs)
|
||||||
|
|
||||||
|
config = types.GenerateContentConfig(**config_kwargs)
|
||||||
|
|
||||||
|
response = client.models.generate_content(
|
||||||
|
model=model,
|
||||||
|
contents=[prompt],
|
||||||
|
config=config,
|
||||||
|
)
|
||||||
|
|
||||||
|
text_response = None
|
||||||
|
image_saved = False
|
||||||
|
|
||||||
|
for part in response.parts:
|
||||||
|
if part.text is not None:
|
||||||
|
text_response = part.text
|
||||||
|
elif part.inline_data is not None:
|
||||||
|
image = part.as_image()
|
||||||
|
image.save(output_path)
|
||||||
|
image_saved = True
|
||||||
|
|
||||||
|
if not image_saved:
|
||||||
|
raise RuntimeError("No image was generated.")
|
||||||
|
|
||||||
|
return text_response
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Generate images using Gemini API")
|
||||||
|
parser.add_argument("prompt", help="Text prompt")
|
||||||
|
parser.add_argument("output", help="Output file path (.jpg recommended)")
|
||||||
|
parser.add_argument("--model", "-m", default="gemini-2.5-flash-image",
|
||||||
|
choices=["gemini-2.5-flash-image", "gemini-3-pro-image-preview"])
|
||||||
|
parser.add_argument("--aspect", "-a",
|
||||||
|
choices=["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"])
|
||||||
|
parser.add_argument("--size", "-s", choices=["1K", "2K", "4K"])
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = generate_image(args.prompt, args.output, args.model, args.aspect, args.size)
|
||||||
|
print(f"Image saved to: {args.output}")
|
||||||
|
if text:
|
||||||
|
print(f"Model response: {text}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue