MacBook(M2)にインストールしたStable DiffusionのAPIをPythonで呼び出して画像を生成してみた

[Supership](https://supership.jp/)の名畑です。今期アニメの[AIの遺電子](https://ai-no-idenshi.com/)が原作連載開始されたのは8年近く前なわけですが、現代においては、さらに迫って感じられます。

## はじめに

前回の記事[Stable Diffusion web UIをMacBook(M2)にインストールして画像の生成をしてみた記録](https://qiita.com/nabata/items/81dcc430aa03fc0b0f45)では**web UI**(ブラウザ)を経由して[Stable Diffusion](https://ja.stability.ai/stable-diffusion)の画像生成を行いました。

今回は**web UI**を経由せずに、PythonからローカルのAPIを叩いて画像の生成を行ってみます。

## 環境設定

[前回の記事](https://qiita.com/nabata/items/81dcc430aa03fc0b0f45)で行った環境構築が完了していることが前提です。

Pythonの呼び出しでは**requests**メソッドを用いますが、標準ライブラリである**urllib**を使う形に置き換えていただいてももちろん大丈夫です。[公式のサンプルコード](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/API)が**requests**を用いていたのでそれに従っただけです。

## COMMANDLINE_ARGSの設定

コマンドライン引数を設定します。

**stable-diffusion-webui**の配下にある**webui-user.sh**を開きます。

```console
#!/bin/bash
#########################################################
# Uncomment and change the variables below to your need:#
#########################################################

# Install directory without trailing slash
#install_dir="/home/$(whoami)"

# Name of the subdirectory
#clone_dir="stable-diffusion-webui"

# Commandline arguments for webui.py, for example: export COMMANDLINE_ARGS="--medvram --opt-split-attention"
#export COMMANDLINE_ARGS=""

# python3 executable
#python_cmd="python3"


以下略
```

このファイルの**COMMANDLINE_ARGS**の行に起動時の引数を3つ追記してコメントアウトを解除します。

- **--api**：**web UI**が**API**と共に起動されます。**--nowebui**を指定すると**API**のみを起動することも可能です。その場合はポート番号が**7860**ではなく**7861**になるのでご注意ください。
- **--skip-torch-cuda-test**：現在のmacOSは[CUDA](https://ja.wikipedia.org/wiki/CUDA)に対応していないため、該当のテストをスキップさせます。
- **--no-half**：前回も書きましたが、デフォルト設定だと「**ビデオカードがhalf typeをサポートしていない**」という旨のエラーが出るためこの指定をします。

適用すると下記になります。

```console
export COMMANDLINE_ARGS="--api --skip-torch-cuda-test --no-half"
```

コマンドライン引数について詳しくは公式の[Command Line Arguments and Settings](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Command-Line-Arguments-and-Settings)をご覧ください。

## web UIの起動

これも前回同様です。
**stable-diffusion-webui**に移動します。

```console
$ cd /path/to/stable-diffusion-webui
```

**webui.sh**を叩きます。

```console
$ ./webui.sh

略

Running on local URL:  http://127.0.0.1:7860

To create a public link, set `share=True` in `launch()`.
Startup time: 3.1s (import torch: 0.8s, import gradio: 0.4s, import ldm: 0.3s, other imports: 0.7s, load scripts: 0.4s, create ui: 0.3s, gradio launch: 0.1s).
DiffusionWrapper has 859.52 M params.
Applying attention optimization: InvokeAI... done.
Textual inversion embeddings loaded(0): 
Model loaded in 5.2s (load weights from disk: 0.4s, create model: 0.5s, apply weights to model: 2.5s, move model to device: 1.5s, calculate empty prompt: 0.2s).
```

## APIリファレンス

**web UI**が起動した状態であれば http://127.0.0.1:7860/docs でAPIリファレンスを開くことができます。

今回は**Text2ImgapiのAPI**(/sdapi/v1/txt2img)を使いますが、リファレンスでは下記のように記載されています。

<img width="50%" alt="image01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/8eafa067-2ea8-d076-33e2-d6f500480a5e.png">

## Pythonのコード

例のごとく、プロンプトとして「**28-year-old Japanese pretty woman with black bobbed hair**」を、ネガティブプロンプトとして「**text, letter, belongings**」を指定してみることとします。ここはもちろんご自由に書き換えてください。

```Python
import requests
import base64
import time  # ファイル名にtimestampを入れるためのみに使用

url = "http://127.0.0.1:7860"
payload = {
    "prompt":"28-year-old Japanese pretty woman with black bobbed hair",
    "negative_prompt": "text, letter, belongings"
}
response = requests.post(url=f'{url}/sdapi/v1/txt2img', json=payload)
# print(response.json()["parameters"])  # レスポンスパラメータを表示したいならコメントアウト解除
with open(f"./{int(time.time())}.png", "wb") as f:
    f.write(base64.b64decode(response.json()["images"][0]))
```

## 結果

無事にpng画像が生成されました。

<img width="50%" alt="image02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869934/7aaa5e46-0f4a-c706-3635-602c7bc01372.png">


> {'enable\_hr': False, 'denoising\_strength': 0, 'firstphase\_width': 0, 'firstphase\_height': 0, 'hr\_scale': 2.0, 'hr\_upscaler': None, 'hr\_second\_pass\_steps': 0, 'hr\_resize\_x': 0, 'hr\_resize\_y': 0, 'hr\_sampler\_name': None, 'hr\_prompt': '', 'hr\_negative\_prompt': '', 'prompt': '28-year-old Japanese pretty woman with black bobbed hair', 'styles': None, 'seed': -1, 'subseed': -1, 'subseed\_strength': 0, 'seed\_resize\_from\_h': -1, 'seed\_resize\_from\_w': -1, 'sampler\_name': None, 'batch\_size': 1, 'n\_iter': 1, 'steps': 50, 'cfg\_scale': 7.0, 'width': 512, 'height': 512, 'restore\_faces': False, 'tiling': False, 'do\_not\_save\_samples': False, 'do\_not\_save\_grid': False, 'negative\_prompt': 'text, letter, belongings', 'eta': None, 's\_min\_uncond': 0.0, 's\_churn': 0.0, 's\_tmax': None, 's\_tmin': 0.0, 's\_noise': 1.0, 'override\_settings': None, 'override\_settings\_restore\_afterwards': True, 'script\_args': [], 'sampler\_index': 'Euler', 'script\_name': None, 'send\_images': True, 'save\_images': False, 'alwayson\_scripts': {}}

**web UI**だと15秒以内だったのが、API経由だと35秒程度かかりましたが、デフォルトのステップ数が50だからですね、きっと(**web UI**の場合のデフォルトは20)。

## 設定の上書き

ここまで見てきて気づいてきた方もいるかもしれませんが、前回の記事において**web UI**上で最後に設定したモデルである[DreamShaper](https://civitai.com/models/4384/dreamshaper)(厳密には**dreamshaper\_7.safetensors [ed989d673d]**)が適用されています。

設定を変更したいときは、**ConfigのAPI**(/sdapi/v1/options)を呼び出します。

たとえば**sd\_model\_checkpoint**だけを変更するコードは下記です。

```Python
option_payload = {
    "sd_model_checkpoint": "v2-1_768-ema-pruned.ckpt [ad2a33c361]"
}
response = requests.post(url=f'{url}/sdapi/v1/options', json=option_payload)
```

参考までに、**model**の一覧は**Sd ModelsのAPI**(/sdapi/v1/sd-models)で取得できます。

```Python
response = requests.get(url=f'{url}/sdapi/v1/sd-models')
```

responseをjsonメソッドで書き出して整形して表示してみます。

```Python
print(json.dumps(response.json(), indent=4))
```

```json
[
    {
        "title": "v2-1_768-ema-pruned.ckpt [ad2a33c361]",
        "model_name": "v2-1_768-ema-pruned",
        "hash": "ad2a33c361",
        "sha256": "ad2a33c361c1f593c4a1fb32ea81afce2b5bb7d1983c6b94793a26a3b54b08a0",
        "filename": "/path/to/stable-diffusion-webui/models/Stable-diffusion/v2-1_768-ema-pruned.ckpt",
        "config": null
    },
    {
        "title": "dreamshaper_7.safetensors [ed989d673d]",
        "model_name": "dreamshaper_7",
        "hash": "ed989d673d",
        "sha256": "ed989d673dbf0153d1cd16994c1bfa27a0f13de1e60665aabcb99334ca5b78e9",
        "filename": "/path/to/stable-diffusion-webui/models/Stable-diffusion/dreamshaper_7.safetensors",
        "config": null
    }
]
```

## 最後に

コードで呼び出せるとやれることの幅も広がりますね。
遊び方をもっと考えたい気持ちになる。

## 宣伝

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

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