Stable Diffusionでの画像生成をPythonとWeb APIで実装してみた記録

[Supership](https://supership.jp/)の名畑です。[ツイステッドワンダーランド](https://twisted-wonderland.aniplex.co.jp/)の7章のChapter 3がついに公開されたわけなんですが、すぐに読み終わってしまい、続きはよ。はよ。

## はじめに

これまで[OpenAI](https://openai.com/)で遊ぶ記事を色々と公開してきまして、また今更感ありまくりなのですが、最近は[stability.ai](https://www.stability.ai/)の[API群](https://platform.stability.ai/)で遊んでおります。テキストからの画像生成モデルである[Stable Diffusion](https://ja.stability.ai/stable-diffusion)がめちゃくちゃ有名ですね。

今回の記事では**アカウントの作成**と**APIを呼び出しての画像生成**をPythonで実装した記録を残しておきます。

登場するコードは[REST API referenceのv1/generation](https://platform.stability.ai/rest-api#tag/v1generation)のままです。

## 自環境

macOS(Monterey)です。

Pythonはすでにインストール済みです。

```shell-session
$ python --version
Python 3.10.7
```

また、サンプルコードがHTTPライブラリである**requests**を用いていたため、入れておきました。


```shell-session
$ pip install requests
```

今のバージョンは2.30.0でした。

```shell-session
$ pip show requests
Name: requests
Version: 2.30.0
以下略
```

## DreamStudioのアカウント作成

[DreamStudio](https://dreamstudio.ai/)という、**Stable DiffusionをWeb上で操作して画像生成する公式サービス**があるのですが、こちらのページの右上にある**Login**をクリックします。

Google、Discord、あるいはメールアドレスでのアカウント作成に対応しています。

## API key発行

ログイン後の[Account](https://dreamstudio.ai/account)のページで行えます。おそらくデフォルトで一つAPI keyが発行されているかと思います。追加発行も可能です。
無料クレジットで足りない場合のクレジットの購入もこちらのページで可能です。

取得したAPI keyはハードコーディングを避けるため環境変数に設定しました。
公式に倣い**STABILITY\_API\_KEY**という環境変数名としました。

```bash
export STABILITY_API_KEY =ここに取得したAPI keyを書く
```

## 価格

[Pricing Table](https://platform.stability.ai/docs/getting-started/credits-and-billing#pricing-table)に価格が掲載されています。エンジンとサイズとステップ数(後述)で変わります。

Stable Diffusionの最新版である**Stable Diffusion XL**（**SDXL**）以外のエンジンであれば現在は下記価格です。

| Steps | 512 x 512	 | 512 x 768 | 512 x 1024 | 768 x 768 | 768 x 1024 | 1024 x 1024 |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 15 | $0.001 | $0.003 | $0.004 | $0.005 | $0.007 | $0.01 |
| 30 | $0.002 | $0.005 | $0.008 | $0.01 | $0.014 | $0.019 |
| 50 | $0.004 | $0.009 | $0.013 | $0.016 | $0.023 | $0.032 |
| 100 | $0.007 | $0.017 | $0.026 | $0.031 | $0.045 | $0.064 |
| 150 | $0.01 | $0.025 | $0.039 | $0.046 | $0.067 | $0.095 |

**Stable Diffusion XL**（**SDXL**）の価格は[SDXL Pricing Table](https://platform.stability.ai/docs/getting-started/credits-and-billing#sdxl-pricing-table)に掲載されています。下記です。大体2.5倍って感じでしょうか。

| Steps | 512 x 512	 | 640 x 512 | 768 x 512 | 896 x 512 | 512 x 640 | 512 x 768 | 512 x 896 |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 15 | $0.0025 | $0.0045 | $0.0065 | $0.0084 | $0.0045 | $0.0065 | $0.0084 | 
| 30 | $0.005 | $0.009 | $0.013 | $0.017 | $0.009 | $0.013 | $0.017 |
| 50 | $0.0083 | $0.0149 | $0.0215 | $0.028 | $0.0149 | $0.0215 | $0.028 |
| 100 | $0.0167 | $0.0298 | $0.0431 | $0.0563 | $0.0298 | $0.0431 | $0.0563 |
| 150 | $0.025 | $0.045 | $0.065 | $0.085 | $0.045 | $0.065 | $0.085 |

## engine_id

APIの呼び出し時に**engine_id**を指定するのですが、現在は下記が選べます。**stable-diffusion-xl-beta-v2-2-2** が最新のSDXLです。

| id | name |
| --- | --- |
| stable-diffusion-v1 | Stable Diffusion v1.4 |
| stable-diffusion-v1-5 | Stable Diffusion v1.5 |
| stable-diffusion-512-v2-0 | Stable Diffusion v2.0 |
| stable-diffusion-768-v2-0 | Stable Diffusion v2.0-768 |
| stable-diffusion-512-v2-1 | Stable Diffusion v2.1 |
| stable-diffusion-768-v2-1 | Stable Diffusion v2.1-768 |
| stable-diffusion-xl-beta-v2-2-2 | Stable Diffusion v2.2.2-XL Beta |
| stable-inpainting-v1-0 | Stable Inpainting v1.0 |
| stable-inpainting-512-v2-0 | Stable Inpainting v2.0 |
| esrgan-v1-x2plus | Real-ESRGAN x2 |
| stable-diffusion-x4-latent-upscaler | Stable Diffusion x4 Latent Upscaler |

指定可能な**engine_id**のリストは[enginesのlist API](https://platform.stability.ai/rest-api#tag/v1engines/operation/listEngines)で取得可能です。今回は説明は省略します。

## generation APIのパラメータ

|  | Deault値 | 説明 | 
| --- | :---: | --- |
| height | 512 | 高さです。768用のengineではheight * widthが589,824以上である必要があります。 |
| width | 512 | 幅です。768用のengineではheight * widthが589,824以上である必要があります。|
| **text_prompts** | - | これのみがrequiredです。どういった画像を生成してほしいかをtextに記載し、その重みをweigthに記載します。重みはoptionalで、マイナス値(いわゆるネガティブプロンプト)も指定可能です。 |
| cfg\_scale | 7 | text_promptsで記載した内容にどれだけ従うかを0〜35で記載します。35に近いほど強く従います。 |
| clip\_guidance\_preset | NONE | CLIPガイダンスプリセット。下記から選択可能です。<br>FAST\_BLUE / FAST\_GREEN /  NONE / SIMPLE / SLOW / SLOWER / SLOWEST | 
| sampler | - | サンプラー。下記から選択可能です。指定しない場合は適切なものが適用されます。<br>DDIM / DDPM / K\_DPMPP\_2M /  K\_DPMPP\_2S\_ANCESTRAL / K\_DPM\_2 /  K\_DPM\_2\_ANCESTRAL / K\_EULER / K\_EULER\_ANCESTRAL / K\_HEUN / K\_LMS  |
| samples | 1 | 生成画像数 | 
| seed | 0 | 乱数設定するためのシード値。つまりはこれを違う値にすれば同じプロンプトでも違う画像が生成されます。0指定、もしくは無指定だと自動でランダムになります。
| steps | 50 | 生成ステップ数を10〜150で記載します。150に近いほど画像が高精度になります。 | 
| style\_preset | - | 画像のスタイル。下記から選べます。<br>3d-model / analog-film / anime / cinematic / comic-book / digital-art / enhance / fantasy-art / isometric / line-art / low-poly / modeling-compound / neon-punk / origami / photographic / pixel-art / tile-texture
| extras | - | その他のパラメータをengineに渡すためのものです。開発用途などで使われるそうです。 | 

## Pythonでのコード

サンプルコードを基本としていますが「**requiredではないパラメータの削除**」「**コメントの追加**」「**ファイル保存先とファイル名を変更**」をしています。

プロンプトには「**A cute fairy on top of a white laptop**」というtextを渡しています。「**白いノートパソコンの上のかわいい妖精**」ですね。

```Python
import base64
import os
import requests
import time

engine_id = "stable-diffusion-v1-5"
api_host = os.getenv('API_HOST', 'https://api.stability.ai')
api_key = os.getenv("STABILITY_API_KEY")

# API Keyの取得確認
if api_key is None:
    raise Exception("Missing Stability API key.")

# API呼び出し
response = requests.post(
    f"{api_host}/v1/generation/{engine_id}/text-to-image",
    headers={
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Authorization": f"Bearer {api_key}"
    },
    json={
        "text_prompts": [
            {
                "text": "A cute fairy on top of a white laptop"
            }
        ],
    },
)

# レスポンス確認
if response.status_code != 200:
    raise Exception("Non-200 response: " + str(response.text))

# レスポンス取得
data = response.json()

# 画像保存
# ファイル名にはタイムスタンプとengine_id、通番を含めています
# 通番は0〜samplesの値 - 1
for i, image in enumerate(data["artifacts"]):
    with open(f"./{engine_id}_{int(time.time())}_{i}.png", "wb") as f:
        f.write(base64.b64decode(image["base64"]))
```

## 生成結果

**engine_id別**で2枚ずつ結果を添付します。
こうやって並べてみると最新の**SDXL**の生成画像の品質向上が目で見てわかるレベルですね。主観ではありますが。

## Stable Diffusion v1.4

```Python
engine_id = "stable-diffusion-v1"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-v1_1684075967_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/75994db2-bcaf-e4a2-762c-d06e1fff3503.png"> | <img alt="stable-diffusion-v1_1684076096_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/65415219-199c-6c28-64d3-a4493eab5b87.png"> |

## Stable Diffusion v1.5

```Python
engine_id = "stable-diffusion-v1-5"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-v1-5_1684075976_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/4185c19f-ae3b-b5f6-1360-9053b3545f8f.png"> |  <img alt="stable-diffusion-v1-5_1684076102_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/a5584e35-f323-d88c-b139-ccf3de030d94.png"> |


## Stable Diffusion v2.0

```Python
engine_id = "stable-diffusion-512-v2-0"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-512-v2-0_1684076018_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/9ab93af7-2234-ccc3-ba27-371e666d8349.png"> | <img alt="stable-diffusion-512-v2-0_1684076113_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/6a97b3b5-b948-69e6-b4f5-386aac6a0a2f.png"> |


## Stable Diffusion v2.1

```Python
engine_id = "stable-diffusion-512-v2-1"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-512-v2-1_1684075987_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/498218ce-4bc1-cfc2-cccb-b8de431d3280.png"> | <img alt="stable-diffusion-512-v2-1_1684076118_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/8c1f634a-8dff-afcb-f307-35d6b0b28362.png"> |


## Stable Diffusion v2.2.2-XL Beta

```Python
engine_id = "stable-diffusion-xl-beta-v2-2-2"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-xl-beta-v2-2-2_1684075996_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/51b561c9-f1e2-e886-8fbb-1f043b56844a.png"> | <img alt="stable-diffusion-xl-beta-v2-2-2_1684076124_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/31c27cb4-f6cd-51ee-f80f-48f70e00af83.png"> |


## 生成結果(anime指定)

**style_preset**に**anime**を指定した絵も生成してみました。
こちらも**SDXL**でかなり質が高まったと感じます。

```Python
    json={
        "text_prompts": [
            {
                "text": "A cute fairy on top of a white laptop"
            }
        ],
        "style_preset": "anime",
    },
```

## Stable Diffusion v1.4(anime指定)

```Python
engine_id = "stable-diffusion-v1"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-v1_1684075613_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/5aee099e-e59f-7326-113a-0fbedd5b61fc.png"> | <img alt="stable-diffusion-v1_1684076193_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/b8c0419f-54eb-55b7-8598-c7441176b2dd.png"> |

## Stable Diffusion v1.5(anime指定)

```Python
engine_id = "stable-diffusion-v1-5"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-v1-5_1684075678_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/316aabc9-34b2-8942-74f4-33c032a8bac4.png"> | <img alt="stable-diffusion-v1-5_1684076197_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/b1710b9c-dffb-ccde-ce8a-b3d9e6b073b3.png"> |

## Stable Diffusion v2.0(anime指定)

```Python
engine_id = "stable-diffusion-512-v2-0"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-512-v2-0_1684075686_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/ca0ba940-3ce6-c0de-8ed2-d79931343848.png"> | <img alt="stable-diffusion-512-v2-0_1684076202_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/8ecf1c98-8ec3-b775-086c-6074733ce100.png"> |


## Stable Diffusion v2.1(anime指定)

```Python
engine_id = "stable-diffusion-512-v2-1"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-512-v2-1_1684075692_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/edee6656-3b03-8c3d-8987-c419135308ff.png"> | <img alt="stable-diffusion-512-v2-1_1684076206_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/55ecf1dd-a705-cbcf-1163-e2df0bbcfa29.png"> |


## Stable Diffusion v2.2.2-XL Beta(anime指定)

```Python
engine_id = "stable-diffusion-xl-beta-v2-2-2"
```

| | |
| --- | --- |
| <img alt="stable-diffusion-xl-beta-v2-2-2_1684075552_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/5b4ba321-5dc4-81f7-e196-e7d2d8a8638d.png"> | <img alt="stable-diffusion-xl-beta-v2-2-2_1684075701_0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/daa23577-1491-767c-f494-6e666007d3c1.png"> |



## 最後に

次回はプロンプトをもう少し色々といじって遊んでみようかと思います。同一人物における表情変化を記事にする予定です。

## 宣伝

[SupershipのQiita Organization](https://qiita.com/organizations/supership-inc)を合わせてご覧いただけますと嬉しいです。他のメンバーの記事も多数あります。

Supershipではプロダクト開発やサービス開発に関わる方を絶賛募集しております。
興味がある方は[Supership株式会社 採用サイト](https://supership.jp/recruit/)よりご確認ください。