instruct-pix2pixで画像を指示した通り変更したり

この記事ではStable Diffusionの機能の人つである**instruct-pix2pix**及びその派生である**controlnet instruct-pix2pix**について説明します。


# instruct-pix2pixとは？　img2imgとどう違うの？

[**instruct-pix2pix**](https://github.com/timothybrooks/instruct-pix2pix)は画像を指示した通り変更するStable Diffusion機能です。

ただそもそもStable Diffusionには似ているものとしてimg2imgがあります。以前の記事で説明してあります。

https://qiita.com/phyblas/items/eed011fd01f5afd0f759

img2imgの方は知名度が高くて一般的に使われていますね。だから「どう違うか？img2imgで良くないか？」と疑問に思うでしょう。

確かにinstruct-pix2pixもimg2imgも、ある画像を元にして新しい画像に変更する機能です。それでも変換の仕様は違います。

img2imgはプロンプトに書いた内容の通り「元画像を参考にしてどうやって新しい画像を作る」というのに対し、instruct-pix2pixは「元画像をどうやって改造する」という感じです。

だからinstruct-pix2pixを使ったら全体的に結果は元画像のままでプロンプトに指示された内容との関係がある部分だけ変えられるが、img2imgは全体変えられます。プロンプトの内容と関係ない部分であっても勝手に決められます。

ということで原型を残したまま一部だけ変更したい場合はimg2imgよりinstruct-pix2pixの方がいいと思えます。

もう一つの違いはプロンプトの書き方です。img2imgでは欲しい結果画像の姿を描写するという形でプロンプトを唱えるのに対し、instruct-pix2pixを使う時に「これに変えたい」という書き方です。

例えば少女の画像があってその少女を笑わせたい場合、img2imgは「笑顔をしている少女」と書いたらいいのですが、instruct-pix2pixでは「少女を笑顔にする」みたいな書き方をするのです。

又、一部だけ変えたいというと、inpaintかcontrolnet inpaintという機能があります。これも前回の記事で説明してあります。

https://qiita.com/phyblas/items/7cacb9297650afd63d34

それを使うのは一番だと思いますが、inpaintを使うためには修正したい部分を塗ってマスク画像を作っておく必要があります。

それに対し、instruct-pix2pixはマスクなんて必要せず、どこを修正するか自動的に決められてしまいます。例えば顔の表情を変えたい場合顔の部分のマスクをする必要もなく、自動的にどこが顔かわかって適切に修正してくれるのです。凄い便利ですね！

とはいえ、やはり間違えることもありますし、望まない部分も修正される可能性があるので、細かく修正したい場合はまだあまり向いていないかもしれません。

尚、そもそも「pix2pix」というのは[敵対的生成ネットワーク（GAN）](https://ja.wikipedia.org/wiki/敵対的生成ネットワーク)によって作られた画像生成モデルの名前です。instruct-pix2pixも画像から別の画像を作るというところは同じですが、GANを使っているわけではなく、本来のpix2pixとは違う別のモデルであり、直接関係がないらしいです。

だからinstruct-pix2pixを単にpix2pixと呼んだら紛らわしい気がします。それはJavaScriptをJavaと呼ぶのと同じようなものかもしれません。


# controlnet instruct-pix2pixとは

instruct-pix2pixはそもそも2023年1月に[timbrooks等](https://arxiv.org/abs/2211.09800)によって発表された機能で、[github](https://github.com/timothybrooks/instruct-pix2pix)に公開してあります。

huggingfaceにもモデルが公開されて、diffusersから簡単に使えます。

https://huggingface.co/timbrooks/instruct-pix2pix

しかしその後2023年4月に[<ruby>張呂敏<rt>チャン リュミン</rt></ruby>（lllyasviel）](https://huggingface.co/lllyasviel)はcontrolnet 1.1を公開して、色々な機能の中でinstruct-pix2pixと同じ機能を持つものも含まれています。ここではtimbrooks氏の従来のinstruct-pix2pixと区別するために「controlnet instruct-pix2pix」と呼びます。

https://huggingface.co/lllyasviel/control_v11e_sd15_ip2p

controlnetって、新鮮な機能を導入するだけでなく、以前からあった機能の改善版もしますね。inpaintもそうです。これについて[前回のcontrolnet inpaintの記事](https://qiita.com/phyblas/items/7cacb9297650afd63d34)にも書いてあります。

だからこれはcontrolnet版のinstruct-pix2pixとも言えます。従来のinpaintに対してcontrolnet inpaintがあるのと同じように、従来のinstruct-pix2pixに対してcontrolnet instruct-pix2pixがあります。

そして双方の相違点も同じようなところです。従来のinstruct-pix2pixはinstruct-pix2pix用のモデルだけで実行するのに対し、controlnet instruct-pix2pixは同時にベースとする別のチェックポイントモデルを必要とします。だからcontrolnet instruct-pix2pixはの方が色々調整できて使い勝手がいいです。

とはいえ、使ってみたところ従来のinstruct-pix2pixも随分いいものだと思います。従来のinpaintみたいにアニメ画像が苦手なわけではないらしい。

だからここでは従来のinstruct-pix2pixもcontrolnet instruct-pix2pixも紹介したいです。


# instruct-pix2pixを使ってみる

まずは従来のinstruct-pix2pixです。これは`StableDiffusionInstructPix2PixPipeline`クラスでパイプラインを作成して使うことができます。使い方は大体img2imgと同じです。必要なのは元画像とプロンプトくらい。

今回は例としてこの元画像を使います。

![nekomimi_hanasono.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/0150dde7-6d6a-ae61-ff70-c6ed62982e7b.jpeg)

この画像は綺麗な花園ですが、instruct-pix2pixを使って雪の中にしてみます。

```python
import torch
from PIL import Image
from diffusers import StableDiffusionInstructPix2PixPipeline
from translate import Translator

device = 'mps'
pipe = StableDiffusionInstructPix2PixPipeline.from_pretrained(
    'timbrooks/instruct-pix2pix',
    torch_dtype=torch.float16,
    safety_checker=None
).to(device)

seed = 21910
generator = torch.Generator(device=device).manual_seed(seed)
img0 = Image.open('nekomimi_hanasono.jpg')
honyaku = Translator('en','ja').translate
prompt = '雪の中の場面にする'
img = pipe(
    honyaku(prompt),
    image=img0,
    num_inference_steps=20,
    image_guidance_scale=1,
    generator=generator
).images[0]
img.save('nekomimi_hanasono_ip2p.jpg')
```

結果はこうなります。

![nekomimi_hanasono_ip2p.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/7ebd2231-17d3-e9d7-cef6-fb9cedb15522.jpeg)

木も花も殆どそのままで雪を被らせるというのは凄いですね。それに今回はシーンに関するプロンプトなので人物のところに殆ど変更がありません。逆に表情などに関するプロンプトなら背景は殆ど変わりません。


## image_guidance_scale

instruct-pix2pixのパイプラインを実行する時に`image_guidance_scale`は大きく影響を与えるパラメータの一つです。これはどれくらい画像を変えるかを決める値です。`1`以上の値が入れられます。

既定値は`1.5`ですが、そのまま使ってもあまり元と変わらない画像ができるので普段は`1`を使うのは一般的らしいです。

違う`image_guidance_scale`の値の結果を比較してみましょう。
```python
import torch
from PIL import Image
from diffusers import StableDiffusionInstructPix2PixPipeline
from translate import Translator
import matplotlib.pyplot as plt

def seisei(igs):
    pipe = StableDiffusionInstructPix2PixPipeline.from_pretrained(
        'timbrooks/instruct-pix2pix',
        torch_dtype=torch.float16,
        safety_checker=None
    ).to(device)
    generator = torch.Generator(device=device).manual_seed(seed)
    
    return pipe(
        honyaku(prompt),
        image=img0,
        num_inference_steps=20,
        image_guidance_scale=igs,
        generator=generator
    ).images[0]

device = 'mps'
img0 = Image.open('nekomimi_hanasono.jpg')
honyaku = Translator('en','ja').translate
prompt = '砂漠の場面にする'
seed = 55573

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

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

![nekomimi_hanasono_igs.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/c1a5eae8-46cc-b9f3-65ac-caf9c271eed6.jpeg)

今回砂漠に変えるというプロンプトだから花も消えますね。

その他にも`guidance_scale`は大事なパラメータでもありますが、これはtxt2imgとimg2imgの場合と同じ概念なので割愛します。


## 一応SDXL版もありますが……

SDXL版と言われるinstruct-pix2pixも公開されています。これは主に768×768ピクセルの画像に使うためらしいです。

https://huggingface.co/diffusers/sdxl-instructpix2pix-768

しかし使ってみたところあまりいい結果が出なくてがっかりしました。だからここには紹介しません。

使い方はほぼ同じで、パイプラインオブジェクトを作る時に`StableDiffusionInstructPix2PixPipeline`から`StableDiffusionXLInstructPix2PixPipeline`に変えるくらいです。

上の例と同じ入力画像（ただしサイズは768×768）と同じプロンプトで試した結果です。望んだ結果とはかけ離れていなますが、これもこれで面白いので一応載せておきます。

![yukinoip2pxl21907_g1.5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/5b990e52-25e2-83a8-8364-77c729c0f1dc.jpeg)


# controlnet instruct-pix2pixを使ってみる

次はcontrolnet instruct-pix2pixの方を使ってみます。比較するために最初の例と同じ入力画像で同じプロンプトを使います。

controlnet instruct-pix2pixを使う時にベースのチェックポイントモデルが必要なので、今回HimawariMixを使います。

https://civitai.com/models/131611/himawarimix

```python
import torch
from PIL import Image
from diffusers import StableDiffusionControlNetPipeline,ControlNetModel,EulerAncestralDiscreteScheduler
from translate import Translator

device = 'mps'
controlnet = ControlNetModel.from_pretrained(
    'lllyasviel/control_v11e_sd15_cip2p',
    torch_dtype=torch.float16
).to(device)
pipe = StableDiffusionControlNetPipeline.from_single_file(
    'stadifmodel/himawarimix_v11.safetensors',
    controlnet=controlnet,
    torch_dtype=torch.float16
).to(device)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)

seed = 77205
generator = torch.Generator(device=device).manual_seed(seed)
img0 = Image.open('nekomimi_hanasono.jpg')
honyaku = Translator('en','ja').translate
prompt = '雪の中の場面にする'
img = pipe(
    honyaku(prompt),
    image=img0,
    num_inference_steps=25,
    generator=generator
).images[0]
img.save('nekomimi_hanasono_cp2p.jpg')
```

![nekomimi_hanasono_cip2p.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/636bf872-e15e-d661-8f35-6f506f55149e.jpeg)

従来のinstruct-pix2pixと比べて変化が大きいです。服も背景も雪の場面に合わせていますね。それでも元の画像の構造は大体そのままです。

ただしランダム性も従来のinstruct-pix2pixより高くて、何度試して色々違う結果ができます。このように花園そのまま雪だけ入れられるという結果もあります。

![nekomimi_hanasono_cip2p77218.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/11042f3e-9248-e4f2-7779-04384ae63d71.jpeg)


## 色んなプロンプトで試そう

その他にも例えば「炎の中の場面にする」と書いたらこうなります。

![honoononaka21901-.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/dec87730-c805-e87e-ba4e-1a5dc4ff5db0.jpeg)

本当に花園が燃えているみたいですね。

「森の中の場面にする」

![morinonaka21901.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/616f5d0d-bcb8-2047-472e-55669eaf326b.jpeg)

「山中の場面にする」

![sanchuu21901.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/26b56b2f-5a58-83f8-7c8e-b98855cc3384.jpeg)

「砂漠の場面にする」

![nekomimi_hanasono_cp2p砂漠の場面にする.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/02bf9f6f-a1d6-4300-e073-d462c6f795ca.jpeg)

場所の指定はここまでにしましょう。では試しに「着物姿に着替える」としたら場所は関係なくただ服が変えられるだけになって凄いです。

![kimono21903.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/b5caba9f-06c9-cd42-1c22-a7707e3f62fa.jpeg)


## controlnet_conditioning_scale

controlnet instruct-pix2pixだけの話ではないのですが、controlnetパイプラインを実行する時に`controlnet_conditioning_scale`というパラメータがあります。これはコントロール画像の影響の重みを決める大事なパラメータです。既定値は最大値の`1`です。`1`より小さい値にしたら入力画像の影響が薄くなります。

controlnet instruct-pix2pixの場合`0.6`になる時点でもはや元画像の原型が残らずただプロンプトに書いたままの画像になります。

では違う`controlnet_conditioning_scale`の結果を比較してみましょう。入力は今までと同じ画像で、今回は浜辺にします。
```python
import torch
from PIL import Image
from diffusers import StableDiffusionControlNetPipeline,ControlNetModel,EulerAncestralDiscreteScheduler
from translate import Translator
import matplotlib.pyplot as plt

def seisei(ccs):
    controlnet = ControlNetModel.from_pretrained(
        'lllyasviel/control_v11e_sd15_ip2p',
        torch_dtype=torch.float16
    ).to(device)
    pipe = StableDiffusionControlNetPipeline.from_single_file(
        'stadifmodel/himawarimix_v11.safetensors',
        controlnet=controlnet,
        torch_dtype=torch.float16
    ).to(device)
    pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)

    return pipe(
        honyaku(prompt),
        image=img0,
        num_inference_steps=25,
        controlnet_conditioning_scale=ccs,
        generator=torch.Generator(device=device).manual_seed(seed)
    ).images[0]

device = 'mps'
seed = 77799
img0 = Image.open('nekomimi_hanasono.jpg')
honyaku = Translator('en','ja').translate
prompt = '浜辺の場面にする'

plt.figure(figsize=[6,9.5],dpi=100)
for i in range(6):
    ccs = 0.6+0.08*i
    plt.subplot(3,2,i+1)
    plt.imshow(seisei(ccs))
    plt.title('controlnet_conditioning_scale = %.2f'%ccs,size=11)
    plt.axis('off')
    torch.mps.empty_cache()

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

![nekomimi_hanasono_ccs.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/7184e4e5-0ec5-c0b8-a02b-4a1d8c90e893.jpeg)

`controlnet_conditioning_scale`が少ないと、少女の姿が消えてただ書いた通りの浜辺の場面になりますね。


## 感情を変えてみる

controlnet inpaintみたいにcontrolnet instruct-pix2pixも表情を変えるのに使うことができます。結果はcontrolnet inpaintほどよくないが、マスク画像を準備する必要なくすぐ使える分はメリットですね。

今回はこれを元画像に使います。

![nekomimi_hanami.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/941f883b-2a8a-4709-d79d-c08704cdfbcc.jpeg)

プロンプトは「泣き顔にする」。コードは大体同じでただ画像ファイルの名前とプロンプトを変えるだけなので省略します。

結果はこうなります。

![nekomimi_hanami_cip2p.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/c40670bc-5cbc-0fef-2816-f9444f0a7b7c.jpeg)

物凄く苦しそうな泣き方で可哀想です……。

でもやはりcontrolnet inpaintの方がもっと綺麗にできますね。


## 文字をお洒落にする

controlnet instruct-pix2pixで文字の画像を弄ったら看板やロゴみたいにお洒落に変えることもできそうなので試してみました。

例えばこのように簡単なコードで作成されたただ文字が地味に書かれているだけの画像です。

```python
from PIL import Image, ImageDraw, ImageFont

img = Image.new('RGB',(512,512),(200,200,240))
draw = ImageDraw.Draw(img)
font = ImageFont.truetype('MEIRYOB.TTC',120)
draw.text((15,0),'おいしい',fill=(120,80,90),font=font)
font = ImageFont.truetype('MEIRYOB.TTC',180)
draw.text((85,140),'博多',fill=(220,110,80),font=font)
font = ImageFont.truetype('MEIRYOB.TTC',120)
draw.text((15,350),'ラーメン',fill=(150,80,10),font=font)
img.save('ohara.png')
```

![ohara.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/ab0627ee-b437-b991-1d1c-edf300401773.png)

生成コードはこの記事を参考に。

https://qiita.com/YoshikiIto/items/ace92dc9b05e375b8326

この画像を入力として、落書きに変えてみます。

```python
import torch
from PIL import Image
from diffusers import StableDiffusionControlNetPipeline,ControlNetModel,EulerAncestralDiscreteScheduler
from translate import Translator

device = 'mps'
controlnet = ControlNetModel.from_pretrained(
    'lllyasviel/control_v11e_sd15_ip2p',
    torch_dtype=torch.float16
).to(device)
pipe = StableDiffusionControlNetPipeline.from_single_file(
    'stadifmodel/himawarimix_v11.safetensors',
    controlnet=controlnet,
    torch_dtype=torch.float16
).to(device)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)

seed = 21899
generator = torch.Generator(device=device).manual_seed(seed)
img0 = Image.open('ohara.png')
honyaku = Translator('en','ja').translate
prompt = '落書きにさせる'
img = pipe(
    honyaku(prompt),
    image=img0,
    num_inference_steps=20,
    generator=generator
).images[0]
img.save('ohara_rakugaki.jpg')
```

![ohara_rakugaki.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/4eb48d0b-4573-31e6-75ee-ca4a3be4550d.jpeg)

壁に書いたような落書きっぽい文字になりましたね。もっと工夫したら使えそうです。


# 参考

- [論文まとめ：InstructPix2Pix: Learning to Follow Image Editing Instructions](https://blog.shikoan.com/instruct-pix2pix/)
- [AI を自分好みに調整できる、追加学習まとめ ( その６: Instruct Pix2Pix ) ](https://note.com/te_ftef/n/n1a9263c58e7a)
- [Instruct pix2pix | CDLEコミュニティサイトβ版](https://cdle.jp/blogs/307c1fef7e9a)
- [Instruct-pix2pixで、画像を文で編集する](http://cedro3.com/ai/instruct-pix2pix/)
- [instruct pix2pix：プロンプトだけを使ってAIでイラストの修正を行う](https://webbigdata.jp/post-17904/)
- [ControlNet – instruct pix2pix（ip2p）の特性や使い方を解説 / Stable Diffusion web UI](https://taziku.co.jp/blog/tech/ai-gen/controlnet-instruct-pix2pixip2p)
- [Google Colab ではじめる Instruct-Pix2Pix](https://note.com/npaka/n/nc5aa734ef6ed)
- [【StableDiffusionXLInstructPix2PixPipeline】ある画像に対して「写っている犬を猫に変えて」みたいなプロンプトで新しい画像が生成できる Instruct-Pix2Pix が SDXL に対応したのでさっそく使ってみました](https://touch-sp.hatenablog.com/entry/2023/09/08/095340)
- [ControlNet 1.1 新機能instruct pix2pix（ip2p）を試す。](https://kindanai.com/manual-controlnet-instruct-pix2pix/)
- [新時代の pix2pix？ ControlNet 解説](https://zenn.dev/minux302/articles/e47be4bcd14453)
- [「instruct pix2pix（ip2p）」機能をわかりやすく解説ー画像の一部を命令で変更【ControlNet講座#2】｜Stable diffusion](https://minorgame.syowp.com/archives/cnet-2-ip2p.html)
- [ColabでControl Netをお試し　〜落書きも構図もボーンもpix2pix〜](https://qiita.com/Hanull/items/9477eb74066a9bdba606)
- [【Stable Diffusion】instruct pix2pix：ブロンプトで画像の修正をする](https://gift-by-gifted.com/stable-diffusion-instruct-pix2pix/)