Google Nano Bananaを呼び出して画像を切り出す方法

この記事では、実際の実行可能な Python ソース コードに基づいて、Google Nano Banana を呼び出して、完全なソース コードを保持したまま製品画像を切り取る方法を紹介します。

この記事では、実用的な Python スクリプトを使用して、Google の Nano Banana 画像編集機能を呼び出して商品画像を切り出す方法を示します。

この現在の実装の目標は明確です。

  • カタログから製品画像を読み取る
  • Google 画像モデルを呼び出して背景の削除を実行します。
  • 返された画像に対してローカルの透明な背景のクリーニングを再度実行します
  • 最終出力は透明な下部 PNG

白い背景の製品画像、ヘッドフォンの画像、および配線図面のバッチがすでにあり、電子商取引で使用できる透明な背景画像をすばやく生成したい場合、この方法は非常に簡単です。

このコードは何をするのか

このスクリプトは主に 4 つの部分に分かれています。

  1. 「背景を削除し、被写体を保持し、影を追加しない」ことをモデルに知らせるプロンプト ワードを定義します。
  2. google-genai の画像生成インターフェイスを呼び出します。
  3. モデル応答から画像結果を抽出する
  4. 次に、ローカル ロジックを使用してエッジの明るい背景を透明に変換し、残留エッジを減らします。

つまり、単にモデルに画像を投げただけでは終わりではなく、「モデル編集+ローカル後処理」を繋ぎ合わせて完成するのです。

走る前の準備

最初に依存関係をインストールします。

1
.\.venv\Scripts\python.exe -m pip install google-genai pillow

GEMINI_API_KEYの取得方法

GEMINI_API_KEY は、Gemini API を呼び出すときに使用されるキーです。 Google の公式クイックスタートによると、キーをまだ持っていない場合は、Google AI Studio で直接作成できます。

入手手順は以下の通りです。

  1. Google AIスタジオを開きます。
  2. Google アカウントにサインインします。
  3. Get API key または API keys ページを見つけます。
  4. 新しい API キーを作成します。
  5. 生成されたキーをコピーします。
  6. スクリプトが読み取ることができるように、これをローカル環境変数に構成します。

ページ上に使用可能なプロジェクトがない場合は、通常、最初にプロジェクトの初期化を完了してから、[API キー] ページに戻ってキーを作成する必要があります。

キーを取得したら、環境変数を構成します。

1
$env:GEMINI_API_KEY="your_api_key"

cmd を使用している場合は、次のように記述できます。

1
set GEMINI_API_KEY=your_api_key

GEMINI_API_KEYGOOGLE_API_KEY を同時に設定した場合、実際の動作では通常 GOOGLE_API_KEY が最初に読み込まれるため、混乱を避けるために 1 つだけを保持することをお勧めします。

ディレクトリ構造の例

スクリプトは 2 つのパラメータを受け取ります。

  • input_dir: 画像ディレクトリに入る
  • output_dir: 出力画像ディレクトリ

例えば:

1
2
3
4
5
images/
  product1.jpg
  product2.png

output/

走り方

スクリプトファイル名がcutout.pyで、実行方法が次のとおりであるとします。

1
.\.venv\Scripts\python.exe .\cutout.py .\images .\output

モデルを変更したい場合は、パラメーターを明示的に渡すこともできます。

1
.\.venv\Scripts\python.exe .\cutout.py .\images .\output --model gemini-2.5-flash-image

スクリプトは、入力ディレクトリ内のファイルを次の形式でループします。

  • .jpg
  • .jpeg
  • .png
  • .webp

処理が完了すると、同じ名前の透過的な PNG ファイルが出力ディレクトリに生成されます。

コア呼び出しプロセス

実際に Google Nano Banana を呼び出すキーコードは次のとおりです。

1
2
3
4
response = client.models.generate_content(
    model=model,
    contents=[PROMPT, image],
)

ここでは 2 つのコンテンツが渡されます。

  • テキスト プロンプトの単語 PROMPT
  • ワンピース PIL.Image

プロンプトの内容は、製品画像全体の背景を削除し、本体のみを残し、いくつかの点を強調するようモデルに依頼することです。

  • 完全な製品を保管する
  • 細いワイヤーとケーブルの詳細を保持
  • 内部の空洞と環状領域をきれいにします
  • 新しいオブジェクトを追加しないでください
  • 影を追加しないでください

この種のプロンプトワードは、カットアウトの品質、特にヘッドフォンケーブル、透明なエッジ、中空領域などの細部に大きな影響を与えます。

なぜローカルで後処理を行う必要があるのでしょうか?

モデルが結果を返した後、スクリプトは直接保存されませんが、make_transparent_from_borders(image) が再度実行されます。

このステップの考え方は次のとおりです。

  • 画像の周囲のエッジから始まる明るい背景ピクセルを見つけます
  • 幅優先検索を使用して、接続されているすべての明るい色の領域をマークします。
  • 最後に、これらの領域を透明に変更します。

これを行う利点は、残っている白いエッジ、明るい灰色の背景、および不十分にきれいなエッジ領域をさらに除去できることです。

「背景かどうか」の判断条件は以下の通りです。

1
2
3
4
def is_light_background_pixel(r: int, g: int, b: int) -> bool:
    brightness = (r + g + b) / 3
    spread = max(r, g, b) - min(r, g, b)
    return brightness >= 170 and spread <= 35

簡単な理解は次のとおりです。

  • 全体的に色が明るいので
  • 3 つの RGB チャネルの差が大きすぎることはありません

商品画像の背景が白地やライトグレー地、単色に近いものを加工する場合に適しています。

完全なソースコード

現在の完全なソース コードは、直接の再利用や二次的な変更を容易にするために以下に保持されています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from __future__ import annotations

import argparse
import os
from pathlib import Path
from collections import deque

from PIL import Image

try:
    from google import genai
except ImportError as exc:  # pragma: no cover
    raise SystemExit(
        "Missing dependency: google-genai. Install it with "
        r"'.\.venv\Scripts\python.exe -m pip install google-genai'."
    ) from exc


PROMPT = (
    "Remove the entire background from this product photo and return only the product "
    "on a fully transparent background as a PNG. Keep the full product intact, preserve "
    "thin cable details, clean the inner loops and holes, and do not add any new objects "
    "or shadows."
)


def is_light_background_pixel(r: int, g: int, b: int) -> bool:
    brightness = (r + g + b) / 3
    spread = max(r, g, b) - min(r, g, b)
    return brightness >= 170 and spread <= 35


def to_pil_image(image_obj) -> Image.Image:
    if isinstance(image_obj, Image.Image):
        return image_obj
    pil_image = getattr(image_obj, "_pil_image", None)
    if isinstance(pil_image, Image.Image):
        return pil_image
    as_pil = getattr(image_obj, "pil_image", None)
    if isinstance(as_pil, Image.Image):
        return as_pil
    raise TypeError(f"Unsupported image object type: {type(image_obj)!r}")


def make_transparent_from_borders(image: Image.Image) -> Image.Image:
    rgba = image.convert("RGBA")
    width, height = rgba.size
    pixels = rgba.load()

    visited: set[tuple[int, int]] = set()
    queue: deque[tuple[int, int]] = deque()

    def push_if_bg(x: int, y: int) -> None:
        if (x, y) in visited:
            return
        r, g, b, _ = pixels[x, y]
        if is_light_background_pixel(r, g, b):
            visited.add((x, y))
            queue.append((x, y))

    for x in range(width):
        push_if_bg(x, 0)
        push_if_bg(x, height - 1)
    for y in range(height):
        push_if_bg(0, y)
        push_if_bg(width - 1, y)

    while queue:
        x, y = queue.popleft()
        for nx, ny in ((x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)):
            if 0 <= nx < width and 0 <= ny < height:
                push_if_bg(nx, ny)

    for x, y in visited:
        pixels[x, y] = (0, 0, 0, 0)

    return rgba


def save_first_image_part(response, dst: Path) -> None:
    parts = getattr(response, "parts", None)
    if parts is None and getattr(response, "candidates", None):
        parts = response.candidates[0].content.parts

    if not parts:
        raise RuntimeError("Model returned no content parts.")

    for part in parts:
        inline_data = getattr(part, "inline_data", None)
        if inline_data is None and isinstance(part, dict):
            inline_data = part.get("inline_data")

        if inline_data is None:
            continue

        if hasattr(part, "as_image"):
            image = to_pil_image(part.as_image())
            dst.parent.mkdir(parents=True, exist_ok=True)
            make_transparent_from_borders(image).save(dst)
            return

        data = getattr(inline_data, "data", None)
        mime_type = getattr(inline_data, "mime_type", "")
        if data:
            dst.parent.mkdir(parents=True, exist_ok=True)
            with open(dst, "wb") as handle:
                handle.write(data)
            with Image.open(dst) as img:
                processed = make_transparent_from_borders(img)
                processed.save(dst.with_suffix(".png"))
            if dst.suffix.lower() != ".png":
                dst.unlink(missing_ok=True)
            return

    raise RuntimeError("Model returned text only and no edited image.")


def process_image(src: Path, dst: Path, client, model: str) -> None:
    with Image.open(src).convert("RGBA") as image:
        response = client.models.generate_content(
            model=model,
            contents=[PROMPT, image],
        )
    save_first_image_part(response, dst)


def main() -> None:
    parser = argparse.ArgumentParser(description="Use Nano Banana / Gemini image editing to cut out product images.")
    parser.add_argument("input_dir", type=Path)
    parser.add_argument("output_dir", type=Path)
    parser.add_argument("--model", default="gemini-2.5-flash-image")
    args = parser.parse_args()

    api_key = os.environ.get("GEMINI_API_KEY")
    if not api_key:
        raise SystemExit("Missing GEMINI_API_KEY environment variable.")

    client = genai.Client(api_key=api_key)
    exts = {".jpg", ".jpeg", ".png", ".webp"}

    for src in sorted(args.input_dir.iterdir()):
        if not src.is_file() or src.suffix.lower() not in exts:
            continue
        dst = args.output_dir / f"{src.stem}.png"
        process_image(src, dst, client, args.model)
        print(dst)


if __name__ == "__main__":
    main()

最適化を続けるのに適した場所

このスクリプトを大量生産に引き続き使用する予定がある場合は、後でこれらの機能を追加し続けることができます。

  • 単一の画像エラーが報告された後にバッチ全体が中断されるのを避けるために、失敗の再試行が追加されました。
  • ログを記録して、処理に失敗した画像を特定しやすくします
  • さまざまなバックグラウンドしきい値に合わせて構成可能
  • サブディレクトリの再帰的スキャンをサポート
  • 元の画像と結果画像の比較プレビューを追加しました

まとめ

「Google Nano Banana を呼び出して画像を切り出す方法」をすぐに理解したい場合は、実際には 3 つの主要な手順があります。

  1. google-genai および Pillow をインストールする
  2. GEMINI_API_KEY を設定します
  3. client.models.generate_content() を使用してプロンプトの言葉と画像を渡します

このコードの価値は、モデルを呼び出すだけでなく、製品画像の切り抜きタスクで直接使用するのに適した透明な背景の後処理も追加することです。

记录并分享
Hugo で構築されています。
テーマ StackJimmy によって設計されています。