9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

生成AIに関する記事を書こう!
Qiita Engineer Festa20242024年7月17日まで開催中!

diffusers(Stable Diffusion)による画像の改造/合成/変換/修正/拡大

Last updated at Posted at 2024-07-17

はじめに

この前の入門記事の続きの内容です。

前回は主に言葉のプロンプトだけから画像を生成するというtxt2imgの話でしたが、今回はimg2imgをはじめとして画像を既存の画像を元に改造したり変換したり修正したり拡大させたりする機能の話となります。

ここではまず主に:

  • img2img
  • refiner
  • upscale
  • inpaint

の話になります。その他にもcontrolnetとipadapterなど関連の機能がありますが、これについて内容が多いので後で別の記事に書きます。

又、「潜在空間」という概念も関わってきて扱い方を知っておいたらいいと思うのでついでにここでちょっと触れてみます。

前回からの続きなのでまず必要なライブラリーがインストールされているという前提で進みます。

  • diffusers
  • pytorch
  • transformer
  • translate (一応プロンプトは全部日本語で入力します)

img2img 〜画像から画像生成〜

img2imgはとある画像があってそれを別の何かの画像に改造や修正をしたい時に使う機能です。

img2imgのパイプラインクラスはStableDiffusionImg2ImgPipelineStableDiffusionXLImg2ImgPipelineです。前回説明したtxt2imgでStableDiffusionPipelineStableDiffusionXLPipelineを使うのと同じように、img2imgでもSDXLモデルかどうかで使い分けする必要があります。

使い方はtxt2imgと大体似ていますが、入力はただ言葉だけでなく、元画像も必要となります。

例えばアニメ風画像をリアル風にしたい場合です。例としてこの画像を入力として使います。これもStable Diffusionによって生成されたものです。(具体的に言うと以前の記事で説明したComfyUIでblue_pencil-XLモデル

ginpatsukitsunemimi.jpg

そしてこのリアル風のチェックポイントモデルを使います。

実装のコードはこんな感じ。

import torch
from PIL import Image
from diffusers import StableDiffusionImg2ImgPipeline,DPMSolverMultistepScheduler
from translate import Translator

device = 'mps'
pipe = StableDiffusionImg2ImgPipeline.from_single_file(
    'stadifmodel/beautifulRealistic_v7.safetensors',
    torch_dtype=torch.float16
).to(device)
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
generator = torch.Generator(device).manual_seed(174)
honyaku = Translator('en','ja').translate
prompt = honyaku('黒髪ツインテイル少女')
img0 = Image.open('ginpatsukitsunemimi.jpg')
img = pipe(
    prompt,
    image=img0,
    guidance_scale=7,
    strength=0.6,
    num_inference_steps=30,
    generator=generator
).images[0]  
img.save("realkurokamitwintail.jpg")    

そしてできたのはこのように元画像の構造を保ちながらプロンプトに書いた内容も従うリアルな画像です。

realkurokamitwintail.jpg

まだアニメっぽいって感じですが、これもパラメータ調整やプロンプトやチェックポイントモデル次第です。

ここで一番重要なパラメータはstrengthです。これを変えるだけで結果は全然違います。その他のパラメータは殆どtxt2imgと同じですが、strengthだけはimg2imgしか使わないので、ここでstrengthの影響について説明します。

strength 〜変化の強さ〜

strengthは変化の強さを表すパラメータです。値の範囲は0から1です。0だと元の画像のままで、1だとほぼ完全に元画像を無視するということになります。

又、生成時のステップ数はnum_inference_stepsではなく、「num_inference_steps × strength」となります。だからstrengthが小さいと速いです。変化が少ない分、生成時間も短いです。

では違うstrengthの結果を比較してみます。例として今回はこの画像を元画像にします。これは元々私がpixivに投稿した画像です。

isogashiiteto.jpg

そしてこのアニメ風チェックポイントモデルを使います。

import torch
from PIL import Image
from diffusers import StableDiffusionImg2ImgPipeline,EulerAncestralDiscreteScheduler
from translate import Translator
import matplotlib.pyplot as plt

def seisei(strength):
    pipe = StableDiffusionImg2ImgPipeline.from_single_file(
        'stadifmodel/himawarimix_v11.safetensors',
        torch_dtype=torch.float16
    ).to(device)
    pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
    generator = torch.Generator(device).manual_seed(seed)
    img = pipe(
        prompt,
        image=img0,
        guidance_scale=8,
        strength=strength,
        num_inference_steps=30,
        generator=generator
    ).images[0]
    return img

device = 'mps'
seed = 11190
honyaku = Translator('en','ja').translate
prompt = honyaku('ずんだ餅を食べている緑髪少女')
img0 = Image.open('isogashiiteto.jpg')

plt.figure(figsize=[6,15.5],dpi=100)
for i in range(10):
    strength = 0.1+i*0.1
    img = seisei(strength)
    plt.subplot(5,2,i+1,title='strength = %.1f'%strength)
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout(pad=0.5)
plt.savefig('isogashiiteto_janai.jpg')
plt.close()

isogashiiteto_janai.jpg

髪の色がどんどん変わっていくのがいいですね。コーヒーもずんだ餅になっていくのは面白いです。

guidance_scale 〜プロンプトの重さ〜

guidance_scale(CFG scaleとも)はtxt2imgの場合と同じようにプロンプトの重要性を表すパラメータです。strengthほどではないが、guidance_scaleも重要なのでここでもguidance_scaleの違いの比較もしてみます。

今回の例はリアル写真とアニメ風のチェックポイントモデルを使ってみます。これは博多駅の近くの店の肉ごぼう天うどんです。これをうなぎ丼に改造してみます。

hktudon.jpg

import torch
from PIL import Image
from diffusers import StableDiffusionImg2ImgPipeline,EulerAncestralDiscreteScheduler
from translate import Translator
import matplotlib.pyplot as plt

def seisei(gs):
    pipe = StableDiffusionImg2ImgPipeline.from_single_file(
        'stadifmodel/himawarimix_v11.safetensors',
        torch_dtype=torch.float16
    ).to(device)
    pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
    generator = torch.Generator(device).manual_seed(seed)
    
    img = pipe(
        prompt,
        image=img0,
        guidance_scale=gs,
        strength=0.5,
        num_inference_steps=30,
        generator=generator
    ).images[0]
    return img

device = 'mps'
seed = 111147
honyaku = Translator('en','ja').translate
prompt = honyaku('うなぎ丼')
img0 = Image.open('hktudon.jpg')

plt.figure(figsize=[6,9.5],dpi=100)
for i in range(6):
    img = seisei(i*2)
    plt.subplot(3,2,i+1,title='guidance_scale = %d'%(i*2))
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout(pad=0.5)
plt.savefig('hktunagidon.jpg')
plt.close()

hktunagidon.jpg

うどんからどんぶりに変わりますね。guidance_scaleが大きくなるとうなぎ丼っぽくなっていく気がします。

guidance_scaleが0だとプロンプトを入れないのと同じですが、それでも画像はそれなりに変えられます。ただしどんな画像にするかイメージがはっきりしないせいかぼやっとした感じです。

refiner 〜モデル合成〜

リファイナー(refiner)というのは元々SDXLモデルと同時に公開された機能です。最初はSDXLモデルと一緒に使うことで画質が上がるという主張でしたが、違いが微妙で必要がないと評価した人も多くて結局その目的で使う人は少ないと思います。

ただしリファイナーはもう一つの用途があります。それは2つのSDXLモデルを合成することです。具体的にはまず「ベースモデル」となる一つのモデルで生成を始めて、途中で「リファイナーモデル」となるもう一つのモデルに渡して生成を続けるのです。

リファイナーとして使うモデルは本来SD-XL 1.0-refinerが推奨されましたが、結局普段使っているSDXLモデルどれもリファイナーとして使うことができます。

リファイナーモデルはSDXL版のimg2imgパイプラインであるStableDiffusionXLPipelineが使われますが、使い方は普段のimg2imgの時とはちょっと違います。

例として今回はアニメ風モデルをベースモデルとして、リアル風モデルをリファイナーモデルとして使ってみます。

アニメ風モデルの方はこれを使います。

リアル風モデルはこれを使います。

import torch
from diffusers import StableDiffusionXLPipeline,StableDiffusionXLImg2ImgPipeline,EulerAncestralDiscreteScheduler
from translate import Translator

device = 'mps'
# ベースモデルのパイプライン
pipe_bs = StableDiffusionXLPipeline.from_single_file(
    '/stadifmodel/AnythingXL_xl.safetensors',
    torch_dtype=torch.float16
).to(device)
pipe_bs.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe_bs.scheduler.config)
# リファイナーモデルのパイプライン
pipe_rf = StableDiffusionXLImg2ImgPipeline.from_single_file(
    'stadifmodel/crystalClearOneVs1_v10.safetensors',
    torch_dtype=torch.float16,
    scheduler=pipe_bs.scheduler # スケジューラーも統一
).to(device)

seed = 111162
generator = torch.Generator(device).manual_seed(seed)
honyaku = Translator('en','ja').translate
prompt = honyaku('猫を抱いている高校制服姿の黒髪少女')
# ベースモデルでまず生成開始
img0 = pipe_bs(
    prompt,
    num_inference_steps=20,
    generator=generator,
    denoising_end=0.4, # ここで途中で生成をやめると指定
    output_type='latent' # 出力を潜在空間と指定
).images
# リファイナーモデルで
img = pipe_rf(
    prompt,
    image=img0,
    num_inference_steps=20,
    generator=generator,
    denoising_start=0.4, # ここで生成を途中から続けると指定
).images[0]
img.save('nekodaku.jpg')

nekodaku111162.jpg

このようにリアルとアニメの間の画風ができます。

ここで重要なポイントはベースパイプラインの方のoutput_type='latent'と指定することです。これは出力を「潜在空間」にするという意味です。これについて詳しくは後で説明しますが、まずもう一つ重要なポイントであるdenoising_endを説明します。これは移り変わりポイントを示すパラメータです。そしてリファイナーパイプラインのdenoising_startもその数値に合わせるのです。値は0から1まで。

例えば0.4を入れると40%までベースモデルで生成して、その後はリファイナーモデルで生成を続けるという過程になります。ここでステップ数(num_inference_steps)が20にしているので8と12に分けられます。

数値が小さいとリファイナーモデルの影響が大きくて、逆に数値が1に近づいたら殆どベースモデルだけ使うのと変わらないことになります。

denoising_enddenoising_startはSDXLではない従来のモデルに使っても効果がないので、この方法はSDXLモデル限定です。

違う数値の比較をしてみます。

import torch
from diffusers import StableDiffusionXLPipeline,StableDiffusionXLImg2ImgPipeline,EulerAncestralDiscreteScheduler
from translate import Translator
import matplotlib.pyplot as plt

def seisei(se):
    pipe_bs = StableDiffusionXLPipeline.from_single_file(
        'stadifmodel/AnythingXL_xl.safetensors',
        torch_dtype=torch.float16
    ).to(device)
    pipe_bs.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe_bs.scheduler.config)
    
    pipe_rf = StableDiffusionXLImg2ImgPipeline.from_single_file(
        'stadifmodel/crystalClearOneVs1_v10.safetensors',
        torch_dtype=torch.float16,
        scheduler=pipe_bs.scheduler
    ).to(device)
    
    generator = torch.Generator(device).manual_seed(seed)
    img0 = pipe_bs(
        prompt,
        num_inference_steps=20,
        generator=generator,
        denoising_end=se,
        output_type='latent'
    ).images
    
    img = pipe_rf(
        prompt,
        image=img0,
        num_inference_steps=20,
        generator=generator,
        denoising_start=se,
    ).images[0]
    return img

device = 'mps'
seed = 100532
honyaku = Translator('en','ja').translate
prompt = honyaku('花園で兎と遊んでいる高校制服姿の日本人少女')

plt.figure(figsize=[6,12.5],dpi=100)
for i in range(8):
    se = 0.1+i*0.1
    img = seisei(se)
    plt.subplot(4,2,i+1,title='%.1f'%se)
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout(pad=0.5)
plt.savefig('usagiasobu.jpg')
plt.close()

usagiasobu.jpg

数値が大きくなるにつれ、リアルからアニメに変わっていっていい感じですね。

latent 〜潜在空間の変換〜

リファイナーを使う時にoutput_type='latent'というのがあったからついでにこれについてここで説明しておきます。

「latent」というのは潜在空間のことです。実は普段Stable Diffusionが画像を生成する時にまず潜在空間という形で生成してその後「ピクセル空間」つまり我々の馴染んでいる画像に変換するのです。

そんな仕組みになっている理由はその方が効果的だからです。潜在空間の画像はピクセル空間よりずっと小さいので計算量も小さいです。そのため潜在空間で生成してその後変換するという手段を取ることで生成が高速化できるわけです。

だからdiffusersでは生成している途中の画像は潜在空間で、終わった後自動的にピクセル空間に変換されるという仕組みになっています。

しかしoutput_type='latent'を指定することで生成が終わった後ピクセル空間に変換せずに潜在空間のままで出力することになります。

リファイナーを使う時にベースパイプラインの出力はまだ生成の途中だから変換する必要がなく潜在空間のままを使うのです。

では具体的に潜在空間というのはどんなものなのかわかるためにとりあえずtxt2imgで普通に画像を生成するが、出力は潜在空間にしてみましょう。

import torch
from diffusers import StableDiffusionPipeline,EulerAncestralDiscreteScheduler
from translate import Translator

model_file = 'stadifmodel/himawarimix_v11.safetensors'
device = 'mps'
seed = 24179
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
generator = torch.Generator(device).manual_seed(seed)
honyaku = Translator('en','ja').translate
prompt = '夜の商店街で油を売っている水色髪メイド'
img_latent = pipe(
    honyaku(prompt),
    num_inference_steps=20,
    generator=generator,
    output_type='latent'
    ).images

print(img_latent.shape) # torch.Size([1, 4, 64, 64])

このように潜在空間は4×64×64ピクセルの画像です。本来出力される画像のサイズは512×512だから潜在空間では1/8のサイズだとわかりますね。だから生成する時にwidthheightの指定は8に割り切れる数値でないといけないのです。

潜在空間の画像はまだそのまま使える画像ではないが、試しに無理矢理画像として表示することも一応できてしまいます。

import numpy as np
import matplotlib.pyplot as plt

imgl = np.float32(img_latent[0].cpu()).transpose(1,2,0)
plt.figure(figsize=[6,6],dpi=100)
plt.imshow((imgl-imgl.min())/(imgl.max()-imgl.min()))
plt.tight_layout()
plt.savefig('aburamaid.png')
plt.close()

aburamaid.png

とりあえずこのように何かぼやっと形が見えますね。

こんなものをピクセル空間の完成品に変換するのはVAEデコーダーです。入門の記事でもちょっとVAEのことを説明しましたが、このVAEはStable Diffusionモデルの重要な一部です。普段チェックポイントモデルの中でVAEが埋め込まれています。diffusersで生成する時にパイプラインの中でVAEが使われて最後の画像に変換するのです。

今回VAEを使わずに潜在空間のまま出力してしまったが、自分でVAEを呼び出して変換させることもできます。このようにします。

from PIL import Image

img1 = pipe.vae.decode(img_latent/pipe.vae.config.scaling_factor)
img1 = img1.sample[0].detach().cpu().numpy().transpose(1,2,0)
img1 = Image.fromarray(np.uint8(np.clip(img1*0.5+0.5,0,1)*255))
img1.save('aburamaid.jpg')

そしてこのような完成した画像ができます。もう普段生成した画像と同じですね。

aburamaid.jpg

基本的にdecodeを呼び出すのはここのポイントですが、それだけでなく他の計算も必要なので自分で変換のコードを書くのはちょっと面倒ですね。

VAEはエンコーダーデコーダーに分けられています。潜在空間からピクセル空間に変換するのはVAEのデコーダー部分ですが、逆にピクセル空間から潜在空間に変換する時にエンコーダー部分が使われます。

実際にimg2imgパイプラインは入力した画像を自動的にVAEエンコーダーによって潜在空間に変換するので、潜在空間で入れてもピクセル空間で入れてもいいようにされています。

普通img2imgとして使う時に実は中でまず入力した画像が潜在空間に変換されるから自分で変換する必要は普段ないと思いますが、あえて自分で変換したいならこのように書きます。

imgl2 = torch.HalfTensor(np.array(img1).transpose(2,0,1)[None,:]/255).to(device)
imgl2 = pipe.vae.encode(imgl2).latent_dist.sample()*pipe.vae.config.scaling_factor

upscale 〜綺麗に拡大する〜

小さな画像を大きな画像にアップスケールするのもStable Diffusionのよく使われる機能の一つです。普段の画像編集ソフトウェアで画像を大きくしたらできた画像は破綻しますが、機械学習モデルで変換することで綺麗な拡大画像ができます。Stable Diffusionもそんなことができるモデルの一つです。

Stable Diffusionでは主にアップスケールに使うモデルが2つあります。

  • x4 upscaler
  • x2 latent upscaler

それぞれの使い方を紹介します。

x4 upscaler 〜元画像から4倍拡大〜

x4 upscalerは画像を4倍サイズにするアップスケーラーのモデルです。

このモデルはhuggingfaceに公開されているので、このリンクで直接ダウンロードすることもできますが、diffusersで使う場合直接ダウンロードせずに.from_pretrainedで自動ダウンロードすることもできます。

このモデルを使うためにStableDiffusionUpscalePipelineというクラスでパイプラインを作ります。

今回は例としてこの画像を使います。サイズは240×240ピクセルで、アップスケールしたら960×960となります。このアップスケーラーは物凄くメモリーを消耗するのでこんな小さい画像でないときついです。

s240.jpg

import torch
from PIL import Image
from diffusers import StableDiffusionUpscalePipeline
from translate import Translator

honyaku = Translator('en','ja').translate
device = 'mps'
pipe = StableDiffusionUpscalePipeline.from_pretrained(
    'stabilityai/stable-diffusion-x4-upscaler',
    torch_dtype=torch.float16,
    variant='fp16',
).to(device)

img0 = Image.open('s240.jpg')

seed = 10000
generator = torch.Generator(device).manual_seed(seed)
prompt = '赤目銀髪少女'
img = pipe(
    honyaku(prompt),
    img0,
    num_inference_steps=20,
    generator=generator,
).images[0]
img.save('s960.jpg')

s960.jpg

ただのアップスケーラーだからプロンプトなんて関係ない……と、最初はそう思ったのですが、実際にプロンプトが必要です。試しにプロンプトを空っぽにしたら結果はこんなによくないのです。関係ないプロンプトを入れても駄目です。Stable Diffusionはプロンプトを解釈してどのように補足するか的確な判断をした上でアップスケールするらしいです。

試しに顔の部分だけ切り取って比較してみたらこんな感じ。

upscale_hikaku.jpg

違いは微妙ですが、確かに適切なプロンプトを入れることで拡大した画像が明瞭になりますね。

x2 latent upscaler 〜潜在空間で2倍拡大〜

もう一つのモデルは潜在空間の中で画像を2倍サイズにするx2 latent upscalerモデルです。

既存の画像に使うx4 upscalerと違って、このモデルは主に生成したばかりでまだピクセル空間に変換していない潜在空間の画像に使うのです。とはいっても実際に既存の画像を入力しても問題ないらしいです。

ただしこのモデルを使う時にプロンプトを入れると悪い結果になるので基本的にプロンプトは空っぽにするのです。なぜかわからないが、プロンプトを入れると画像は目茶苦茶になります。これはx4 upscalerの場合と逆なので注意です。

SDXLではない従来のモデルは512×512が一番得意なサイズであり、1024×1024を直接生成しようとしてもいい結果が出ない場合も多いので、512×512で生成してから1024×1024にアップスケールするのも手らしいです。そんな時はこのアップスケーラーモデルの出番ですね。

では画像を生成してアップスケールしてみます。

import torch
from PIL import Image
from diffusers import StableDiffusionPipeline,StableDiffusionLatentUpscalePipeline,EulerAncestralDiscreteScheduler
from translate import Translator

device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(
    'stadifmodel/himawarimix_v11.safetensors',
    torch_dtype=torch.float16
).to(device)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)

pipe_x2 = StableDiffusionLatentUpscalePipeline.from_pretrained(
    'stabilityai/sd-x2-latent-upscaler',
    torch_dtype=torch.float16,
).to(device)

seed = 10114
generator = torch.Generator(device).manual_seed(seed)
honyaku = Translator('en','ja').translate
prompt = honyaku('窓際で泣いている紫髪吸血鬼少女')
img0 = pipe(
    prompt,
    num_inference_steps=20,
    generator=generator,
    output_type='latent'
).images

img = pipe_x2(
    '',
    img0,
    num_inference_steps=20,
    generator=generator,
).images[0]
img.save('madogiwa1024.jpg')

madogiwa1024.jpg

一応元の512×512の画像を保存して比較してみます。(潜在空間で出力したのでちょっと変換が必要)

import numpy as np

img1 = pipe.vae.decode(img0/pipe.vae.config.scaling_factor)
img1 = img1.sample[0].detach().cpu().numpy().transpose(1,2,0)
img1 = np.uint8(np.clip(img1*0.5+0.5,0,1)*255)
Image.fromarray(img1).save('madogiwa512.jpg')

madogiwa512.jpg

inpaint 〜特定の部分だけ修正〜

最後に、画像をある特定の部分だけ修正を行うためのインペイント(inpaint)機能を紹介します。

インペイント用のモデルはhuggingfaceに公開されています。これもdiffusersでfrom_pretrainedを通じて自動ダウンロードして使うことができます。

インペイントをする時に準備する必要があるのは、修正したい元画像と、修正する部分を指定するマスク画像です。

例えば今回この画像を使います。

jinja_akagami.jpg

修正してみたい部分は女の子が立っているところなのでこのマスク画像を使います。

jinja_masuku.png

マスク画像はRGBでもグレースケールでもいいです。修正したい部分は白で、その他は黒で塗ります。ComfyUIやAUTOMATIC1111 Stable Diffusion web UIだったらこのマスクを作るためのGUIがありますが、diffusersはのそのような機能がないので個別でPhotoshopなどで塗るしかないですね。

ではインペイントを実行してみます。

import torch
from PIL import Image
from diffusers import StableDiffusionInpaintPipeline
from translate import Translator

device = 'mps'
pipe = StableDiffusionInpaintPipeline.from_pretrained(
    'runwayml/stable-diffusion-inpainting',
    torch_dtype=torch.float16,
    variant='fp16',
    safety_checker=None # これがないと真っ暗な画像ばっかりになる
).to(device)

seed = 21359
generator = torch.Generator(device=device).manual_seed(seed)
img0 = Image.open('jinja_akagami.jpg') # 元画像
img_mask = Image.open('jinja_masuku.png') # マスク画像
honyaku = Translator('en','ja').translate
prompt = 'こっちを見て笑っている黒髪和服少女、アニメ風'
img = pipe(
    prompt=honyaku(prompt),
    image=img0,
    mask_image=img_mask,
    num_inference_steps=20,
    generator=generator
).images[0]
img.save('jinja_kurokami.jpg')

jinja_kurokami.jpg

結果は意外と上手くいかないものです。実は試してみたところあまり思い通りにならなかくてがっかりしました。

それにこのインペイントモデルはアニメ画像が得意ではないらしいです。プロンプトの中で一応「アニメ風」を入れてよくなってきたが、それでもまだ微妙です。

ただし実際にcontrolnetという機能でインペイントすることもできます。その方が色々調整できていい結果は期待できそうなので、インペイントするならこのインペイントパイプラインではなく、controlnetを使った方がいいと思います。

controlnet inpaint 〜インペイントの改善版〜

controlnetでインペイントを行う方法については次の記事に書いてあります。

instruct-pix2pix 〜指示通り画像変更〜

img2imgと似ているものとしてinstruct-pix2pixという機能があります。これについて個別の記事に書いてあります。

paint by example 〜指定の部分へ画像を挿入する〜

paint by exampleはinpaintと同じように、画像の一部を修正する機能です。ただしinpaintでは修正したい内容をプロンプトで決めるのに対し、paint by exampleではプロンプトを必要とせず画像で決めるのです。つまり、ある画像のあるところにもう一つの画像を挿入するということです。

使う時はこのモデルを使ってPaintByExamplePipelineクラスでパイプラインを作ります。

paint by exampleを使う時に3枚画像を準備する必要があります。

  • 元画像
  • 修正したい部分マスク画像
  • 挿入したいものが入っている画像

例としてここで元画像をこれにします。

tenohiraneko.jpg

子猫の代わりにこの象を入れてみたいです。

kawaiizousan.jpg

マスク画像。

tenohiraneko_mask.png

では試してみましょう。書き方は簡単です。

import torch
from PIL import Image
from diffusers import PaintByExamplePipeline

device = 'mps'
pipe = PaintByExamplePipeline.from_pretrained(
    'Fantasy-Studio/Paint-by-Example',
    torch_dtype=torch.float16,
).to(device)
img_ex = Image.open('kawaiizousan.jpg')
img0 = Image.open('tenohiraneko.jpg')
img_mask = Image.open('tenohiraneko_mask.png')
img = pipe(
    example_image=img_ex,
    image=img0,
    mask_image=img_mask,
    num_inference_steps=30
).images[0]
img.save('tenohirazou.jpg')

tenohirazou.jpg

なぜかあまり目標の象と似ていなくて期待外れです。

試してみたところ結果は微妙です。実際にこの方法は知名度が低くてググってもあまり情報が多くないみたいです。だからここではこんな方法もあるね、という程度で紹介しておきましたが、恐らくまだ実用的ではないですね。

参考

img2img

refiner

upscale

inpaint

paint by example

9
7
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?