Stable Diffusion

今更だけど、ちょっと遊んでみた。VAEのエンコーダで低次元の潜在空間に圧縮して、拡散させて、それをUNETで復元する際にCLIPを経由した生成指示情報を各層のAttentionに埋め込み、VAEのデコーダでピクセルに復元するとな。なるほど、完全に理解した。当たり前だけど、よく考えられているなぁ。

Windows 10 Pro 64bit
GeForce RTX2070 8GB

python 3.9.12
torch 1.12.1+cu116
diffusers 0.9.0

1.生成してみる(txt2img)

まず、モデルを読み込む。ここではv2を使っているが、v1-4などにも変更しやすいように変数化。GPUメモリは8GBしかないので、fp16版で、かつなんかメモリを節約してくれるらしいメソッド(最終行のやつ)を呼ぶ。

from diffusers import StableDiffusionPipeline, EulerDiscreteScheduler
import torch

version = "v2-0"
model_id = "stabilityai/stable-diffusion-2"
device = "cuda"

scheduler = EulerDiscreteScheduler.from_pretrained(model_id, subfolder="scheduler")
pipe = StableDiffusionPipeline.from_pretrained(model_id, scheduler=scheduler, revision="fp16", torch_dtype=torch.float16)
pipe = pipe.to(device)
pipe.enable_attention_slicing()

適当に10枚ほど生成。768×768で1枚約22秒。非実在写真を無限に錬成できる。

import os
import time
outdir = f"outputs_{version}"
os.makedirs(outdir, exist_ok=True)
fname = str(int(time.time()))

num = 10
prompt = "a beautiful girl in street. she has blond hair."

with open(f"{outdir}/{fname}.txt", "w") as f:
    print(prompt, file=f)

for i in range(num):
    image = pipe(prompt)["images"][0]
    image.save(f"{outdir}/{fname}_{i:03.0f}.png")
生成した画像

2.変換してみる(img2img)

次に、この画像(写真風)をイラスト風に変換してみる。イラストに強いと言われるanything-v3-0を使ってみる。

import torch
from diffusers import StableDiffusionImg2ImgPipeline

version = "anything-v3-0"
model_id = "Linaqruf/anything-v3.0"
pipe = StableDiffusionImg2ImgPipeline.from_pretrained(model_id, revision="diffusers", torch_dtype=torch.float16)

device = "cuda"
pipe.to(device)
pipe.enable_attention_slicing()

これを使って、先ほど生成した画像をイラスト風に変換してみる。768×768で1枚約25秒。画像がでかいとメモリが足りないみたいなことを言われるので、スケーリング係数をかけられるようにしている。*_000.pngは変換前の画像のコピー、001~が変換画像。

適当にコピペで動かしているんだけど、autocastってなんだろう?

strengthは大きくすると、元画像との差異が大きくなる? guidance_scaleは大きくすると、プロンプトによる指定が強く効くようになる?

import os
from PIL import Image
from torch import autocast
filename = "outputs_v2-0/1670846014_001.png"

outdir = f"outputs_i2i_{version}"
os.makedirs(outdir, exist_ok=True)
fname = os.path.splitext(os.path.basename(filename))[0]

init_image = Image.open(filename).convert("RGB")
scaling = 1.0
init_image = init_image.resize((int(scaling*init_image.width), int(scaling*init_image.height)))
init_image.save(f"{outdir}/{fname}_000.png")

start = 1
num = 10
prompt = "a girl of illust style. "

with open(f"{outdir}/{fname}.txt", "w") as f:
    print(prompt, file=f)

for i in range(start, num+start):
    with autocast(device):
        image = pipe(prompt, init_image=init_image, strength=0.5, guidance_scale=7.5, num_inference_steps=50).images[0]
    image.save(f"{outdir}/{fname}_{i:03.0f}.png")

すごいなぁ。画像は一致してないんだけど、印象はほぼそのままにイラスト風になる。

AIで生成した写真風画像と、それをさらにAIでイラスト風に変換した画像