Diffusersで画像生成する

## 自動で画像生成がしたい

最近グラボを買いかえたのをきっかけにStableDiffusionでの画像生成にはまっていまして、ComfyUIを使っていたわけなんですが、いちいちプロンプトを書き換えて画像を作るのがめんどくさくなってきました。

ComfyUIの機能拡張などである程度の自動化はできるんですが、どう考えてもコードで制御した方が楽だなと思っていたところ、Diffusersという便利なものがあることを知りました。

で、とりあえず画像生成できるようになったので記録しときます。


## コード

```python:create_image.py

from diffusers import StableDiffusionXLPipeline, AutoencoderKL, DPMSolverMultistepScheduler
import torch

# SDXL モデルを直接ロード
pipe = StableDiffusionXLPipeline.from_single_file(
    "C:\\Users\\....\\StabilityMatrix\\Models\\StableDiffusion\\ntrMIXIllustriousXL_xiii.safetensors",
    torch_dtype=torch.bfloat16
).to("cuda")

pipe.vae = AutoencoderKL.from_pretrained(
    "madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.bfloat16
).to("cuda")

pipe.vae.enable_tiling()

# スケジューラーを DPM++ 2M Karras に設定
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
pipe.scheduler.use_karras_sigmas = True  # Karrasを有効に

# 画像生成設定
guidance_scale = 7 # CFG Scale
num_inference_steps = 30 # Steps

# プロンプト
prompt = """
1girl, beautiful girl, red hair, yellow eyes,braided hair,
white summer dress, straw hat, country road, bare foot,
masterpiece, best quality, amazing quality, very aesthetic, absurdres, newest,
"""
negative_prompt = """
lowres,worst quality, bad quality,bad anatomy,jpeg artifacts,
signature,watermark,old,oldest,censored,bar_censor, text,
anatomical nonsense, artistic error, bad anatomy, bad arm, bad aspect ratio,
bad ass, bad face, bad feet, bad hands, bad knees, bad leg, bad neck, bad perspective,
bad proportions, bad reflection, bad vulva, different reflection, extra arms, extra digits,
extra eyes, extra faces, extra legs, extra pupils, fewer digits,
oversized limbs, wrong foot, wrong hand,
"""

torch.cuda.empty_cache()  # VRAMクリア

# 画像生成
image = pipe(
    prompt,
    negative_prompt=negative_prompt,
    num_inference_steps=num_inference_steps,
    guidance_scale=guidance_scale,
).images[0]

# 画像を保存
image.save('generated_image.png', format="PNG")

```

## できた画像
![generated_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817/0d39fced-8a37-4de7-944a-9a9cbda4362c.png)


## モデル読み込み
```python
# SDXL モデルを直接ロード
pipe = StableDiffusionXLPipeline.from_single_file(
    "C:\\Users\\....\\StabilityMatrix\\Models\\StableDiffusion\\ntrMIXIllustriousXL_xiii.safetensors",
    torch_dtype=torch.bfloat16
).to("cuda")

pipe.vae = AutoencoderKL.from_pretrained(
    "madebyollin/sdxl-vae-fp16-fix", torch_dtype=torch.bfloat16
).to("cuda")

pipe.vae.enable_tiling()
```

今回はモデルとvaeを別で読み込んでます。あとで触れますが、当初階調がうまく出ない問題が発生していて、vaeを変えたら改善するんじゃないかと思って別読みにしてます。デフォルトでよければ明示的にvaeを読み込む必要はないです。

`torch_dtype` は PyTorchの計算に使うデータ型を指定していて、`torch.bfloat16`というのが精度がそれなりに高くて動作が軽いという型らしいです。ただし環境によっては使えないらしいので、その場合は`torch.float16`か`torch.float32`を使ってください。

タイリングはしてもしなくても良いと思います。

## スケジューラの指定

```python
# スケジューラーを DPM++ 2M Karras に設定
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
pipe.scheduler.use_karras_sigmas = True  # Karrasを有効に
```

ここが今回一番苦労したところで、ComfyUIとかWebUIだとサンプラーとスケジューラが別で指定できるんですが、Diffusersではざっくり言うと同じものしか指定できない、というかセットで指定することになっています。

当初、スケジューラに`KDPM2AncestralDiscreteScheduler`を指定していました。

```python
pipe.scheduler = KDPM2AncestralDiscreteScheduler.from_config(pipe.scheduler.config)
```
ChatGPTにDPM++2Mの指定はどうやったらいいのかを聞いたところ、
「DPM++2MはKDPM2AncestralDiscreteSchedulerだ」
と言ったので、それを信じて設定したところ、こんな画像が出ました。

![generated_image2025-02-21_134002_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817/ee66aeb2-b81a-41d7-bdfc-6f0c973f33fc.png)

もやもやしてる！

階調が失われてグラデーションが段々になっている感じ。まさかスケジューラの指定のせいとは思わず、色々調整してめちゃくちゃ時間取られました。`KDPM2AncestralDiscreteScheduler`は`K-DPM2 A`というやつで、前のステップのノイズを少し変化させながら画像を生成するため、階調が失われやすい傾向があるらしい。

`DPM++2M`は`DPMSolverMultistepScheduler`の方で、これを使うと30ステップでもずいぶん綺麗に画が出るようになりました。

![generated_image2025-02-21_134533_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3817/cb5c73a0-f165-4ac1-8bc6-98e58ddb84ec.png)
`DPMSolverMultistepScheduler`の力…

あとはプロンプト書いて、CFGスケールとステップ数を設定して、生成するだけですね。


## まとめ
これでスクリプトから画像出せるようになったので、ランダムでプロンプト吐かせて無限に画像が作れます。次はHires.fixあるいはUpscaleを自動かつ高精度にやれるスクリプトを検討したいです。