見出し画像

StableDiffusionPipelineの改造に手を出した

マスクの向こう側の世界には何がある?

まだ機能が足りない

 前回の記事の後、体調を崩しましたがすぐ治りました。疲労がたまっていたのかもしれません。
 さて先月にDiffusersベースの自作GUIをある程度作りこんだのですが、素材を作ろうとして機能が足りないところが出てきました。
・768サイズのモデルで画像生成がうまく動かない
→ Diffusers公式のコミュニティパイプラインにあるLPW pipelineなら動作する (Long Prompt Weighting, カッコで重みを増減したり75トークンの壁を越えられるアレ)
・Layered Diffusion(旧unstable diffusionライブラリ)では、75トークンを越えるLPWのエンベッディングを直接使用できない。Shift Encodingのみ可
→ これもLPW Pipelineなら動作する

 応急処置的にモデルサイズを見てLayered DiffusionとLPW Pipelineを切り替えていたのですが、どうにも中途半端。結局問題は解決できてないわけだし。
 一旦、使用シーンから見たライブラリの長所・短所をまとめます。

  • unstable diffusion

    • 長所:inpaintを実装しやすい、高度なプロンプト構造を記述できるのが面白そう

    • 短所:

      • 768サイズのモデルでの画像生成をうまく動かせられない(僕が)

      • LPW pipelineを呼び出してもプロンプトの75トークン制限を越えられない(無理やりなので仕方ない)

  • LPW pipeline

    • 長所:呼び出しやすい、768サイズのモデルで画像生成できる、LPWの機能をそのまま使える

    • 短所:latent noiseを使ったinpaintを実装できない

 これはLPW Pipelineに手を入れるべきなのでは…いやしかしユーザライブラリとはいえ公式のものに手を入れるのはどうなのか…インポートとかどうするんだ…

LPW Pipelineの改造に手を出しました

Readmeから抜粋。他の変更も併せてそこそこ大きな更新になった

 結局避けては通れなかったよ…
 Colabのノートブックに、LPW Pipelineを改造してlatent noiseを使うinpaintを実装したものを書きました。
https://github.com/calm-ixia/SDManualGUI

 Readmeという名のマニュアルを更新するので力尽きたので、あとは開発途中に撮った画像をいくつか貼ることにします。

マスクの向こう側

 Diffusersには、モデルベースのInpaintが実装される前のコードであるInpaintLegacyという実装があります。LPW Pipelineの実装はInpaintLegacyを引き継いでいますが、なぜLegacy、過去の遺物なのか。
 latent noiseを使うinpaintを実装するために、実際に使ってみると、キャラが「マスクに阻まれる」以上に背景が荒れることが多く、しかも外部から潜在空間上の初期データ(latents)を直に与えるとマスクが使えませんでした(同時指定するとエラーが出る。おそらくlatentsとマスクの併用を想定されていない)。
 そこでlatentsとマスクの併用をできるようにしても、背景が荒れてしまいました。InpaintLegacyの実装では、ステップを反復する前に初期画像をlatentsに変換してadd_noiseし、さらに各ステップの終わりにノイズ量を減らしながらadd_noise&マスク処理(マスク適用外の部分を背景画像で上書き)するのですが、おそらくadd_noiseの回数が過剰なのだと思いました。

左:これはこれで雰囲気はあるが…  右:最後にマスク処理で背景を隠す

 背景が荒れるのなら隠せばよい、という動機は理解したので、InpaintLegacyの改造でも最後にマスクをかけることにしました。実際にAUTOMATIC1111 web UIやLayered Diffusionでは最終出力をデコードする前にマスクをかけています。潜在空間上でのマスク処理なので境界はなじみやすいです。とはいえ生成画像が「マスクに阻まれて」惜しい結果になることも多いです。

 ふと、ステップ内のadd_noiseのタイミングを変えるとどうなるか試してみたくなりました。コードを追ってみると、実装によってadd_noiseするタイミングが違っていました。
・Diffusers:デノイズ後、ステップの最後にadd_noise&マスク処理してから次のステップに移る
・Layered Diffusion:ステップの最後にadd_noise&マスク処理。画像レイヤーのみが対象。最終回のステップではadd_noiseしない。
・AUTOMATIC1111版:ステップの最初にadd_noise&マスク処理してからデノイズ

 そこで、ステップ内の最初にadd_noise&マスク処理してみると、マスク内の画質が少し良くなりました。おそらく最終回のデノイズ結果がそのまま最終出力になるためです。Layered Diffusionで最終回のステップでadd_noiseしないのもこれを狙っているのでしょう。

ステップの最初にadd_noise, 最終出力へのマスクあり

 さらに、どうせならadd_noiseやマスク処理をやめてみたらどうなるか試してみました。ステップを始める前に初期データにノイズをかけているので、わざわざ足すこともないですし。
 すると結構よい結果が得られたのですが、キャラが案の定マスクに阻まれていました。残念。でもマスクの向こう側が気になる…

そこで、最終出力のマスクを外してみることにしました。


マスクの向こう側の世界はとんでもなく美しかった。

 サムネイルから全体像を見た時に、思わず声がもれました。手前味噌ですが、今でも見入ってしまいます。マスクを通して呼び出そうとしているキャラはこんな世界の中にいるのかと。
 また自分の作業フローの点でも、キャラを生成した後に背景を書き換える必要がなくなるのも好都合です。
 よく考えたら、ステップ中にadd_noiseやマスク処理をしないのはimg2imgと同じアルゴリズムです。一方で、背景にノイズをマスク処理して書き込んだものを初期データに使うのは、img2imgとも少し違う点です。

 さてこの背景画像ではうまくいったのですが、たまたまかもしれません。それにどんな背景でも同じ結果になるのなら、txt2imgと大差ありません。そこで、マスクにも使う白一色でも試してみました。

女神かと思った

 白色の影響か、光輝いて見えます。見えました。一応、背景画像の影響や変化もありそうです。
 よく見ると腕の描写や体の軸がおかしいのですが、DPM++2Mでステップ8回の描画なので、まだ多少ずれます。それよりも雰囲気の描写に感心しました。

 ちなみにプロンプトは以前のものと同じです。いつもの左上の子はいなくなってしまった…

Layered Diffusionでの生成
改造したLPW Pipelineでの生成

aesthetic official photogenic detailed pastel CG anime illustration of one full-body girl in landscape,
nubile lady, purple eyes, pink hair, lovely, intelligent, mysterious, standing, slender body, relaxed smile, fair skin, slender breasts,
light novel, manga, delicate fine, (Pissarro:0.98),
benzoin

Negative prompt: inaccurate or extra limb, bad, ugly, cropped head, out of frame, watermark, signature, text, jpeg artifacts, blurry, monochrome, nude

Steps: 8, Sampler: DPM++ 2M, CFG scale: 7.0, Seed: 20725624, Size: 512x512, Denoising strength: 1.0, Masked content: latent noise, Mask timing: none, Mask output: False

と思ったらtxt2imgの方で出てきました。気まぐれというかなんというか。見つかって少しホッとしました。

txt2imgでの同パラメータの結果

素材を作ろう

 試行錯誤して、自作GUIは当初は想像していなかったツールになってきました。すでにある道具にちょっと手を加えて自分の使いたいものに仕立てるのもDIYの面白さ。これで素材作成が進みそうです。


この記事が気に入ったらサポートをしてみませんか?