Amazon-Bedrock

Amazon Nova Canvasで画像生成してみた

はじめに

こんにちは、ディーネットの山田です。

JAWS DAYS 2025 のお祭りトラックにある「JAWS DAYS 2025 生成AI 絵画展」へ応募するにあたって、Amazon Nova Canvas で画像を生成させてみたという小ネタです。

※本ブログで紹介する内容は、絵画展に応募した「プロンプト」や「画像」とは異なります。

Amazon Novaとは

AWS re:Invent 2024 で発表されたアマゾン独自の大規模基盤モデルです。

その中でも画像生成に特化したモデルが、「Amazon Nova Canvas」になります。

早速試してみた

プレイグラウンドを使って遊んでみた

プロンプト

anime style illustration of Osaka Castle, historical and majestic, with rich green surroundings, clear blue sky, traditional Japanese elements, detailed architecture, and harmonious cityscape

(日本語:豊かな緑の環境、澄んだ青い空、伝統的な日本の要素、緻密な建築、そして調和のとれた街並みを持つ、歴史的で雄大な大阪城のアニメ風イラスト)

負のプロンプト

realistic photo, dull colors, low detail, modern buildings, night view, disjointed elements

(日本語:リアルな写真、くすんだ色、低ディテール、現代の建物、夜景、ばらばらの要素)

その他のパラメータ

応答画像 プロンプト強度 シード
2048 x 2048 (1:1) 8 0

生成結果

外堀を含めた、大阪城の画像が生成されました。

Python3のboto3からも遊んでみた

ソースコード

import boto3
import json
import base64
from pathlib import Path
from botocore.exceptions import ClientError
from dataclasses import dataclass
from typing import Optional

# 設定クラス
@dataclass
class ImageGenerationConfig:
    height: int = 2048
    width: int = 2048
    number_of_images: int = 1
    cfg_scale: int = 8
    seed: int = 0

# 定数
DEFAULT_REGION = "us-east-1"
MODEL_ID = "amazon.nova-canvas-v1:0"
OUTPUT_FILENAME = "generated_image.png"

class ImageGenerator:
    def __init__(self, region: str = DEFAULT_REGION):
        """
        画像生成クラスの初期化
        Args:
            region (str): AWS リージョン
        """
        self.client = boto3.client("bedrock-runtime", region_name=region)
        self.config = ImageGenerationConfig()

    def _create_request_body(self, prompt: str, negative_prompt: Optional[str] = None) -> str:
        """
        リクエストボディの作成
        Args:
            prompt (str): 画像生成のプロンプト
            negative_prompt (str, optional): 負のプロンプト
        Returns:
            str: JSON形式のリクエストボディ
        """
        text_to_image_params = {
            "text": prompt
        }

        # 負のプロンプトが指定されている場合は追加
        if negative_prompt:
            text_to_image_params["negativeText"] = negative_prompt

        body = {
            "textToImageParams": text_to_image_params,
            "taskType": "TEXT_IMAGE",
            "imageGenerationConfig": {
                "cfgScale": self.config.cfg_scale,
                "seed": self.config.seed,
                "width": self.config.width,
                "height": self.config.height,
                "numberOfImages": self.config.number_of_images,
            }
        }
        return json.dumps(body, ensure_ascii=False)

    def generate_image(self, 
                      prompt: str, 
                      negative_prompt: Optional[str] = None, 
                      output_path: Optional[str] = None) -> bool:
        """
        画像を生成して保存
        Args:
            prompt (str): 画像生成のプロンプト
            negative_prompt (str, optional): 負のプロンプト
            output_path (str, optional): 出力ファイルパス
        Returns:
            bool: 生成成功時True
        """
        output_path = output_path or OUTPUT_FILENAME

        try:
            print(f"画像生成を開始:")
            print(f"プロンプト:「{prompt}」")
            if negative_prompt:
                print(f"負のプロンプト:「{negative_prompt}」")

            print(self._create_request_body(prompt, negative_prompt))

            # モデル呼び出し
            response = self.client.invoke_model(
                body=self._create_request_body(prompt, negative_prompt),
                modelId=MODEL_ID,
                accept="application/json",
                contentType="application/json"
            )

            # レスポンス処理
            response_body = json.loads(response.get("body").read())
            base64_image = response_body.get("images")[0]
            image_bytes = base64.b64decode(base64_image.encode('ascii'))

            # 画像保存
            output_file = Path(output_path)
            output_file.parent.mkdir(parents=True, exist_ok=True)

            with open(output_file, 'wb') as f:
                f.write(image_bytes)

            print(f"画像を保存しました: {output_file}")
            return True

        except ClientError as e:
            print(f"AWS API エラー: {e}")
            return False
        except Exception as e:
            print(f"予期せぬエラー: {e}")
            return False

def main():
    # 使用例
    generator = ImageGenerator()

    # プロンプトと負のプロンプトを設定
    prompt = "anime style illustration of Osaka Castle, historical and majestic, with rich green surroundings, clear blue sky, traditional Japanese elements, detailed architecture, and harmonious cityscape"
    negative_prompt = "realistic photo, dull colors, low detail, modern buildings, night view, disjointed elements"

    # 画像生成
    generator.generate_image(
        prompt=prompt,
        negative_prompt=negative_prompt,
        output_path="output/generate_image.png"
    )

if __name__ == "__main__":
    main()

おまけ

出力される画像のサイズ含め、生成に使われるパラメータですので、画像サイズを変えるだけでテイストは同じだが、雰囲気が異なる画像が生成されました。

2048 x 2048 のサイズで生成を依頼

1024 x 4096 のサイズで生成を依頼

備考

JAWS DAYS 2025 当日は、大阪から現地(東京、池袋)へ駆けつけます。

当日お会いできたらうれしいです。ではまたー(´∀`)ノシ

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA