diffusers v0.20.0 で対応された GLIGEN (Grounded Language-to-Image GENeration) を試してみます。
GLIGEN はバウンディングボックスとその矩形に対するキャプションを用いて、プロンプト+キャプション付き矩形で Text-to-Image を行うようです。
はじめに
diffusers v0.20.0 のリリースノートでは、2つの方法が紹介されています。
1つは Grounded generation で、もう1つは Grounded inpainting です。
Grounded generation は通常の Text-to-Image に キャプション付き矩形 を加えたものになり、 Grounded inpainting は、キャプション付き矩形で Inpaint を行うものになります。マスク画像の代わりにキャプション付き矩形を扱う感じになります。
Grounded generation
モデルのロード等
パイプラインの準備は非常に簡単です。Adapter や ControlNet は不要のため StableDiffusionGLIGENPipeline でモデルをロードするだけとなります。
import torch from diffusers import StableDiffusionGLIGENPipeline # パイプラインの準備 pipe = StableDiffusionGLIGENPipeline.from_pretrained( "masterful/gligen-1-4-generation-text-box", variant="fp16", torch_dtype=torch.float16 ).to("cuda")
バウンディングボックスの準備
boxes = [[0.1387, 0.2051, 0.4277, 0.7090], [0.4980, 0.4355, 0.8516, 0.7266]] phrases = ["a waterfall", "a modern high speed train running through the tunnel"]
ボックスは、数値の二次元配列となり若干わかりづらいですが、 [x1, y1, x2, y2]
の配列のようです。
数値自体は 0~1の数字に見えるので、出力画像の百分率で矩形位置を指定するようです。
パイプライン実行
# パイプラインの実行 prompt = "a waterfall and a modern high speed train running through the tunnel in a beautiful forest with fall foliage" image = pipe( prompt=prompt, gligen_boxes=boxes, gligen_phrases=phrases, gligen_scheduled_sampling_beta=1, ).images[0] # 実行結果 image
見慣れないパラメータとしては gligen_
から始まるパラメータですね。 gligen_boxes
と gligen_phrases
は前段階で準備した矩形とそのキャプションになるのでわかるとして、 gligen_scheduled_sampling_beta
に何か 1
を設定しています。
Google 翻訳 — GLIGEN のスケジュールされたサンプリング係数: Open-Set Grounded Text-to-Image Generation。スケジュールされたサンプリング係数は、品質と制御性を向上させるために、推論中にスケジュールされたサンプリングに対してのみ変更されます。
よくある、係数でした。 1
に近づくほど GLIGEN の影響力が大きくなるとかでしょうか?
実行結果
実行結果が正しいか確認してみます。
プロンプトの確認
Google 翻訳 — 滝と紅葉の美しい森のトンネルを走る現代の高速列車
滝 , 紅葉の美しい森 現代の高速列車 というキーワードがあり、どこに何が描画されるかプロンプトからはわかりませんが、結果はあっていそうです。
キャプションの確認
バウンディングボックスにより 滝の位置 と 現代の高速列車の位置 が指示されました。
矩形を可視化
次に boxes について、数値のままだとどこを指定しているのかわからないので、可視化してみます。
期待通りの位置に描画されています。
Grounded inpainting
続いて、キャプション付き矩形による Inpaint です。通常の Inpaint や ControlNet の Inpaint は以下でやりました。
モデルのロード等
GLIGEN 版の Inpaint では StableDiffusionGLIGENPipeline を使います。パイプライン自体は Grounded generation と同じです。ロードするモデルが異なります。
import torch from diffusers import StableDiffusionGLIGENPipeline # パイプラインの準備 pipe = StableDiffusionGLIGENPipeline.from_pretrained( "masterful/gligen-1-4-inpainting-text-box", variant="fp16", torch_dtype=torch.float16 ).to("cuda")
元画像の準備
from diffusers.utils import load_image # 元画像 init_image_url = "https://hf.co/datasets/huggingface/documentation-images/resolve/main/diffusers/gligen/livingroom_modern.png" init_image = load_image(init_image_url)
バウンディングボックスの準備
# 枠の設定と枠の情報 boxes = [[0.2676, 0.6088, 0.4773, 0.7183]] phrases = ["a birthday cake"]
パイプライン実行
# パイプラインの実行 prompt = "a birthday cake" image = pipe( prompt=prompt, gligen_phrases=phrases, gligen_inpaint_image=init_image, gligen_boxes=boxes, gligen_scheduled_sampling_beta=1, ).images[0] # 実行結果 make_image_grid([init_image, image], rows=1, cols=2)
実行結果
さて Grounded generation 同様、結果が正しいか確認してみます。
プロンプトの確認
プロンプトからはどこが Inpaint されるかわかりませんが、ケーキらしきものは描画されています。
キャプションの確認
バウンディングボックスにより バースデーケーキの位置 が指示されました。
矩形を可視化
どうやら期待通りに出力できていそうです。
ちなみに元画像が「640x640」で、出力画像が「512x512」でしたが、 バウンディングボックスは百分率指定なので、元画像が大きくても矩形位置に影響はありませんでした。
まとめ
Inpaint については、 バウンディングボックス を用意するか、 マスク画像 を用意するかの違いで、準備のしやすさに違いは出ますが、
Text-to-Image の方は、プロンプトに追加して更に位置を指示できるので、期待通りの構図を作りやすそうです。
GLIGEN でモデルを好きなものに変えられたら凄そうです。