暗くしたい、明るくしたい、色調を変えたい (主に img2img)

生成される画像の色や明るさの方向性を限定したいことがあります。 全体的に明るくカラフルにしたいとか、全体的に暗くして光を強調したい、というような場合です。

このページでは、単純な方法から多少複雑な方法まで、明度、コントラスト、色調の各種コントロール手法とその得失について解説します。

まとめ
  • 個人的には『生成した画像を使って img2img する』が好き。標準ワークフローに組み込み済み。
プロンプトで頑張る epi_noiseoffset を使う グレー単色画像を使って img2img する 読み込んだ画像を使って img2img する 生成した画像を使って img2img する
全体的な色の制御 × ×
座標指定的な色の制御 × × ×
全体的な明るさの制御 ほぼ ×
座標指定的な明るさの制御 × × ×
全体的な高コントラスト化 ほぼ × ○△?
生成画像のバリエーション ○△
試行錯誤の簡単さ ×

要外部ツール

サイクルタイムの短かさ ほぼ ○

目次

VAE を確認する

大前提ですが、使用するモデルに適合する VAE を使用してください。 適合しないと、色が薄くなったり顔が崩れやすくなったりします。 困ったことに、モデルに適合しない VAE がそのモデルのチェックポイントファイルに同梱されていることが珍しくありません。 使用するモデルの説明を確認してください。

説明に無い場合、あるいは他の VAE を試したい場合は、mse (vae-ft-mse-840000-ema-pruned) が第一選択です。 NovelAI (NAI) の派生モデルの場合は、NovelAI の VAE (かその派生版) も試すべきです。 VAE の読み込みは VAELoader で行います。

CLIP を確認する

CLIP の CLIPSetLastLayer の設定 (stop_at_clip_layer) は、使用するモデルに合わせてください。 適合しないと、プロンプトの効きが悪くなったり、色が混ざりやすくなったりします。 使用するモデルの説明を確認してください。

Stable Diffusion v2 のモデルなら、(今のところは) 設定無しの読み込んだままで大丈夫です。

Stable Diffusion v1 のモデルで説明に無い場合は、実際に -1 と -2 を試して比較した方が良いでしょう。 booru タグを使えるモデルでは -2 が良いことが多いです。 1girl, red_hair, blue worker suit などが比較しやすいです。

また、最初のトークンが効かないなどの症状のある壊れた CLIP がモデルに同梱されていることがあります (古いモデルに多い)。 正しいと分かっている別の CLIP を CheckpointLoaderSimpleCLIPLoader で読み込んで使うことで回避できます。 しかし、その壊れた CLIP 前提で U-Net が学習されてしまっている場合は、望まぬ副作用があるかもしれません。

プロンプトで頑張る

単純明快、プロンプトに色などの指示を入れる方法です。

色に関する指定 (psychedelic, red, pale color, colorful, vivid color など) は、効果があります。 工夫すれば、ある程度効果範囲を限定することもできます。 ただ、StableDiffsion v1 では、関係無い所にまで色指定の影響が漏れる現象 (color bleeding) がかなり出やすいです。 v2 では軽減されましたが、それでも出ます。 CLIP レベルでの color bleeding への対策としては、ConditioningConcat が有効です。

明るさ指定 (dark, dimly lit, low key など) は、効いても小さな領域限定です。 画像全体を平均して見ると、どうしても中間に近くなります。

高コントラスト化 (rim lighting, two tone lighting など) は、明るさ指定がうまくいっている部分に関しては、わずかに効果があるような気がします。

epi_noiseoffset を使う

epi_noiseoffset (civitai) という、メジャーな LoRA を使う方法です。 Stable Diffsion v1 限定です。 プロンプト中の明るさやコントラストに関する記述が反映されやすくなります。 読み込みと強さの指定には LoraLoader を使います。

色に関する指定 (psychedelic, red, pale color, colorful, vivid color など) は通常通りで、効果があります。

明るさ指定 (dark, dimly lit, low key など) は、通常より良く効くようになります。

高コントラスト化 (rim lighting, two tone lighting など) も、通常より良く効く、ということになっています。

グレー単色画像で img2img する

色無しのグレー単色画像 (妙な呼び方ですが) を用意してそれを元に img2img することで、生成される画像の明るさをコントロールする方法です。

指定色の latent 画像や pixel 画像を直接生成する方法は今のところ無いので、SolidMask で望みの値のマスクを作って MaskToImage で pixel 画像に変換し、VAEEncode で latent 画像に変換し、KSampler に入れる、という手順になります。

KSamplerdenoise は 1.0 で良いでしょう。 明度コントロールが弱いようなら denoise を少し下げることで対処できます。

ComfyUI 内で完結する上に、値を一個変更するだけで明るさをほぼ確実にコントロールできるので、使いやすい方法です。 色付き単色画像を使えば色のコントロールもできるのですが、ComfyUI 内だけで完結する方法が無いので、次項と同じ方法になってしまいます。

任意の画像を読み込んで img2img する

あらかじめ用意した画像を使えるなら、明るさだけではなく色やおおまかな配置などもコントロールできるようになります。 やることは普通の『img2img』そのものです。

LoadImage で読み込み、VAEEncode で latent 画像に変換し、KSampler に入れる、という手順になります。

与える画像は、単色、2領域の塗り分け、単純なグラデーションといったものでも良いし、詳細に描き込まれたものでも大丈夫です。 ただし、与える画像の中にモデルが好む絵柄やプロンプトに適合しやすい模様が含まれていると、生成画像がそれらに引っ張られます。 コントロール手段として使うこともできますが、生成画像のバリエーションがかなり限定されることにもなります。

KSamplerdenoise は、色や配置をコントロールしたいなら低め (0.7 〜 0.95)、そうでないなら高め (0.95 〜 1.0)、といったあたりが目安です。

あらかじめ外部のツールで画像を作ってサーバ側から見えるようにしておかないといけないため手順が多く、試行錯誤しにくいのが難点です。

コントロール用画像を生成して img2img する

上の img2img の応用です。 コントロールに使う画像を読み込む代わりに KSampler で生成する方法です。 青空などのコントラストの低い画像や、黒地に黄色いボールのようなコントラストの高い画像を生成してそのまま使うこともできるし、マンダラやフラクタルなどの模様を生成し一部を切り出してサイズを戻すことでも、使いやすい原型を得ることができます。

上の例では暗いマンダラを生成して使用しています。 下の例ではマンダラを生成してから暗くして使用しています。 下のようにする方が明るさのコントロールは直接的になります。

拡大時の upscale_method は、bilinear の方が良いでしょう。 nearest-exact だと余計な模様が混入しやすくなります。

前段のプロンプトで、色を指定できます。 前段のプロンプトは後段の被写体の種類に直接影響しないため、"gold fractal flair energy flow" など自由な記述が可能です。

明るさ、色、構図を固定したい時は、前段の seed を固定して、切り出す範囲を微調整します。

コントロールのしやすさと試行錯誤のしやすさを両立した、優れた方法と言えます。

おまけ: 明度のコントロールが効きにくい理由 (の仮説)

Stable Diffusion おいて、明度のコントロールが効きにくい (中庸になりやすい。そして結果としてコントラストが低くなる) ことは経験的に知られていました。 Nicholas Guttenberg による blog 記事 Diffusion With Offset Noise (2023-01-30 付) で、その理由と解決方法の一つが考察されています。

記事では、Stable Diffusion に真っ黒な画像を学習させても真っ黒な画像を生成できないこと等を確認しています。 その上で、順方向プロセスにおいて画像全体に及ぶような低周波成分はノイズの影響を受けにくい = 学習されにくい、として、学習時のノイズの問題を指摘しています。 そして解決手法として、学習時に画像全体に一様に影響するようなノイズを追加で加算する方法を提案し、実際に効果があったとしています。

学習済みチェックポイントも公開されていますが、同手法をベースに LoRA 化されたもの (とされているもの) が、このページでも紹介している epi_noiseoffset です。

おまけ: 明度のコントロールが効きにくい理由 (の仮説その 2)

"Common Diffusion Noise Schedules and Sample Steps are Flawed" (2023-05-15 付) で、更なる考察と解決方法の提案がされました。

論文では、学習時の順方向の最大タイムステップにおいて SNR が 0 でなく画像の低周波成分が残ってしまっているため、低周波のノイズを学習する機会が失われていることが根本の原因だとしています。 推論時に加えられるノイズは原点を中心とした分布なのにそれに対応していない、ということです。 また、一部の実装では画像生成時のノイズスケジュールが中途半端な所から始まることも指摘しています。

解決策として、1. 学習時のノイズスケジュールを変更する (最終的に SNR が 0 になるようにする)、2. epsilon prediction から v-prediction に変更する (SNR = 0 の近辺でも学習が進むように)、3. サンプラーが必ずスケジュールをフルに使うように実装を修正する、4. CFG scale を再スケールするようにする (スケジュール開始時近くで値が飛びやすいので)、の 4点を提案しています。

1 と 2 は学習時の問題です。 LoRA などである程度対応できるようになるかもしれません。 3. は ComfyUI では、scheduler として ddim_uniform を使わなければ問題ありません。 4. については、実験的な実装がされています (sampler_rescalecfg.py)。 cfg に合わせて強度を調整すれば、画像が真っ白になったりするのを防ぐ効果があります。