🍌

意地でもNano-Bananaに縦長/横長の画像を生成させる

に公開

この記事を読んだらできること


はじめまして、ふっきーです。
Googleの画像生成AIのnano-bananaすごいですよね 一貫性の高さが異次元で、怖いくらいです
ですが、一つだけ不満があります
※本当はもっとある

それは 「正方形(1024x1024)しか対応していない」 ということです。

動画制作やWebデザインでは16:9(横長)や9:16(縦長)の画像が必要になることが多いのですが、後からクロップすると構図が崩れたり、重要な部分が切れてしまったりします。公式のドキュメントの中にアスペクト比をプロンプトで指定する的な記載を見た覚えがあって試したのですが、うまくいきませんでした。

Nano-Bananaの画像の参照精度の高さを活かした方法を思い浮かんで試してみたらいい感じだったので、1920x1080/1080x1920サイズの画像を生成する手法を共有します。
※おそらくほかのサイズでも利用可能です

どうやったか

見ればわかる

これが

こうなって

こうなる

説明

正方形のAI画像生成に対して 生成したいサイズの緑色の領域をマスクとして使用 し、緑領域にのみ画像を生成させます

基本的な仕組み

  1. 緑マスク画像を準備:1024x1024の画像で、生成したい領域を緑色(RGB: 0,255,0)、それ以外を黒色で塗りつぶす
  2. 参照画像として指定:AI画像生成時に緑マスク画像を参照画像として渡す
  3. プロンプトで指示:「緑の領域にのみ画像を生成し、黒い部分は絶対に触らないで」と明確に指示
  4. クロップ→リサイズ:Nano-Banaによる画像生成後、画処理で黒背景領域を切り取るようにクロップし、目的のサイズにリサイズ

APIベース(Python)での実装

#!/usr/bin/env python3
"""
nano-banana緑マスク手法による縦横比対応画像生成スクリプト
"""
import os
import time
import google.generativeai as genai
import numpy as np
from PIL import Image
from dotenv import load_dotenv

# 環境変数読み込み
load_dotenv()

class SimpleAspectRatioGenerator:
    def __init__(self):
        # Google AI API設定
        api_key = os.getenv('GOOGLE_AI_API_KEY')
        if not api_key:
            raise ValueError("GOOGLE_AI_API_KEY環境変数を設定してください")

        genai.configure(api_key=api_key)
        self.model = genai.GenerativeModel('gemini-2.5-flash-image-preview')

        # 緑マスクを自動生成
        self._create_masks()

    def _create_masks(self):
        """緑マスク画像を自動生成"""
        # 16:9マスク
        mask_16x9 = np.zeros((1024, 1024, 3), dtype=np.uint8)
        crop_height = int(1024 / (16/9))  # 576
        crop_y = (1024 - crop_height) // 2  # 224
        mask_16x9[crop_y:crop_y + crop_height, :] = [0, 255, 0]
        Image.fromarray(mask_16x9).save("green_mask_16x9.png")

        # 9:16マスク
        mask_9x16 = np.zeros((1024, 1024, 3), dtype=np.uint8)
        crop_width = int(1024 * (9/16))  # 576
        crop_x = (1024 - crop_width) // 2  # 224
        mask_9x16[:, crop_x:crop_x + crop_width] = [0, 255, 0]
        Image.fromarray(mask_9x16).save("green_mask_9x16.png")

    def generate(self, prompt: str, aspect_ratio: str = "16:9") -> str:
        """
        ポートレート画像を生成

        Args:
            prompt: 生成したい画像の説明
            aspect_ratio: "16:9" または "9:16"

        Returns:
            生成された画像のパス
        """
        if aspect_ratio not in ["16:9", "9:16"]:
            raise ValueError("aspect_ratioは '16:9' または '9:16' を指定してください")

        # マスクファイルパス
        mask_file = f"green_mask_{aspect_ratio.replace(':', 'x')}.png"

        # 出力サイズ設定
        output_size = (1920, 1080) if aspect_ratio == "16:9" else (1080, 1920)

        # 完全なプロンプト構築
        full_prompt = f"""MASK INSTRUCTIONS:
1. Reference image contains GREEN regions (RGB: 0,255,0) and BLACK regions
2. Generate content ONLY within the GREEN areas
3. BLACK areas must remain absolutely untouched and pure black
4. Use the green region as a chromakey mask for content placement

{prompt}

MASKING REQUIREMENTS:
- Treat GREEN pixels as content area (like greenscreen)
- Keep BLACK pixels completely black (RGB: 0,0,0)
- Content should fit perfectly within green boundaries
- No bleeding or overflow outside green regions"""

        print(f"Generating {aspect_ratio} image: {prompt[:50]}...")

        # 画像生成
        mask_image = genai.upload_file(mask_file)
        response = self.model.generate_content([
            full_prompt,
            mask_image
        ])

        # レスポンスから画像を保存
        timestamp = int(time.time())
        temp_file = f"temp_generated_{timestamp}.png"

        # 実際のレスポンス処理
        if hasattr(response, 'parts') and response.parts:
            for part in response.parts:
                if hasattr(part, 'inline_data'):
                    image_data = part.inline_data.data
                    with open(temp_file, 'wb') as f:
                        f.write(image_data)
                    break
        else:
            raise Exception("画像生成に失敗しました")

        # クロップ・リサイズ
        final_file = self._crop_and_resize(temp_file, aspect_ratio, output_size)

        # 一時ファイル削除
        if os.path.exists(temp_file):
            os.remove(temp_file)

        print(f"Generated: {final_file}")
        return final_file

    def _crop_and_resize(self, image_path: str, aspect_ratio: str, output_size: tuple) -> str:
        """画像をクロップ・リサイズ"""
        img = Image.open(image_path)

        # 1024x1024にリサイズ
        if img.size != (1024, 1024):
            img = img.resize((1024, 1024), Image.LANCZOS)

        # アスペクト比に応じてクロップ
        if aspect_ratio == "16:9":
            crop_height = int(1024 / (16/9))
            crop_y = (1024 - crop_height) // 2
            img = img.crop((0, crop_y, 1024, crop_y + crop_height))
        else:  # 9:16
            crop_width = int(1024 * (9/16))
            crop_x = (1024 - crop_width) // 2
            img = img.crop((crop_x, 0, crop_x + crop_width, 1024))

        # 最終サイズにリサイズ
        img = img.resize(output_size, Image.LANCZOS)

        # 保存
        output_file = f"image_{aspect_ratio.replace(':', 'x')}.png"
        img.save(output_file, 'PNG')

        return output_file

# 使用例
if __name__ == "__main__":
    generator = SimpleAspectRatioGenerator()

    # 横長画像生成(横構図が重要な例)
    result = generator.generate("three people sitting side by side on a park bench, wide shot", "16:9")
    print(f"横長画像生成完了: {result}")

    # 縦長画像生成(縦構図が重要な例)
    result = generator.generate("tall lighthouse tower against blue sky", "9:16")
    print(f"縦長画像生成完了: {result}")

使用方法

  1. 環境設定

    pip install google-generativeai pillow python-dotenv numpy
    
  2. .envファイル作成

    GOOGLE_AI_API_KEY=your_api_key_here
    
  3. 実行

    python simple_aspect_generator.py
    

出力例(横長Ver)

まとめ

正方形しか対応していないNano-Bananaでも、理想的なアスペクト比の画像を生成できるようになります。
この技法は様々な用途に応用できそうです

  • YouTube動画用サムネイル(16:9)
  • スマホアプリの背景画像(9:16)

もし同じような課題を抱えている方がいたら、ぜひ試してみてください。

Discussion