Stable Diffusion APIを使って簡単に画像生成アプリを作ってみた

# はじめに
以前に比べると盛り上がりは感じないかもしれないが、それでもAndroidやiOSストアのランキング上位には画像生成AI系のアプリがいくつも並んでいて、生成系AIにも興味があったので同じようなものが作れないかなと思った。

しかし、全部自前で用意するとなると時間もお金もかかりそうなのでまずはAPIとしてそのまま利用できるサービスを探してお試ししてみることに。

# 利用したサービス

Stable Diffusion本家のStability AIでも利用できるAPIは用意されていたが
https://platform.stability.ai/


APIや料金体系がわかりやすそうだったりしたので今回はこちらのサービスを利用してみました。
https://stablediffusionapi.com/

![price.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245797/a80f644b-ab4c-e887-807a-18a8e26acea5.png)

APIを利用できるのはStandardプランからで9999枚の画像まで利用できるとのこと、Premiumプランでは画像上限がなくなります。

上記はProプランの一覧で、更に上のEnterpriseプランが用意されており、値段の方もあがりますが専用のGPUを利用することができるようです。

後述もしますが、Proプランでは共有のGPUを利用するため、画像の生成時そこそこの頻度でQueueに入ってしまうケースがありました。

# 使用したAPI
まずは初めと言うことで一番スタンダードに利用できるText-to-ImageのAPIを使用した。
https://stablediffusionapi.com/docs/stable-diffusion-api/text2img

リクエストのパラメータは以下
```
{
  "key": "",
  "prompt": "ultra realistic close up portrait ((beautiful pale cyberpunk female with heavy black eyeliner))",
  "negative_prompt": null,
  "width": "512",
  "height": "512",
  "samples": "1",
  "num_inference_steps": "20",
  "safety_checker": "no",
  "enhance_prompt": "yes",
  "seed": null,
  "guidance_scale": 7.5,
  "multi_lingual": "no",
  "panorama": "no",
  "self_attention": "no",
  "upscale": "no",
  "embeddings_model": null,
  "webhook": null,
  "track_id": null
}
```
使用してみて注意したパラメータについて記載。
画像生成時の細かいパラメータについては今回は気にしないこととした。

- safety_checker
NSFW(R18)画像チェッカー
yesにすると対象となる画像が生成された場合に黒い画像が返ってくるようになる。今回はAndroid,iOSのストアにアプリを申請するのでこれをyesにしないと申請が通らない。（または仮に通ったとしても後からBANされてしまう可能性あり）

- multi_lingual
多言語対応
yesにすると英語以外でも自動翻訳してくれて対応してくれるとあったが、日本語が対応言語に入っているのにこのパラメータをyesにしても生成される画像の精度が良くなかった。
そのため、自前でプロンプトを英語に翻訳してあげてから渡してあげると結果が良くなりました。
翻訳APIの作成についてはこちらの記事を参考にしました。
[Google翻訳APIを無料で作る方法](https://qiita.com/satto_sann/items/be4177360a0bc3691fdf)


次にレスポンスは以下
```
{
  "status": "success",
  "generationTime": 1.3200268745422363,
  "id": 12202888,
  "output": [
    "https://pub-8b49af329fae499aa563997f5d4068a4.r2.dev/generations/e5cd86d3-7305-47fc-82c1-7d1a3b130fa4-0.png"
  ],
  "meta": {
    "H": 512,
    "W": 512,
    "enable_attention_slicing": "true",
    "file_prefix": "e5cd86d3-7305-47fc-82c1-7d1a3b130fa4",
    "guidance_scale": 7.5,
    "model": "runwayml/stable-diffusion-v1-5",
    "n_samples": 1,
    "negative_prompt": " ((out of frame)), ((extra fingers)), mutated hands, ((poorly drawn hands)), ((poorly drawn face)), (((mutation))), (((deformed))), (((tiling))), ((naked)), ((tile)), ((fleshpile)), ((ugly)), (((abstract))), blurry, ((bad anatomy)), ((bad proportions)), ((extra limbs)), cloned face, glitchy, ((extra breasts)), ((double torso)), ((extra arms)), ((extra hands)), ((mangled fingers)), ((missing breasts)), (missing lips), ((ugly face)), ((fat)), ((extra legs))",
    "outdir": "out",
    "prompt": "ultra realistic close up portrait ((beautiful pale cyberpunk female with heavy black eyeliner)) DSLR photography, sharp focus, Unreal Engine 5, Octane Render, Redshift, ((cinematic lighting)), f/1.4, ISO 200, 1/160s, 8K, RAW, unedited, symmetrical balance, in-frame",
    "revision": "fp16",
    "safetychecker": "no",
    "seed": 3499575229,
    "steps": 20,
    "vae": "stabilityai/sd-vae-ft-mse"
  }
}
```
成功した場合、`status:success`とoutputにURLが配列になって返ってくる。
今回、共有GPUを使用するプランのためか、レスポンス時点で画像が作成されずキューに入れられてしまうことが多々発生した。その場合`status:processing`でoutputが空の状態で返ってきた。

キューに入れられた画像を取得するためのAPIが用意されいるので
https://stablediffusionapi.com/docs/stable-diffusion-api/fetchqueimg
レスポンスのidを保持しておいて投げる処理を作ってみたが、体感ではあるがフェッチのAPIを投げるよりも再度画像生成し直した方が圧倒的に速く画像ができていそうだったので、キューに入ってしまった場合はエラー扱いとして画像を再度生成してもらうこととした。


# アプリ側の処理について
単純なHTTPのpostなのでコードについては省略。AndroidではOkHttp、iOSではAlamofireを使用して特にハマる点などはなかった。

# できあがったアプリのスクリーンショット
<p>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245797/e4b9d3c9-7e11-213d-08ed-74a0ff666575.png" width="40%">
　　　
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245797/c2aeecc1-2a69-fdc1-e960-42592c3bad22.png" width="40%">
</p>

プロンプトを何も入力しない状態で送ると"man on the moon"がデフォルトになるよう。

# 感想
本来はもっと手間がかかりそうなアプリを簡単に作成することができる点がすごく良かった。
しかし、出来上がる画像のクオリティなどはそこまで高くないのかな？とも感じたので、ストアの上位に入っているようなクオリティに近づけるにはもう少し工夫が必要だと改めて実感した。

現在のプランだと画像生成時、結構キューに入ってしまうことが多いのでそのあたりは費用との相談かなとも。

# 今後
まずは用意されている他のモデルが利用できるAPIがあるのでそちらを利用して別のアプリも作成してみようと考えている。
https://stablediffusionapi.com/docs/community-models-api-v4/dreamboothtext2img

他にもImage-to-ImageやのAPIが利用できたり、追加でお金が必要だが使用したいモデルのアップロードや、モデルのトレーニングもできそうなのでやれることは多そう。

また、Stability AIの方も利用してみて使い勝手の差などを比べてみたい。

# 作ったアプリの公開
今回作成したアプリはストアに公開しているので興味のある方は是非ダウンロードして試してみてください！
https://play.google.com/store/apps/details?id=jp.co.togatta.aiart
https://apps.apple.com/jp/app/ai-art-generator-simple/id6464369305