ジャコ Lab

プログラミング関連のメモ帳的ブログです

ControlNet 版 Inpaint をやってみる

zako-lab929.hatenablog.com

前回 runwayml/stable-diffusion-inpainting を使って Inpainting をしました。

今回は ControlNet 版で試してみます

はじめに

ControlNet is a type of model for controlling image diffusion models by conditioning the model with an additional input image. There are many types of conditioning inputs (canny edge, user sketching, human pose, depth, and more) you can use to control a diffusion model.

ControlNet とは、 追加の入力画像を使用して調整することで、出力画像を制御するモデルの一種で、制御に使用できる入力には様々なタイプがある とのことです。diffusers では v0.14.0 で登場し、 v0.16.0 でパワーアップした模様です。

今回は、その中でも、前々回から触っている Inpaint をやってみようと思います。

ControlNet 版 Inpaint

huggingface.co

投入画像の準備

from diffusers.utils import load_image
import numpy as np
import torch

# 投入画像の準備
init_image_url = "https://huggingface.co/datasets/diffusers/test-arrays/resolve/main/stable_diffusion_inpaint/boy.png"
init_image = load_image(init_image_url).resize((512, 512))

mask_image_url = "https://huggingface.co/datasets/diffusers/test-arrays/resolve/main/stable_diffusion_inpaint/boy_mask.png"
mask_image = load_image(mask_image_url).resize((512, 512))

# コントロールイメージを作成するメソッド
def make_inpaint_condition(image, image_mask):
    image = np.array(image.convert("RGB")).astype(np.float32) / 255.0
    image_mask = np.array(image_mask.convert("L")).astype(np.float32) / 255.0

    assert image.shape[0:1] == image_mask.shape[0:1], "image and image_mask must have the same image size"
    image[image_mask > 0.5] = -1.0  # set as masked pixel
    image = np.expand_dims(image, 0).transpose(0, 3, 1, 2)
    image = torch.from_numpy(image)
    return image

control_image = make_inpaint_condition(init_image, mask_image)

まずは、入力画像の準備です。 make_inpaint_condition メソッドについては詳しくは何をやっているかわからないですが、 入力画像マスク画像 から ControlNet で使用するイメージを作成しているようです。

(左) 入力画像 | (右) マスク画像

ControlNetModel の準備

# ControlNet の準備
controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_inpaint",
    torch_dtype=torch.float16
)

# パイプラインの準備
pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5", 
    torch_dtype=torch.float16,
    controlnet=controlnet, 
).to("cuda")
pipe.enable_model_cpu_offload()

# スケジューラの設定
pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)

お次は、ControlNetModel の準備です。

"lllyasviel/control_v11p_sd15_inpaint" を使用します。
パイプラインには StableDiffusionControlNetInpaintPipeline を使用して、 controlnet 引数 を設定します。

パイプライン実行

# パイプライン実行
prompt = "a handsome man with ray-ban sunglasses"
image = pipe(
    prompt,
    num_inference_steps=20,
    generator=torch.Generator(device="cuda").manual_seed(1),
    eta=1.0,
    image=init_image,
    mask_image=mask_image,
    control_image=control_image,
).images[0]

最後にパイプラインの実行です。 image 引数 に入力画像
mask_image 引数 にマスク画像
control_image 引数 に先程作成した control_image を設定します。

eta とはなんでしょうね?初見です。

ControlNet 版 Inpaint で生成した画像

ハンサムな男性が、レイバンのサングラスをかけたハンサムな男性になりました。

eta 引数とは?

eta (float, optional, defaults to 0.0) — Corresponds to parameter eta (η) from the DDIM paper. Only applies to the DDIMScheduler, and is ignored in other schedulers.

ということで DDIMScheduler に使われるパラメータでした。DDIM 以外の Scheduler では無視されるパラメータのようです。

通常版?(runwayml/stable-diffusion-inpainting) でもやってみよう

"runwayml/stable-diffusion-inpainting" 版

上手く行きました!

コード全文 (折りたたみ)

import torch
from diffusers import AutoPipelineForInpainting, DDIMScheduler

# パイプラインの準備
pipeline = AutoPipelineForInpainting.from_pretrained(
    "runwayml/stable-diffusion-inpainting",
    torch_dtype=torch.float16,
    variant="fp16"
).to("cuda")
pipeline.enable_model_cpu_offload()

# スケジューラの設定
pipeline.scheduler = DDIMScheduler.from_config(pipeline.scheduler.config)

# 投入画像の準備
init_image_url = "https://huggingface.co/datasets/diffusers/test-arrays/resolve/main/stable_diffusion_inpaint/boy.png"
init_image = load_image(init_image_url).resize((512, 512))

mask_image_url = "https://huggingface.co/datasets/diffusers/test-arrays/resolve/main/stable_diffusion_inpaint/boy_mask.png"
mask_image = load_image(mask_image_url).resize((512, 512))

# パイプライン実行
prompt = "a handsome man with ray-ban sunglasses"
image = pipeline(
    "a handsome man with ray-ban sunglasses",
    num_inference_steps=20,
    generator=torch.Generator(device="cuda").manual_seed(1),
    eta=1.0,
    image=init_image,
    mask_image=mask_image,
).images[0]

まとめ

ControlNet 版 Inpaint も diffusers でお手軽に使用できました。

runwayml/stable-diffusion-inpainting との違いはわかりませんが、ControlNet は同じノリで他にも色々できるようなので、「Inpaint だけ別のやり方」というよりは ControlNet に統一しても良さそうな気がしました。