画像生成 AI 入門: Python による拡散モデルの理論と実践#

Open In Colab

Section 07. Play with Diffusion Model#

Stable Diffusion を中心とした拡散モデルを用いて、最先端の画像生成技術を実際に動かして実践していきます。

Lecture 19. Stable Diffusion#

本実習ではテキストからの画像生成 (Text2Image) モデルである Stable Diffusion を更に深掘りします。特に本実習では以下の観点で Stable Diffusion の応用可能性について見ていきます:

  • Seed Search: 画像生成における乱数の種の探索

  • Image2Image: 画像 (と文) から画像を生成

  • Image Inpainting: 画像上のマスクされた領域を再構成

  • Depth2Image: 画像の深度情報を元に被写体の形状を損なうことなく画像を生成

セットアップ#

GPU が使用できるか確認#

本 Colab ノートブックを実行するために GPU ランタイムを使用していることを確認します。CPU ランタイムと比べて画像生成がより早くなります。以下の nvidia-smi コマンドが失敗する場合は再度講義資料の GPU 使用設定 のスライド説明や Google Colab の FAQ 等を参考にランタイムタイプが正しく変更されているか確認してください。

!nvidia-smi
Sat Jul  1 08:03:55 2023       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.85.12    Driver Version: 525.85.12    CUDA Version: 12.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |
| N/A   43C    P8     9W /  70W |      0MiB / 15360MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

利用する Python ライブラリをインストール#

diffusers ライブラリをインストールすることで拡散モデルを簡単に使用できるようにします。diffusers ライブラリを動かす上で必要となるライブラリも追加でインストールします:

  • transformers: 拡散モデルにおいて核となる Transformer モデルが定義されているライブラリ

  • accelerate: transformers と連携してより高速な画像生成をサポートするライブラリ

!pip install diffusers==0.16.1
!pip install transformers accelerate
Collecting diffusers==0.16.1
  Downloading diffusers-0.16.1-py3-none-any.whl (934 kB)
?25l     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/934.9 kB ? eta -:--:--
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 934.9/934.9 kB 46.6 MB/s eta 0:00:00
?25hRequirement already satisfied: Pillow in /usr/local/lib/python3.10/dist-packages (from diffusers==0.16.1) (8.4.0)
Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from diffusers==0.16.1) (3.12.2)
Collecting huggingface-hub>=0.13.2 (from diffusers==0.16.1)
  Downloading huggingface_hub-0.15.1-py3-none-any.whl (236 kB)
?25l     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/236.8 kB ? eta -:--:--
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 236.8/236.8 kB 19.1 MB/s eta 0:00:00
?25hCollecting importlib-metadata (from diffusers==0.16.1)
  Downloading importlib_metadata-6.7.0-py3-none-any.whl (22 kB)
Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from diffusers==0.16.1) (1.22.4)
Requirement already satisfied: regex!=2019.12.17 in /usr/local/lib/python3.10/dist-packages (from diffusers==0.16.1) (2022.10.31)
Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from diffusers==0.16.1) (2.27.1)
Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.13.2->diffusers==0.16.1) (2023.6.0)
Requirement already satisfied: tqdm>=4.42.1 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.13.2->diffusers==0.16.1) (4.65.0)
Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.13.2->diffusers==0.16.1) (6.0)
Requirement already satisfied: typing-extensions>=3.7.4.3 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.13.2->diffusers==0.16.1) (4.6.3)
Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.13.2->diffusers==0.16.1) (23.1)
Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.10/dist-packages (from importlib-metadata->diffusers==0.16.1) (3.15.0)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->diffusers==0.16.1) (1.26.16)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->diffusers==0.16.1) (2023.5.7)
Requirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.10/dist-packages (from requests->diffusers==0.16.1) (2.0.12)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->diffusers==0.16.1) (3.4)
Installing collected packages: importlib-metadata, huggingface-hub, diffusers
Successfully installed diffusers-0.16.1 huggingface-hub-0.15.1 importlib-metadata-6.7.0
Collecting transformers
  Downloading transformers-4.30.2-py3-none-any.whl (7.2 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.2/7.2 MB 87.7 MB/s eta 0:00:00
?25hCollecting accelerate
  Downloading accelerate-0.20.3-py3-none-any.whl (227 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 227.6/227.6 kB 12.4 MB/s eta 0:00:00
?25hRequirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from transformers) (3.12.2)
Requirement already satisfied: huggingface-hub<1.0,>=0.14.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.15.1)
Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (1.22.4)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from transformers) (23.1)
Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (6.0)
Requirement already satisfied: regex!=2019.12.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (2022.10.31)
Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from transformers) (2.27.1)
Collecting tokenizers!=0.11.3,<0.14,>=0.11.1 (from transformers)
  Downloading tokenizers-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.8/7.8 MB 113.5 MB/s eta 0:00:00
?25hCollecting safetensors>=0.3.1 (from transformers)
  Downloading safetensors-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.3/1.3 MB 53.9 MB/s eta 0:00:00
?25hRequirement already satisfied: tqdm>=4.27 in /usr/local/lib/python3.10/dist-packages (from transformers) (4.65.0)
Requirement already satisfied: psutil in /usr/local/lib/python3.10/dist-packages (from accelerate) (5.9.5)
Requirement already satisfied: torch>=1.6.0 in /usr/local/lib/python3.10/dist-packages (from accelerate) (2.0.1+cu118)
Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from huggingface-hub<1.0,>=0.14.1->transformers) (2023.6.0)
Requirement already satisfied: typing-extensions>=3.7.4.3 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub<1.0,>=0.14.1->transformers) (4.6.3)
Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch>=1.6.0->accelerate) (1.11.1)
Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch>=1.6.0->accelerate) (3.1)
Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch>=1.6.0->accelerate) (3.1.2)
Requirement already satisfied: triton==2.0.0 in /usr/local/lib/python3.10/dist-packages (from torch>=1.6.0->accelerate) (2.0.0)
Requirement already satisfied: cmake in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch>=1.6.0->accelerate) (3.25.2)
Requirement already satisfied: lit in /usr/local/lib/python3.10/dist-packages (from triton==2.0.0->torch>=1.6.0->accelerate) (16.0.6)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (1.26.16)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (2023.5.7)
Requirement already satisfied: charset-normalizer~=2.0.0 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (2.0.12)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (3.4)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch>=1.6.0->accelerate) (2.1.3)
Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.10/dist-packages (from sympy->torch>=1.6.0->accelerate) (1.3.0)
Installing collected packages: tokenizers, safetensors, transformers, accelerate
Successfully installed accelerate-0.20.3 safetensors-0.3.1 tokenizers-0.13.3 transformers-4.30.2

Stable Diffusion を用いた画像生成における乱数の種の探索#

本セクションでは、Stable Diffusion Pipeline を使用して、繰り返し使用可能な乱数の種 (seed) を使って生成された画像を元に、自身がイメージした画像を生成する方法を紹介します。

以下、Stable Diffusion with Repeatable Seeds を参考に動作を追っていきます。ここではまず初めに StableDiffusionPipelinerunwayml/stable-diffusion-v1-5 を読み込みます。

import torch
from diffusers import StableDiffusionPipeline

model_id = "runwayml/stable-diffusion-v1-5"
dtype = torch.float16

pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=dtype)
pipe = pipe.to("cuda")
`text_config_dict` is provided which will be used to initialize `CLIPTextConfig`. The value `text_config["id2label"]` will be overriden.

まずいくつかの画像を生成して、どの画像が好ましいかを確認します。複数生成した画像をグリッド上に表示する関数を以下のように定義します。この関数は 🤗 Hugging Face Stable Diffusion のブログ記事のものを利用しています。

from typing import List
from PIL import Image
from PIL.Image import Image as PilImage

def image_grid(imgs: List[PilImage], rows: int, cols: int) -> PilImage:
    assert len(imgs) == rows * cols

    w, h = imgs[0].size
    grid = Image.new("RGB", size=(cols * w, rows * h))
    grid_w, grid_h = grid.size

    for i, img in enumerate(imgs):
        grid.paste(img, box=(i % cols * w, i // cols * h))
    return grid

今回は一度に 4 つの画像を生成して、その結果を確認していきます。生成する画像の枚数は GPU の RAM の容量に応じて自由に変更してください。また、今回 512 x 512 サイズの画像を生成します。

num_images = 4 # 生成する画像の枚数

img_h = 512 # 生成する画像の高さ
img_w = 512 # 生成する画像の幅

あらかじめ用意した潜在変数を pipeline に渡すことで、生成される画像をコントロールしたり、再現可能にすることができます。再現可能な潜在変数を得るためには、乱数の seed を固定して生成したり、その結果を再利用する方法をとります。

diffusers の pipeline では、デフォルトで潜在変数の生成を pipeline の内部で行われます。以下のようにして、自前で潜在変数を生成して pipeline に渡すことで、意図した画像を生成できるようにします。

from diffusers.utils import randn_tensor

# 初期値となる乱数生成器を seed の固定込みで定義
generator = torch.Generator().manual_seed(19950815)

image_latents = []
seeds = []
for _ in range(num_images):
    # 上記で定義した乱数生成器で乱数を得る
    seed = generator.seed()
    seeds.append(seed)

    # 得られた乱数を元に乱数生成器の seed を固定
    generator = generator.manual_seed(seed)

    # 設定した乱数生成器を元に潜在変数を生成
    image_latent = randn_tensor(
        (1, pipe.unet.config.in_channels, img_h // 8, img_w // 8),
        generator=generator,
        device=pipe.device,
        dtype=dtype,
    )
    image_latents.append(image_latent)

# List[torch.Tensor] ([latent1, latent2, ..., latent4]) 形式を
# concat することで torch.Tensor 形式に変換
latents = torch.cat(image_latents)

# 生成したい画像の数だけ潜在変数が得られていることを確認
latents.shape
torch.Size([4, 4, 64, 64])

以上で画像を生成する準備ができました。パイプライン pipe に準備した潜在変数 latents を渡します。

prompt = "Labrador in the style of Vermeer"

images = pipe([prompt] * num_images, guidance_scale=7.5, latents=latents).images

生成が完了しました。上記で定義した image_grid 関数を使って生成画像を並べて表示しましょう。

image_grid(images, rows=2, cols=2)
Output hidden; open in https://colab.research.google.com to view.

生成結果を眺めてみて、好みの画像を 1 つ選んで深掘りしてみましょう。画像生成時に使用した seed を再利用すれば、同じ結果を再現することができます。

seed = seeds[2]
seed
4413939642087467736
generator = generator.manual_seed(seed)

image_latent = randn_tensor(
    (1, pipe.unet.config.in_channels, img_h // 8, img_w // 8),
    generator=generator,
    device=pipe.device,
    dtype=dtype,
)

上記のコードは seed 値から再度 generator の seed を固定して、潜在変数を生成していますが、単に latents から以下のように習得することも考えられます:

image_latent = latents[1] # たとえば 2 番目の潜在変数を取得

seed を記録しておくだけで、いつでも好きなときに生成結果を再現できるようになります。これらの latents と同じプロンプトで (1 枚の画像の) 生成を繰り返せば、以前と同じ画像が得られるはずです:

pipe(prompt, guidance_scale=7.5, latents=image_latent).images[0]
../_images/dd5a4c5b43a58fa6c5252efd921b0539affc3cea06bbb29de6c68af889a45fd9.png

期待したとおりに同様の画像が生成されました。次に同じ潜在変数を保持したまま、プロンプトの内容を変えてみましょう。こうすることで画像の構図は似ているが内容やスタイルが異なる画像が生成できるはずです。

以下がその例です:

prompt = "Terrier in the style of Vermeer"

pipe(prompt, guidance_scale=7.5, latents=image_latent).images[0]
../_images/9b3bc69485839e6bf6e695022dc3ec24eabc1f8cc04ab94d0e89aa61666c2fdd.png

犬種をラブラドール (Labrador) からテリア (Terrier) に変えたところ、同様の構図で変化していることがわかります。

prompt = "Labrador in the style of Van Gogh"

pipe(prompt, guidance_scale=7.5, latents=image_latent).images[0]
../_images/013dc4490e0864c4e7adb1e6536d25455b70c903704e8fe934ee573d30246792.png

次に画風をフェルメールからゴッホにしてみました。確かにゴッホ風の画像が生成されています。

prompt = "Clown in the style of Vermeer"

pipe(prompt, guidance_scale=7.5, latents=image_latent).images[0]
../_images/bd1cfc14ace149009009162a6d24c1f6328e671f431b50f35c44aed0a6d4553f.png

これまでは犬種でしたが、今度はピエロにしてみて画像を生成してみました。同様の構図やスタイルを引き継ぎつつ、画像が生成されているように見えます。

このようにして、乱数の seed やそれを元にした潜在変数を探索しつつプロンプトを調整していくことで、自身のイメージに合った画像を生成することが可能になります。

Image2Image#

本セクションでは、Stable Diffusion Image to Image Pipeline を使用して、Stable Diffusion モデルによる(テキスト条件付きの)ベースとなる画像からの画像生成技術について説明します。

以下、Image2Image Pipeline for Stable Diffusion using 🧨 Diffusers を参考に動作を追っていきます。ここではまず初めに StableDiffusionImg2ImgPipelinerunwayml/stable-diffusion-v1-5 を読み込みます。

from diffusers import StableDiffusionImg2ImgPipeline

model_id = "runwayml/stable-diffusion-v1-5"

pipe = StableDiffusionImg2ImgPipeline.from_pretrained(model_id, torch_dtype=dtype)
pipe = pipe.to("cuda")
`text_config_dict` is provided which will be used to initialize `CLIPTextConfig`. The value `text_config["id2label"]` will be overriden.

生成結果を保持するためのリストを用意しておきます。

img2img_results: List[PilImage] = []

オンラインにある画像をダウンロードする関数を定義します。この関数を使って今回使用するセグメンテーションマップを stable diffusion 本家のレポジトリからダウンロードしてきます。

from diffusers.utils import load_image

init_image = load_image("https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg")
img2img_results.append(init_image)

init_image
../_images/3f7c9152fbdcb66580c6fe0e0756ae8088a1087c2c4d746e180e7e0a1f702d60.png

では入力するプロンプトを用意して、pipeline を使って画像を生成してみます。

prompt = "A fantasy landscape, trending on artstation"

Image2Image タスクにおいて、pipeline の image 引数に上記でダウンロードした init_image を渡すことで、その画像を元にした画像を生成可能です。Image2Image pipeline では strength 引数が存在します。このパラメータは 0.0 から 1.0 の間の値で、入力画像に加えるノイズの量を制御するものになっています。1.0 に近い値を設定すると、様々なバリエーションでの画像生成が可能になりますが、入力画像と意味的に一致しない画像を生成する場合もあります。以下は strength0.75 に設定した結果です:

generator = torch.Generator().manual_seed(19950815)

image = pipe(
    prompt=prompt, # 条件付け用のプロンプト
    image=init_image, # 条件付け用の初期画像
    strength=0.75, # strength を 0.75 に設定
    guidance_scale=7.5,
    generator=generator,
).images[0]
img2img_results.append(image)

image
../_images/59134a8f6fb4df5a98dd59c824c58a3fd4fee1324eceae177a79e4663aff0287.png

以下は strength0.5 に設定した結果です:

generator = torch.Generator().manual_seed(19950815)

image = pipe(
    prompt=prompt,
    image=init_image,
    strength=0.5, # strength を 0.5 に設定
    guidance_scale=7.5,
    generator=generator,
).images[0]
img2img_results.append(image)

image
../_images/d9bc3da728cdfe1645c692bbc63c9020381b78c68550a6b97687c60a10b966da.png

strength を小さい値にすると、生成された画像はより元の画像に近くなることがわかりました。

では次に、デフォルトとは異なるノイズスケジューラを使用したときの生成結果を比較してみましょう。今回は LMS スケジューラ を使用してみます。 これまで使用してきた pipeline の設定 (pipe.scheduler.config) を元に、LMSDiscreteScheduler を定義し、pipeline にセットします。

from diffusers import LMSDiscreteScheduler

lms_scheduler = LMSDiscreteScheduler.from_config(pipe.scheduler.config)
pipe.scheduler = lms_scheduler
generator = torch.Generator().manual_seed(19950815)

image = pipe(
    prompt=prompt,
    image=init_image,
    strength=0.75,
    guidance_scale=7.5,
    generator=generator,
).images[0]
img2img_results.append(image)

image
../_images/9e1fc0d266aa3e906d72914bbf938edb6f42811be699a5fb93a4153f5cd3c369.png

以下は上記で生成した結果を並べたものです。左から順位以下の並びになっています:

  • オリジナルのセグメンテーションマップ

  • デフォルトのノイズスケジューラを使用・strength は 0.75

  • デフォルトのノイズスケジューラを使用・strength は 0.5

  • LMS ノイズスケジューラを使用・strength は 0.75

image_grid(img2img_results, rows=1, cols=len(img2img_results))
Output hidden; open in https://colab.research.google.com to view.

diffusers では 複数のスケジューラが実装 されています。簡単にスケジューラを変更可能であるため、自身がイメージした画像が生成されるものを比較してみてもよいかもしれません。

Inpainting#

本セクションでは、Stable Diffusion Inpainting Pipeline を使用して、Stable Diffusion モデルによる、画像上のマスクされた領域を再構成技術について説明します。

以下、In-painting pipeline for Stable Diffusion using 🧨 Diffusers を参考に動作を追っていきます。ここではまず初めに StableDiffusionInpaintPipelinerunwayml/stable-diffusion-inpainting を読み込みます。このモデルは上記で使用してきた runwayml/stable-diffusion-v1-5 とは異なり、inpainting 専用のモデルになっています。

from diffusers import StableDiffusionInpaintPipeline

model_id = "runwayml/stable-diffusion-inpainting"
pipe = StableDiffusionInpaintPipeline.from_pretrained(model_id, torch_dtype=dtype)
pipe = pipe.to("cuda")
safety_checker/model.safetensors not found
`text_config_dict` is provided which will be used to initialize `CLIPTextConfig`. The value `text_config["id2label"]` will be overriden.

Inpaint の対象となるサンプル画像を stable diffusion 本家のレポジトリからダウンロードしてきます。

image = load_image("https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png")
image = image.resize((512, 512))
image
../_images/0e3579c5a13e5d3b5d35b3d0b0b87e73a0209596124757caeaeb1274eef40be6.png

次に inpaint の際に使用するマスク画像を、同じく stable diffusion 本家のレポジトリからダウンロードしてきます。

mask_image = load_image("https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png")
mask_image = mask_image.resize((512, 512))
mask_image
../_images/6a4049bde93fa34fe7fe044becb1c18a89e4a4e702bea6dca70dc28017e7f8a4.png

ダンロードしてきた画像を元に inpainting モデルの動作を確認します。pipeline にオリジナルの画像とマスク画像を入れつつ、プロンプトでマスク箇所を描き直す条件を指定します。

prompt = "a mecha robot sitting on a bench"
num_images = 3

generator = torch.Generator().manual_seed(19950815)

images = pipe(
    prompt=prompt, # 条件付け用のプロンプトテキスト
    image=image, # inpainting 対象の画像
    mask_image=mask_image, # inpainting 対象の画像を操作するためのマスク画像
    guidance_scale=7.5,
    generator=generator,
    num_images_per_prompt=num_images,
).images

# 可視化用にオリジナルの画像を先頭に追加
images = [image, mask_image] + images

inpainting の結果を確認します。一番左がオリジナルの画像で、そのとなりがマスク画像、それ以外がマスク画像で指定した箇所をプロンプトの条件に従って描きなおしたものになります。

image_grid(images, rows=1, cols=num_images + 2)
Output hidden; open in https://colab.research.google.com to view.

オリジナルの画像の雰囲気を損なわず、マスクで指定した箇所がプロンプトに従ってオブジェクトが生成されております。ロボットの股下に注目すると、オリジナル画像では見えていなかった椅子の格子部分も補完されているのがすごいですね。

Depth2Image#

本セクションでは、 Stable Diffusion Depth to Image Pipeline を使用して、Stable Diffusion モデルによる深度を考慮した画像生成技術について説明します。

以下、Text-guided depth-to-image generation を参考に動作を追っていきます。Depth2Image は Stable Diffusion v2 のメイン機能の 1 つなので、以下では stabilityai/stable-diffusion-v2-depth というモデルを StableDiffusionDepth2ImgPipeline で読み込みます。

更に今回の depth2image のデモでは Stable Diffusion v2 の特徴である negative prompt (生成に含まれてほしくないような条件を指定するプロンプト) の機能も使用します。

from diffusers import StableDiffusionDepth2ImgPipeline

model_id = "stabilityai/stable-diffusion-2-depth"

pipe = StableDiffusionDepth2ImgPipeline.from_pretrained(model_id, torch_dtype=dtype)
pipe = pipe.to("cuda")

StableDiffusionDepth2ImgPipeline では新しい画像の生成条件として、テキストプロンプトと初期画像を渡すことができます。さらに画像構造を保持するために depth_map を渡すこともできます。 depth_map を渡さない場合、pipeline は内部的に統合された深度推定モデルによって、自動的に深度を予測して画像生成に使用します。

まず今回サンプルとして利用する画像をダウンロードします:

init_image = load_image("http://images.cocodataset.org/val2017/000000039769.jpg")
init_image
../_images/cf6f3c4befa148732c7453e0de5afab00f682427435fead2d88b07a9615cdac2.png

プロンプト (prompt) と、画像中に描画してほしくないものを指定するプロンプト (negative prompt) を指定して画像を生成します。negative prompt は、特定の単語が画像の生成に影響しないように制御するものです:

prompt = "two tigers"
nevative_prompt = "bad, deformed, ugly, bad anotomy"

generator = torch.Generator().manual_seed(19950815)

pipe(
    prompt=prompt,
    image=init_image,
    negative_prompt=nevative_prompt,
    strength=0.75,
    generator=generator,
).images[0]
../_images/fe37dbecce795abe8ca27099e3d8da66e0f0dcdc5a236340070f115220bb70e7.png

上記のように物体の深度を正しく推定し、今回では 2 匹の猫を虎に描画し直すことができました。今回は深度マップを pipeline に渡していませんが、より正しい深度マップを渡すことでよりよい画像生成結果を得ることができます。