Amazon Bedrockで画像生成&S3保存するPythonコードを書く

## はじめに
画像生成AIで絵を作成するのが好きなので、Amazon Bedrock(以下Bedrock)でStable Diffusion XL 1.0を使ってみました。

作りたいイメージは頭の中にあるのですが、それをちゃんと出力できるプロンプト考えるのが結構大変なので、チャット形式でざっくりイメージを伝える -> よしなにプロンプト組み立て -> 画像生成して返してくれる
といったアプリを作れればいいなあと思ってます。

今回はAWS Lambda(以下Lambda)からStable Diffusion XL 1.0のAPIを叩いて、作成された画像をS3に格納する部分を作りました。

Bedrockは、Meta、Stability AI、Amazonなどが提供する基盤モデル(生成AIの一種)を利用できるフルマネージド型サービスです。マネジメントコンソール上のPlaygroundsで提供されているモデルを試してみることもできます。

https://aws.amazon.com/jp/bedrock/

## 1. 構成

完成系はこんなイメージです。
Amazon API Gateway経由でLambdaを実行し、BedrockのStable Diffusion XL 1.0のAPIを叩いて、実行結果(base64文字列)をデコードしてS3に保存します。
![Bedrock_StableAI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/1c666719-77cc-a74a-26c9-804c2f92c088.png)


## 2. 構築手順

### 2.1 Bedrockで利用するAIモデルを申請する

Bedrockで基盤モデルを利用するために、まず最初にモデルへのアクセス申請をする必要があります。

Overviewから"Request Model Access"をクリック
![Bedrock_Overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/bfb78b6c-3555-0ac9-363e-fe4096265003.png)

"Manage model access"をクリック
![ModelAccess.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/ae9c4c0b-baae-02ce-d2e0-6ebbe6c31ede.png)

利用したいモデルにチェックを入れ、"Request model access"ボタンを押す
![RequestModelAccess2.png.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/083de914-c150-6e02-8016-fde507c4c07c.jpeg)

自分が申請したときは数分掛かったか掛からないかくらいで使えるようになりました。

### 2.2 Lambdaを書く

モデルを利用できるようになったらStable Diffusion XL 1.0のAPIを叩くコードを書きます。
base64文字列で生成結果が返ってくるので、デコードしてS3に格納します。
ランタイムはPython 3.12です。

コード作成にあたり、下記サイトを参考にさせていただきました。

**Bedrockの基盤モデルのAPIを叩くPythonコード**

https://docs.aws.amazon.com/bedrock/latest/userguide/inference-invoke.html


**Stable Diffusion XL 1.0へのinput部分**

https://github.com/aws-samples/amazon-bedrock-workshop/blob/main/00_Intro/bedrock_boto3_setup.ipynb

**base64文字列をデコードしてS3に保存する**

https://qiita.com/yumakkt/items/1a62d279e81d9f6d0c63


```python
import boto3
import json
import base64

def lambda_handler(event, context):
  s3 = boto3.resource('s3')
  bedrock = boto3.client(service_name='bedrock-runtime')

  #引数をプロンプトとして格納
  prompt_data = event['input']

  body = json.dumps({
      "text_prompts": [{"text": prompt_data}],
      "cfg_scale": 10,
      "seed": 20,
      "steps": 50
  })
  
  modelId = 'stability.stable-diffusion-xl-v1'
  accept = '*/*'
  contentType = 'application/json'
  
  response = bedrock.invoke_model(
      modelId=modelId,
      accept=accept,
      contentType=contentType,
      body=body,
  )

  
  response_body = json.loads(response.get('body').read())
  
  #レスポンスのbodyから生成された画像の情報のみ取得
  img_base64 = response_body["artifacts"][0].get("img_base64")

  #<bucketname>は実際のバケット名に置き換える
  bucket = s3.Bucket('<bucketname>')

  #S3に格納(ファイル名は<プロンプト>.png)
  bucket.put_object(
                  Key=f'{prompt}.png',
                  Body=base64.b64decode(img_base64.encode("UTF-8")),
                  ContentType='image/png')

  return response_body

```

### 2.3 Lambdaを動かしてみる

Lambdaのテストイベントでパラメータ(今回はプロンプト)を指定して実行してみましょう。
"waterfall and basin"というプロンプトで画像生成してもらいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/55169036-4b2f-30a1-030c-5fec40c3020c.png)



実行後、生成されたファイルがS3に格納されました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/2f6806b9-c014-58b9-42fa-ec0157615cd8.png)


ファイルを開いてみるとこんな感じ
![waterfall and basin (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3458266/b19f7b65-0074-fe27-523d-0f16f27bcadc.png)


## おわりに
次はチャット形式でプロンプトを提案してくれる部分を作りたいです。