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の改造に手を出しました
結局避けては通れなかったよ…
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やマスク処理をしないのはimg2imgと同じアルゴリズムです。一方で、背景にノイズをマスク処理して書き込んだものを初期データに使うのは、img2imgとも少し違う点です。
さてこの背景画像ではうまくいったのですが、たまたまかもしれません。それにどんな背景でも同じ結果になるのなら、txt2imgと大差ありません。そこで、マスクにも使う白一色でも試してみました。
白色の影響か、光輝いて見えます。見えました。一応、背景画像の影響や変化もありそうです。
よく見ると腕の描写や体の軸がおかしいのですが、DPM++2Mでステップ8回の描画なので、まだ多少ずれます。それよりも雰囲気の描写に感心しました。
ちなみにプロンプトは以前のものと同じです。いつもの左上の子はいなくなってしまった…
と思ったらtxt2imgの方で出てきました。気まぐれというかなんというか。見つかって少しホッとしました。
素材を作ろう
試行錯誤して、自作GUIは当初は想像していなかったツールになってきました。すでにある道具にちょっと手を加えて自分の使いたいものに仕立てるのもDIYの面白さ。これで素材作成が進みそうです。
この記事が気に入ったらサポートをしてみませんか?