Gemini CLIとVirtual Try on APIで試着アプリを開発してみた

記事タイトルとURLをコピーする

G-gen の奥田です。当記事では、Gemini CLI を利用した開発事例を紹介します。Google Cloud が提供するAPI である Virtual Try on API と、Web UI 用の Python フレームワークである Gradio を使用した、シンプルな画像生成 Web アプリの開発手順を紹介します。

はじめに

Gemini CLI

Gemini CLI とは、ターミナルから直接 Gemini の機能を利用できるオープンソースのコマンドラインインターフェイスです。

詳しくは以下の記事をご覧ください。

blog.g-gen.co.jp

なお、Gemini CLI におけるコマンドの紹介や開発事例については以下の記事をご覧ください。

blog.g-gen.co.jp

Virtual Try on API

Virtual Try on API とは、試着対象の人の画像と、試着したい衣服の画像を入力することにより、着せ替えた写真を出力できる Vertex AI の API です。同機能は2025年8月現在、Preview 公開です。

人物画像と衣服の画像を Base64 エンコードしてリクエストに含ませることで、Cloud Storage バケットに画像を出力できます。使用には、Vertex AI を有効化した Google Cloud プロジェクトが必要です。

この API と同様の技術を活用した機能として、Google は2025年5月の Google I/O で「Try It On」(バーチャル試着)機能を Search Labs 内で公開しました。

Gradio

Gradio は、機械学習 Web アプリを構築するための Python フレームワークです。

Gemini CLI を用いたアプリの開発

初期のディレクトリ構成

まず、アプリケーション用に新しいディレクトリを作成します。

新しいディレクトリを作ることを推奨する理由は、環境や設定を他のプロジェクトと混ざらないように安全に管理するためです。

意図しない挙動を防ぐため、空のディレクトリに公式ドキュメントに記載の Colab ソースコードを保存し、次の手順で Gemini CLI のコンテキストとして与えます。

try-app
|-- virtual_try_on.py
|-- .env

Gemini CLI による開発

Gemini CLI を起動し、以下のように初回プロンプトを記入しました。

こちらの Virtual Try-On API を試せる Gradio アプリを作成してください。Cloud Run へのデプロイを想定します。
ソースコードは`virtual_try_on.py`を参考にしてください。
処理の流れは以下のとおりです。
    1.  人物の画像を、事前に定義した特定の Cloud Storage バケットへアップロードする。
    2.  服の画像を、事前に定義した特定の Cloud Storage バケットへアップロードする。
    3.  上記 2 つの画像のパスを使ってリクエストを送信し、Virtual Try-On API を実行する。
    4.  生成された画像を Gradio アプリ上で表示する。ダウンロードボタンを押すとローカルにダウンロードできる。ダウンロード形式は JPEG とする。

これにより、以下のファイルが生成されました。

try-app
|-- app.py
|-- requirements.txt
|-- README.md
|-- Dockerfile

Cloud Storage バケットの作成

人物画像及び服の画像を格納する Cloud Storage バケットを作成します。

export BUCKET_NAME="risa-test-cloths"
export PROJECT_ID="my-project"
export REGION="us-central1"
  
gsutil mb -p $PROJECT_ID -l $REGION gs://$BUCKET_NAME

エラー対応

app.py を起動すると以下のエラーが発生しました。

Traceback (most recent call last):
  File "/Users/r-risa/virtual_try/app.py", line 10, in <module>
    from google import genai
ImportError: cannot import name 'genai' from 'google' (unknown location)

生成された README.mdを参照すると、現在は非推奨のgoogle-generativeai がパッケージに含まれていました。

gradio
google-generativeai #非推奨のパッケージ
google-cloud-storage
Pillow

そのため、現推奨のgoogle-genaiパッケージに変更しました。

gradio
google-cloud-storage
Pillow
google-genai #現推奨のパッケージに変更

ローカルでテスト

環境変数をエクスポートした後、python app.py を実行してローカルでテストします。

左上に人物の画像をアップロードし、左下に服の画像をアップロードします。

左下のオレンジ色のボタン「試着画像を生成する」をクリックすると、数秒で着せ替えた写真が出力されます。ダウンロードボタンを押下することで、ローカルにダウンロードできます。

Cloud Run にデプロイ

環境変数をエクスポートした後、以下のコマンドで Cloud Run にデプロイします。

当記事では検証環境のため未認証の呼び出しを可能としていますが、必要に応じて認証の設定をしてください。

gcloud run deploy $IMAGE_NAME \
  --source . \
  --image $IMAGE_TAG \
  --region $REGION \
  --service-account $SERVICE_ACCOUNT_EMAIL \
  --set-env-vars="GCP_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GCS_BUCKET_NAME=${BUCKET_NAME}" \
  --set-env-vars="GCP_REGION=${REGION}" \
  --allow-unauthenticated \
  --platform managed

生成されたソースコード

参考までに、最終的に生成されたソースコードを記載します。

requirements.txt

gradio
google-cloud-storage
Pillow
google-genai

app.py

import os
import uuid
import tempfile
from datetime import datetime
  
import gradio as gr
from PIL import Image as PIL_Image
from google.cloud import storage
from google import genai
from google.genai.types import Image, ProductImage, RecontextImageSource, RecontextImageConfig
  
  
# --- 環境変数の設定 ---
# Google CloudプロジェクトIDとロケーションを設定してください。
# Cloud Run環境では、環境変数として設定することを推奨します。
PROJECT_ID = os.environ.get("GCP_PROJECT")
LOCATION = os.environ.get("GCP_REGION", "us-central1")
  
# 画像をアップロードするGCSバケット名を設定してください。
GCS_BUCKET_NAME = os.environ.get("GCS_BUCKET_NAME")
  
# --- Google Cloud クライアントの初期化 ---
client = None
storage_client = None
try:
    # Vertex AI用のクライアントを初期化
    client = genai.Client(vertexai=True, project=PROJECT_ID, location=LOCATION)
    storage_client = storage.Client()
    print("Google Cloud クライアントの初期化に成功しました。")
except Exception as e:
    print(f"Google Cloud クライアントの初期化に失敗しました: {e}")
    print("環境変数 GCP_PROJECT と BUCKET_NAME が正しく設定されているか確認してください。")
  
  
# --- 定数 ---
VIRTUAL_TRY_ON_MODEL = "virtual-try-on-preview-08-04"
  
  
def upload_to_gcs(pil_image: PIL_Image.Image, file_name: str) -> str:
    """PillowイメージをGCSにアップロードし、GCS URIを返す"""
    if not storage_client or not GCS_BUCKET_NAME:
        raise gr.Error("GCSクライアントが初期化されていないか、バケット名が設定されていません。")
  
    bucket = storage_client.bucket(GCS_BUCKET_NAME)
      
    # Pillowイメージをバイトデータに変換
    with tempfile.SpooledTemporaryFile() as temp_byte_io:
        pil_image.save(temp_byte_io, "PNG")
        temp_byte_io.seek(0)

        blob = bucket.blob(file_name)
        blob.upload_from_file(temp_byte_io, content_type="image/png")
    
    return f"gs://{GCS_BUCKET_NAME}/{file_name}"
  
  
def virtual_try_on(person_pil_image: PIL_Image.Image, clothing_pil_image: PIL_Image.Image):
    """Virtual Try-On APIを実行して試着画像を生成する"""
    if not client:
        raise gr.Error("Vertex AIクライアントが初期化されていません。アプリケーションのログを確認してください。")
    if not person_pil_image or not clothing_pil_image:
        raise gr.Error("人物画像と服の画像を両方アップロードしてください。")
  
    try:
        # ファイル名を一意にするためにUUIDとタイムスタンプを使用
        timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
        unique_id = uuid.uuid4().hex[:6]
         
        person_filename = f"person-{timestamp}-{unique_id}.png"
        clothing_filename = f"clothing-{timestamp}-{unique_id}.png"
  
        # GCSに画像をアップロード
        person_gcs_uri = upload_to_gcs(person_pil_image, person_filename)
        clothing_gcs_uri = upload_to_gcs(clothing_pil_image, clothing_filename)
  
        # Virtual Try-On APIを呼び出し
        response = client.models.recontext_image(
            model=VIRTUAL_TRY_ON_MODEL,
            source=RecontextImageSource(
                person_image=Image(gcs_uri=person_gcs_uri),
                product_images=[
                    ProductImage(product_image=Image(gcs_uri=clothing_gcs_uri))
                ],
            ),
            config=RecontextImageConfig(
                base_steps=32,
                number_of_images=1,
                safety_filter_level="BLOCK_LOW_AND_ABOVE",
                person_generation="ALLOW_ADULT",
            ),
        )
  
        generated_pil_image = response.generated_images[0].image._pil_image
  
        # ダウンロード用にJPEG形式で一時ファイルに保存
        with tempfile.NamedTemporaryFile(delete=False, suffix=".jpeg") as temp_file:
            generated_pil_image.convert("RGB").save(temp_file, "jpeg")
            temp_file_path = temp_file.name
  
        return generated_pil_image, temp_file_path
  
    except Exception as e:
        print(f"エラーが発生しました: {e}")
        raise gr.Error(f"画像の生成に失敗しました。詳細: {e}")
  
  
# --- Gradioインターフェースの定義 ---
with gr.Blocks() as demo:
    gr.Markdown("# Virtual Try-On アプリケーション")
    gr.Markdown(
        "人物の画像と服の画像をアップロードすると、AIがその服を着用した画像を作成します。"
        "生成された画像は、表示後にダウンロードできます。"
    )
      
    with gr.Row():
        with gr.Column():
            person_image = gr.Image(type="pil", label="人物の画像")
            clothing_image = gr.Image(type="pil", label="服の画像")
            submit_btn = gr.Button("試着画像を生成する", variant="primary")
        
        with gr.Column():
            output_image = gr.Image(type="pil", label="生成された画像")
            download_file = gr.File(label="画像をダウンロード")
  
    submit_btn.click(
        fn=virtual_try_on,
        inputs=[person_image, clothing_image],
        outputs=[output_image, download_file]
    )
  
  
if __name__ == "__main__":
    # Cloud RunではPORT環境変数が設定される
    server_port = int(os.environ.get("PORT", 8080))
    demo.launch(server_name="0.0.0.0", server_port=server_port)

Dockerfile

# Python 3.10をベースイメージとして使用
FROM python:3.10-slim
  
# 作業ディレクトリを設定
WORKDIR /app
  
# 依存関係をインストールするためにrequirements.txtをコピー
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
  
# アプリケーションコードをコピー
COPY . .
  
# Gradioアプリを起動
# Cloud RunはPORT環境変数でリッスンするポートを指定します
ENV PORT 8080
CMD ["python", "app.py"]

Risa(記事一覧)

クラウドソリューション部クラウドデベロッパー課
Google Cloudの可能性に惹かれ、2024年4月G-genにジョイン。
Google Cloud Partner Top Engineer 2025
Google Cloud 11 資格保有。日々修行中です!