2026年最新版:Google Gemini Image モデル完全ガイド
対象読者: Vertex AI で画像生成を始めたい方、Gemini / Imagen のどのモデルを選べばいいか迷っている方
1. モデル体系の全体像:「Nano Banana」とは?
Google が推奨する画像生成モデルには大きく 2 系統があります。
| 系統 | ブランド名 | 特徴 |
|---|---|---|
| Gemini Image | Nano Banana 系 | 会話・編集・マルチ画像合成に強い。現在の主推奨 |
| Imagen | Imagen 3 / 4 系 | テキスト→画像の純粋生成特化 |
📢 Google 公式は「まず Gemini Image(Nano Banana)を使え」としています。
Imagen 系は 2026年8月頃から段階的に非推奨になる予定で、Nano Banana への移行が推奨されています。
2. Gemini Image モデル(Nano Banana 系)一覧
会話的編集・マルチイメージ合成・キャラクター一貫性に強く、Imagen より柔軟です。
| 順番 | モデル名 | コードネーム | モデル ID | 特徴 |
|---|---|---|---|---|
| 1 | Gemini 3 Pro Image | Nano Banana Pro | gemini-3-pro-image |
最高品質。推論強化された構図、テキスト描画、最大14枚の参照画像対応 |
| 2 | Gemini 3.1 Flash Image | ⭐ Nano Banana 2 | gemini-3.1-flash-image |
高品質+高速。会話編集・マルチイメージ融合・キャラクター一貫性に強い |
| 3 | Gemini 3.1 Flash-Lite Image | Nano Banana 2 Lite | gemini-3.1-flash-lite-image |
超高速・低コスト。大量生成向け |
| 4 | Gemini 2.5 Flash Image | Nano Banana | gemini-2.5-flash-image |
前世代の安定版。十分高品質 |
🍌 Nano Banana の命名対応まとめ
Nano Banana = Gemini 2.5 Flash Image(前世代)
Nano Banana 2 = Gemini 3.1 Flash Image ← 現在のメイン推奨
Nano Banana 2 Lite = Gemini 3.1 Flash-Lite Image(軽量版)
Nano Banana Pro = Gemini 3 Pro Image(最高品質版)
3. Gemini 3.1 Flash Image(Nano Banana 2)詳細スペック
Google Cloud 公式ドキュメント(Gemini 3.1 Flash Image)より。
基本情報
| 項目 | 内容 |
|---|---|
| モデル ID | gemini-3.1-flash-image |
| リリース日 | 2026年5月28日 |
| リリースステージ | GA(一般提供) |
| 提供終了日 | 2027年5月28日以降 |
| サポートリージョン | グローバル(global) |
| 入力サイズ上限 | 500 MB |
モダリティ(対応入出力)
| モダリティ | 入力 | 出力 |
|---|---|---|
| テキスト | ✅ | ✅ |
| 画像 | ✅ | ✅ |
| 音声 | ❌ | ❌ |
| 動画 | ✅(プレビュー) | ❌ |
トークン上限
| 種別 | 上限 |
|---|---|
| コンテキストウィンドウ | 131,072 トークン |
| 最大出力トークン | 32,768 トークン |
機能一覧
| 機能 | 対応状況 |
|---|---|
| 思考(Thinking) | ✅ サポート |
| システム指示 | ✅ サポート |
| 画像生成 | ✅ サポート(動画入力からの生成も対応) |
| 画像編集(マルチターン) | ✅ サポート |
| 画像×テキストのインターリーブ出力 | ✅ サポート |
| Content Credentials(C2PA) | ✅ サポート |
| Google検索グラウンディング | ✅ サポート |
| Gemini Live API | ❌ 非対応 |
| 構造化出力 | ❌ 非対応 |
| 関数呼び出し | ❌ 非対応 |
| コンテキストキャッシュ | ❌ 非対応 |
| RAG Engine | ❌ 非対応 |
| チューニング | ❌ 非対応 |
技術仕様(画像)
| 項目 | 仕様 |
|---|---|
| プロンプトあたりの最大入力画像数 | 14枚 |
| インライン/コンソール直接アップロードの最大ファイルサイズ | 7 MB |
| Google Cloud Storage からの最大ファイルサイズ | 30 MB |
| 対応アスペクト比 | 1:1、3:2、2:3、3:4、1:4、4:1、4:3、4:5、5:4、1:8、8:1、9:16、16:9、21:9、9:21 |
| 対応解像度 | 512、1K、2K、4K(プレビュー) |
| 対応 MIME タイプ | image/png、image/jpeg、image/webp、image/heic、image/heif |
画像生成のトークン消費量
| 解像度 | 出力画像トークン |
|---|---|
| 512(約 0.25 MP) | 747 |
| 1K(約 1 MP) | 1,120 |
| 2K(約 4 MP) | 1,680 |
| 4K(約 16 MP)※プレビュー | 2,520 |
入力画像 1 枚あたり 1,120 入力画像トークンを消費します。
パラメータデフォルト値
| パラメータ | 範囲 | デフォルト |
|---|---|---|
| Temperature | 0.0 ~ 2.0 | 1.0 |
| topP | 0.0 ~ 1.0 | 0.95 |
| candidateCount | — | 1 |
使用オプション
| オプション | 対応状況 |
|---|---|
| プロビジョンド スループット | ✅ |
| バッチ推論 | ✅ |
| Pay-as-you-go(標準・Flex) | ✅ |
| 固定割り当て | ❌ |
セキュリティ管理
| セキュリティ機能 | オンライン予測 | バッチ推論 |
|---|---|---|
| データ所在地 | ✅ | ✅ |
| CMEK | ✅ | ❌ |
| VPC-SC | ✅ | ✅ |
| AXT | ✅ | ❌ |
4. Imagen モデル一覧(テキスト→画像 特化)
純粋な画像生成に特化。品質や速度を優先する場合に使います。
| 順番 | モデル名 | 特徴 |
|---|---|---|
| 1 | imagen-4.0-generate-001 | 最新の標準版(2025年以降の現行主力) |
| 2 | imagen-4.0-ultra-generate-001 | 最高品質版 |
| 3 | imagen-4.0-fast-generate-001 | 高速版 |
| 4 | imagen-3.0-generate-002 | Imagen 3 の最終安定版 |
| 5 | imagen-3.0-generate-001 | Imagen 3 の初期版 |
| 6 | imagen-3.0-fast-generate-001 | Imagen 3 の高速版(一部でまだ使える) |
⚠️ 移行注意: Imagen 系は 2026年8月頃より段階的に非推奨化予定。新規プロジェクトは Gemini Image(Nano Banana)を推奨。
5. モデル選択チートシート
どれを選べばいい?
最高品質が欲しい
→ Gemini 3 Pro Image(Nano Banana Pro)
バランス重視(会話編集・キャラクター一貫性)
→ Gemini 3.1 Flash Image(Nano Banana 2) ★現在の推奨★
とにかく速く・安く大量生成したい
→ Gemini 3.1 Flash-Lite Image(Nano Banana 2 Lite)
前世代で安定したものを使いたい
→ Gemini 2.5 Flash Image(Nano Banana)
テキスト→画像 シンプル生成(キャラクター一貫性不要)
→ Imagen 4.0 系(ただし非推奨移行に注意)
6. 実装コード解説:gcp_image_gen.py
以下は、LINEスタンプ(大五郎+お婆さん)の自動生成に使っている Vertex AI 共通ライブラリです。
コードを読みながら、各部分の役割を解説します。
6.1 モジュールの概要とデフォルト設定
DEFAULT_GCP_PROJECT = "your-gcp-project-id" # 実際のプロジェクトIDに置き換えてください
DEFAULT_LOCATION = "global"
DEFAULT_MODEL = "gemini-3.1-flash-image"
DEFAULT_ASPECT_RATIO = "4:3"
ポイント: gemini-3.1-flash-image は グローバルエンドポイント(global)のみ対応です。
リージョンを us-central1 などにすると API エラーになるので注意。
6.2 参照画像パスの定義
REF_DUO_PORTRAIT = (
PROJECT_ROOT / "common-assets/新参照画像/二人のサイズ/SizeRefference_DaigoroAndGranma.png"
)
REF_DUO_LAND = (
PROJECT_ROOT / "common-assets/新参照画像/二人のサイズ/SizeReference_DaigoroAndGranma_landscape_4x3.jpg"
)
REF_DAIGORO = PROJECT_ROOT / "common-assets/新参照画像/大五郎/Daigoro_userRef_standing.jpg"
REF_GRANMA = PROJECT_ROOT / "common-assets/新参照画像/お婆さん/Granma_userRef_standing.jpg"
なぜ参照画像が必要か?
Gemini Image の「キャラクター一貫性」機能を使うためです。
| 参照画像 | 役割 |
|---|---|
SizeRefference_DaigoroAndGranma.png |
二人の体格差・縦位置構図の参照 |
SizeReference_DaigoroAndGranma_landscape_4x3.jpg |
横位置(4:3)構図の参照 |
Daigoro_userRef_standing.jpg |
大五郎単体の外見参照 |
Granma_userRef_standing.jpg |
お婆さん単体の外見参照 |
最大 14枚 まで参照画像を渡せるのが gemini-3.1-flash-image の強みです。
6.3 default_character_refs() — デフォルト参照画像リスト
def default_character_refs() -> list[Path]:
"""Duo size refs + solo identity refs (only existing files)."""
candidates = [REF_DUO_PORTRAIT, REF_DUO_LAND, REF_DAIGORO, REF_GRANMA]
return [p for p in candidates if p.exists()]
存在するファイルだけを返すので、環境によってファイルが欠けていてもエラーになりません。
デバッグ時に特定の参照だけで試したい場合は use_default_refs=False で無効化できます。
6.4 make_client() — Vertex AI クライアント生成
def make_client(
project: str = DEFAULT_GCP_PROJECT,
location: str = DEFAULT_LOCATION,
) -> genai.Client:
return genai.Client(vertexai=True, project=project, location=location)
vertexai=True を渡すことで、Google AI Studio ではなく Vertex AI エンドポイントに接続します。
認証は Application Default Credentials(ADC)を自動使用するため、事前に以下が必要です。
gcloud auth application-default login
6.5 build_duo_sticker_prompt() — プロンプト生成
def build_duo_sticker_prompt(
pose: str,
aspect_ratio: str = DEFAULT_ASPECT_RATIO,
*,
template_path: Path | str | None = None,
) -> str:
"""Fill prompts/duo_sticker_base.txt with pose + aspect_ratio."""
template = load_prompt_template(template_path)
return template.format(pose=pose.strip(), aspect_ratio=aspect_ratio)
prompts/duo_sticker_base.txt に書かれたベーステンプレートに、ポーズ説明(pose)とアスペクト比を埋め込みます。
テンプレートファイルが存在しない場合は _FALLBACK_TEMPLATE が使われます。
フォールバックテンプレート(ファイルがない場合):
Create a LINE sticker image in WIDE landscape {aspect_ratio} aspect ratio (horizontal).
Match reference characters (Daigoro giant orange cat + grandma in kimono).
Pose / mood:
{pose}
Pure white background, no shadows, no text.
6.6 load_stamps_yaml() — スタンプ定義ファイルの読み込み
def load_stamps_yaml(path: Path | str) -> list[tuple[str, str, str]]:
stamps/ の形式:
stamps:
- id: "01"
text: "おはよ〜"
pose: "Morning greeting, Daigoro and Grandma wave energetically..."
- id: "02"
text: "ありがとう!"
pose: "Both bow deeply in gratitude..."
戻り値は (id, text, pose) のリスト。バッチ生成の入力データとして使います。
id が数字の場合は zfill(2) でゼロパディング(1 → "01")します。
6.7 generate_image() — 画像生成コア関数
最も重要な関数です。詳しく解説します。
① コンテンツリストの構築
contents: list = [prompt]
for p in refs:
contents.append(
types.Part.from_bytes(data=p.read_bytes(), mime_type=_mime_for(p))
)
Gemini API の contents はマルチモーダル形式で、テキストと画像パーツを混在させます。
先頭にテキストプロンプトを置き、続けて参照画像バイナリを追加します。
② 生成設定
config = types.GenerateContentConfig(
response_modalities=list(response_modalities), # ["TEXT", "IMAGE"]
image_config=types.ImageConfig(aspect_ratio=aspect_ratio),
)
response_modalities=["TEXT", "IMAGE"]で画像とテキストの両方を出力可能にしますimage_configでアスペクト比(4:3、16:9など)を指定します
③ リトライロジック(重要!)
for attempt in range(1, max_attempts + 1):
try:
resp = client.models.generate_content(
model=model, contents=contents, config=config
)
...
except Exception as e:
msg = str(e)
if "429" in msg or "RESOURCE_EXHAUSTED" in msg:
wait = min(90, 10 * attempt) # レートリミット: 最大90秒待機
else:
wait = min(30, 3 * attempt) # その他エラー: 最大30秒待機
time.sleep(wait)
Vertex AI の画像生成はレートリミット(429エラー)が頻発します。
attempt 回数に応じて待機時間を増やす指数バックオフ戦略で対処します。
| エラー種別 | 待機時間の計算式 | 最大待機 |
|---|---|---|
| 429 / RESOURCE_EXHAUSTED | 10 × attempt 秒 |
90秒 |
| その他のエラー | 3 × attempt 秒 |
30秒 |
④ 画像の取り出し
def _extract_image(resp) -> Image.Image | None:
for cand in resp.candidates or []:
content = getattr(cand, "content", None)
if not content:
continue
for part in content.parts or []:
inline = getattr(part, "inline_data", None)
if inline and inline.data:
return Image.open(io.BytesIO(inline.data))
return None
レスポンスの candidates[0].content.parts から inline_data を探し、
バイナリを PIL Image に変換します。getattr で安全にアクセスしているため、
レスポンス構造が一部欠けていてもクラッシュしません。
6.8 generate_images() — バッチ生成ヘルパー
def generate_images(
items: Iterable[tuple[str, Path, str]],
...
pace_seconds: float = 3.0,
skip_existing: bool = False,
) -> list[Path]:
引数 items は (ラベル名, 出力パス, ポーズ or フルプロンプト) のイテラブル。
スマートなプロンプト判定:
if "Create a LINE sticker" not in text and len(text) < 800:
prompt = build_duo_sticker_prompt(text, aspect_ratio=aspect_ratio)
- 短い「ポーズフラグメント」→ 自動的にベーステンプレートに埋め込み
- 長文やフルプロンプト → そのまま使用
| 引数 | デフォルト | 説明 |
|---|---|---|
pace_seconds |
3.0 | リクエスト間のスリープ秒数。レートリミット回避 |
skip_existing |
False | 出力ファイルが既に存在する場合はスキップ |
use_default_refs |
True | プロジェクトのキャラクター参照画像を使用 |
pace_seconds=3.0 は API への過負荷を防ぐスロットリングです。
skip_existing=True で途中から再実行する際に便利です。
6.9 CLI インターフェース
# ポーズ指定で生成(テンプレートに埋め込まれる)
python3 scripts/gcp_image_gen.py -o /tmp/test.png --pose "Both wave hello"
# フルプロンプト指定(--pose より優先)
python3 scripts/gcp_image_gen.py -o /tmp/test.png --prompt "A photo of ..."
# デフォルト参照画像なし(高速・プロトタイプ向け)
python3 scripts/gcp_image_gen.py -o /tmp/test.png --pose "..." --no-default-refs
# 追加参照画像を指定(複数可)
python3 scripts/gcp_image_gen.py -o /tmp/test.png --pose "..." \
--ref /path/to/extra1.png --ref /path/to/extra2.png
# 別モデルで生成(例: Pro モデル)
python3 scripts/gcp_image_gen.py -o /tmp/test.png --pose "..." \
--model gemini-3-pro-image
| フラグ | 説明 |
|---|---|
-o / --out |
出力ファイルパス(必須) |
--pose |
ポーズフラグメント(テンプレートに埋め込まれる) |
--prompt |
フルプロンプト(--pose より優先) |
--template |
ベーステンプレートファイルパス |
--aspect-ratio |
アスペクト比(デフォルト: 4:3) |
--model |
モデルID(デフォルト: gemini-3.1-flash-image) |
--project |
GCP プロジェクト ID |
--location |
Vertex AI ロケーション(デフォルト: global) |
--ref |
追加参照画像(複数指定可能) |
--no-default-refs |
プロジェクト参照画像を使わない |
--max-attempts |
最大リトライ回数(デフォルト: 10) |
7. ライブラリとして使う
from scripts.gcp_image_gen import (
generate_image,
generate_images,
default_character_refs,
build_duo_sticker_prompt,
load_stamps_yaml,
)
from pathlib import Path
# ---- 単発生成 ----
prompt = build_duo_sticker_prompt("Both celebrate with confetti 🎉")
img = generate_image(
prompt=prompt,
out_path=Path("output/celebration.png"),
aspect_ratio="4:3",
model="gemini-3.1-flash-image",
)
print(f"生成完了: {img.size}") # → 生成完了: (1024, 768)
# ---- バッチ生成(stamps.yaml から) ----
stamps = load_stamps_yaml("stamps/set01/prompts/stamps.yaml")
items = [
(sid, Path(f"output/set01/{sid}.png"), pose)
for sid, text, pose in stamps
]
generate_images(items, skip_existing=True, pace_seconds=5.0)
8. まとめ
| やりたいこと | 選択肢 |
|---|---|
| 最高品質の画像生成 | Gemini 3 Pro Image(Nano Banana Pro) |
| キャラクター一貫性 × 会話編集 | Gemini 3.1 Flash Image(Nano Banana 2) ← 今のベスト |
| 大量バッチ生成・コスト重視 | Gemini 3.1 Flash-Lite Image(Nano Banana 2 Lite) |
| 前世代の安定版 | Gemini 2.5 Flash Image(Nano Banana) |
| テキスト→画像 シンプル生成 | Imagen 4.0 系(ただし移行注意) |
gemini-3.1-flash-image は 2026年5月 GA、2027年5月まで提供保証済みで、
現時点での最もバランスの取れた画像生成モデルです。
参考リンク
9. 全コード:gcp_image_gen.py
コピーしてそのまま使えるフルバージョンです。
PROJECT_ID と参照画像のパスを自分の環境に合わせて書き換えてください。
#!/usr/bin/env python3
"""
Shared Vertex AI Gemini image generation.
Defaults:
- model: gemini-3.1-flash-image
- location: global ← このモデルは global のみ対応
- aspect_ratio: 4:3
Usage (library):
from gcp_image_gen import generate_image, default_character_refs, build_duo_sticker_prompt
path = generate_image(
prompt=build_duo_sticker_prompt("Both wave hello"),
out_path=Path("out.png"),
)
Usage (CLI):
python3 gcp_image_gen.py -o /tmp/test.png --pose "Both wave hello"
python3 gcp_image_gen.py -o /tmp/test.png --prompt "A photo of ..." --no-default-refs
"""
from __future__ import annotations
import argparse
import io
import time
from pathlib import Path
from typing import Iterable, Sequence
import yaml
from google import genai
from google.genai import types
from PIL import Image
PROJECT_ROOT = Path(__file__).resolve().parents[1]
PROMPTS_DIR = PROJECT_ROOT / "prompts"
DEFAULT_TEMPLATE_PATH = PROMPTS_DIR / "duo_sticker_base.txt"
# --- Vertex AI 設定 ---
DEFAULT_GCP_PROJECT = "your-gcp-project-id" # ← 自分のプロジェクトIDに変更
DEFAULT_LOCATION = "global" # gemini-3.1-flash-image は global のみ
DEFAULT_MODEL = "gemini-3.1-flash-image"
DEFAULT_ASPECT_RATIO = "4:3"
# キャラクター参照画像(自分のパスに合わせて変更)
REF_DUO_PORTRAIT = PROJECT_ROOT / "common-assets/refs/duo_portrait.png"
REF_DUO_LAND = PROJECT_ROOT / "common-assets/refs/duo_landscape_4x3.jpg"
REF_CHAR_A = PROJECT_ROOT / "common-assets/refs/charA_standing.jpg"
REF_CHAR_B = PROJECT_ROOT / "common-assets/refs/charB_standing.jpg"
# テンプレートファイルが存在しない場合のフォールバック
_FALLBACK_TEMPLATE = """Create a LINE sticker image in WIDE landscape {aspect_ratio} aspect ratio.
Pose / mood:
{pose}
Pure white background, no shadows, no text.
"""
def default_character_refs() -> list[Path]:
"""存在する参照画像だけをリストで返す(最大14枚まで渡せる)"""
candidates = [REF_DUO_PORTRAIT, REF_DUO_LAND, REF_CHAR_A, REF_CHAR_B]
return [p for p in candidates if p.exists()]
def make_client(
project: str = DEFAULT_GCP_PROJECT,
location: str = DEFAULT_LOCATION,
) -> genai.Client:
"""Vertex AI クライアントを生成する。認証は ADC を使用。"""
return genai.Client(vertexai=True, project=project, location=location)
def load_prompt_template(path: Path | str | None = None) -> str:
"""プロンプトテンプレートをファイルから読み込む。"""
p = Path(path) if path else DEFAULT_TEMPLATE_PATH
if p.exists():
return p.read_text(encoding="utf-8")
return _FALLBACK_TEMPLATE
def build_duo_sticker_prompt(
pose: str,
aspect_ratio: str = DEFAULT_ASPECT_RATIO,
*,
template_path: Path | str | None = None,
) -> str:
"""テンプレートにポーズとアスペクト比を埋め込んでプロンプトを生成する。"""
template = load_prompt_template(template_path)
return template.format(pose=pose.strip(), aspect_ratio=aspect_ratio)
def load_stamps_yaml(path: Path | str) -> list[tuple[str, str, str]]:
"""
stamps.yaml を読み込んで (id, text, pose) のリストを返す。
YAML の形式:
stamps:
- id: "01"
text: "おはよ〜"
pose: "Morning greeting..."
"""
p = Path(path)
data = yaml.safe_load(p.read_text(encoding="utf-8"))
if not data or "stamps" not in data:
raise ValueError(f"stamps: キーが見つかりません: {p}")
out: list[tuple[str, str, str]] = []
for row in data["stamps"]:
sid = str(row["id"]).zfill(2) if str(row["id"]).isdigit() else str(row["id"])
text = str(row["text"])
pose = str(row["pose"])
out.append((sid, text, pose))
return out
def _mime_for(path: Path) -> str:
"""ファイル拡張子から MIME タイプを返す。"""
ext = path.suffix.lower()
if ext in {".jpg", ".jpeg"}:
return "image/jpeg"
if ext == ".webp":
return "image/webp"
return "image/png"
def _extract_image(resp) -> Image.Image | None:
"""レスポンスから PIL Image を取り出す。"""
for cand in resp.candidates or []:
content = getattr(cand, "content", None)
if not content:
continue
for part in content.parts or []:
inline = getattr(part, "inline_data", None)
if inline and inline.data:
return Image.open(io.BytesIO(inline.data))
return None
def generate_image(
prompt: str,
out_path: Path | str | None = None,
*,
reference_images: Sequence[Path] | None = None,
use_default_refs: bool = True,
aspect_ratio: str = DEFAULT_ASPECT_RATIO,
model: str = DEFAULT_MODEL,
project: str = DEFAULT_GCP_PROJECT,
location: str = DEFAULT_LOCATION,
client: genai.Client | None = None,
max_attempts: int = 10,
response_modalities: Sequence[str] = ("TEXT", "IMAGE"),
verbose: bool = True,
) -> Image.Image:
"""
Vertex AI Gemini Image モデルで画像を1枚生成する。
- PIL Image を返す。out_path を指定すると PNG/JPEG で保存もする。
- 429 / RESOURCE_EXHAUSTED は指数バックオフでリトライする。
"""
out = Path(out_path) if out_path else None
# 参照画像リストを構築
refs: list[Path] = []
if use_default_refs:
refs.extend(default_character_refs())
if reference_images:
for p in reference_images:
pp = Path(p)
if pp not in refs:
refs.append(pp)
if client is None:
client = make_client(project=project, location=location)
# マルチモーダルコンテンツを構築(テキスト + 参照画像バイナリ)
contents: list = [prompt]
for p in refs:
if not p.exists():
if verbose:
print(f" WARN missing ref: {p}")
continue
contents.append(types.Part.from_bytes(data=p.read_bytes(), mime_type=_mime_for(p)))
if verbose:
try:
label = p.relative_to(PROJECT_ROOT)
except ValueError:
label = p.name
print(f" ref {label}")
config = types.GenerateContentConfig(
response_modalities=list(response_modalities),
image_config=types.ImageConfig(aspect_ratio=aspect_ratio),
)
# リトライループ
last_err: Exception | None = None
for attempt in range(1, max_attempts + 1):
try:
if verbose:
print(f" generate attempt {attempt}/{max_attempts} model={model} loc={location} ar={aspect_ratio}")
resp = client.models.generate_content(model=model, contents=contents, config=config)
im = _extract_image(resp)
if im is None:
raise RuntimeError("no image in response")
if out is not None:
out.parent.mkdir(parents=True, exist_ok=True)
im.save(out)
if verbose:
ratio = im.width / im.height if im.height else 0
dest = f" -> {out}" if out else ""
print(f" OK size={im.size} ratio={ratio:.3f}{dest}")
return im
except Exception as e:
last_err = e
msg = str(e)
# 429 はレートリミット → 長めに待つ
if "429" in msg or "RESOURCE_EXHAUSTED" in msg:
wait = min(90, 10 * attempt)
else:
wait = min(30, 3 * attempt)
if verbose:
print(f" retry wait={wait}s: {type(e).__name__}: {msg[:200]}")
time.sleep(wait)
raise RuntimeError(f"image generation failed after {max_attempts} attempts: {last_err}")
def generate_images(
items: Iterable[tuple[str, Path, str]],
*,
aspect_ratio: str = DEFAULT_ASPECT_RATIO,
model: str = DEFAULT_MODEL,
project: str = DEFAULT_GCP_PROJECT,
location: str = DEFAULT_LOCATION,
use_default_refs: bool = True,
reference_images: Sequence[Path] | None = None,
pace_seconds: float = 3.0,
skip_existing: bool = False,
verbose: bool = True,
) -> list[Path]:
"""
バッチ生成ヘルパー。
items: (ラベル, 出力パス, ポーズ or フルプロンプト) のイテラブル
- 短いポーズフラグメント → build_duo_sticker_prompt() でラップ
- 長文 or フルプロンプト → そのまま使用
"""
client = make_client(project=project, location=location)
done: list[Path] = []
for label, out_path, text in items:
out_path = Path(out_path)
if skip_existing and out_path.exists():
if verbose:
print(f"== skip existing {label} {out_path.name}")
done.append(out_path)
continue
if verbose:
print(f"== {label}")
# プロンプト判定:短いポーズはテンプレートに埋め込む
prompt = text
if "Create a LINE sticker" not in text and len(text) < 800:
prompt = build_duo_sticker_prompt(text, aspect_ratio=aspect_ratio)
generate_image(
prompt,
out_path,
reference_images=reference_images,
use_default_refs=use_default_refs,
aspect_ratio=aspect_ratio,
model=model,
project=project,
location=location,
client=client,
verbose=verbose,
)
done.append(out_path)
if pace_seconds > 0:
time.sleep(pace_seconds)
return done
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description="Vertex AI Gemini image generation")
ap.add_argument("-o", "--out", type=Path, required=True, help="出力画像パス")
ap.add_argument("--pose", type=str, default="", help="ポーズフラグメント(テンプレートに埋め込まれる)")
ap.add_argument("--prompt", type=str, default="", help="フルプロンプト(--pose より優先)")
ap.add_argument("--template", type=Path, default=DEFAULT_TEMPLATE_PATH, help="ベーステンプレートパス")
ap.add_argument("--aspect-ratio", default=DEFAULT_ASPECT_RATIO)
ap.add_argument("--model", default=DEFAULT_MODEL)
ap.add_argument("--project", default=DEFAULT_GCP_PROJECT)
ap.add_argument("--location", default=DEFAULT_LOCATION)
ap.add_argument("--ref", type=Path, action="append", default=[], help="追加参照画像(複数指定可)")
ap.add_argument("--no-default-refs", action="store_true", help="デフォルト参照画像を使わない")
ap.add_argument("--max-attempts", type=int, default=10)
args = ap.parse_args(argv)
if args.prompt:
prompt = args.prompt
elif args.pose:
prompt = build_duo_sticker_prompt(
args.pose, aspect_ratio=args.aspect_ratio, template_path=args.template
)
else:
ap.error("--pose または --prompt を指定してください")
generate_image(
prompt,
args.out,
reference_images=args.ref or None,
use_default_refs=not args.no_default_refs,
aspect_ratio=args.aspect_ratio,
model=args.model,
project=args.project,
location=args.location,
max_attempts=args.max_attempts,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())


コメント