ComfyUI ワークフロー設計 101:入力 → 前処理 → 生成 → 後処理 → 出力の責務分離
ComfyUI で画像生成ワークフローを構築する際、ノードを適当に繋げてしまうと、後から修正や拡張が困難になってしまいます。実は、ワークフロー設計にも「責務分離」という考え方を取り入れることで、保守性が高く、再利用しやすい構成を実現できるのです。
本記事では、ComfyUI のワークフロー設計における基本的な考え方として「入力 → 前処理 → 生成 → 後処理 → 出力」という 5 つのフェーズに分けた責務分離の手法を解説します。この設計パターンを身につけることで、複雑なワークフローでも整理された状態を保つことができるでしょう。
背景
ComfyUI のワークフロー設計の重要性
ComfyUI は、Stable Diffusion をはじめとする画像生成 AI をノードベースで操作できる強力なツールです。ノードを自由に配置・接続できる柔軟性がある一方で、設計の指針がないと、ワークフローが複雑化し、メンテナンスが困難になります。
特に以下のような問題が発生しがちです。
- ノードの接続が複雑に絡み合い、データの流れが追いにくい
- 同じような処理が複数箇所に散らばり、修正時の漏れが発生する
- パラメータ変更の影響範囲が把握できず、予期しない結果になる
- 他のワークフローへの再利用が困難
ソフトウェア設計の原則を応用する
ソフトウェア開発の世界では、「関心の分離(Separation of Concerns)」という設計原則があります。これは、システムを独立した責務を持つ部分に分割し、各部分が明確な役割を担うようにする考え方です。
この原則を ComfyUI のワークフロー設計に応用することで、整理された構造を実現できます。具体的には、ワークフローを「入力」「前処理」「生成」「後処理」「出力」という 5 つのフェーズに分離することで、各段階の責務を明確にするのです。
以下の図は、5 つのフェーズの基本的な流れを示しています。
mermaidflowchart LR
input["入力フェーズ<br/>データ取得"]
preprocess["前処理フェーズ<br/>データ準備"]
generation["生成フェーズ<br/>AI 処理"]
postprocess["後処理フェーズ<br/>品質向上"]
output["出力フェーズ<br/>保存・配信"]
input --> preprocess
preprocess --> generation
generation --> postprocess
postprocess --> output
各フェーズは明確な役割を持ち、前のフェーズから受け取ったデータを処理して次のフェーズに渡します。この一方向のデータフローにより、ワークフロー全体の見通しが良くなります。
図で理解できる要点
- データは左から右へ一方向に流れる
- 各フェーズは独立した責務を持つ
- フェーズ間のインターフェースが明確
課題
ワークフロー設計における典型的な問題
ComfyUI でワークフローを構築する際、以下のような課題に直面することがあります。
問題 1:スパゲッティワークフロー
ノードの接続が複雑に絡み合い、どこから読み始めればよいのか分からない状態です。データの流れを追うのに時間がかかり、バグの原因特定も困難になります。
問題 2:重複した処理
同じような画像リサイズ処理や色調整処理が、ワークフロー内の複数箇所に散在します。パラメータを変更する際、すべての箇所を更新する必要があり、修正漏れのリスクが高まります。
問題 3:変更の影響範囲が不明確
あるノードのパラメータを変更したとき、最終的な出力にどのような影響があるのか予測できません。試行錯誤を繰り返すことになり、作業効率が低下します。
問題 4:再利用性の低さ
別のプロジェクトで似たようなワークフローが必要になっても、既存のワークフローからパーツを取り出して再利用することが困難です。結局、ゼロから作り直すことになります。
以下の図は、問題のあるワークフローと、責務分離されたワークフローの違いを示しています。
mermaidflowchart TB
subgraph bad["❌ 問題のあるワークフロー"]
direction LR
n1["ノード A"] --> n2["ノード B"]
n1 --> n3["ノード C"]
n2 --> n3
n3 --> n2
n2 --> n4["ノード D"]
n3 --> n4
n4 --> n1
end
subgraph good["✓ 責務分離されたワークフロー"]
direction LR
p1["入力"] --> p2["前処理"]
p2 --> p3["生成"]
p3 --> p4["後処理"]
p4 --> p5["出力"]
end
問題のあるワークフローでは、ノード間の依存関係が複雑で循環参照も発生しています。一方、責務分離されたワークフローは、明確な一方向の流れを持っています。
設計指針の不足
これらの問題の根本原因は、ワークフロー設計における明確な指針がないことです。「何となく動くから良い」という状態では、ワークフローが大きくなるにつれて問題が深刻化します。
体系的な設計手法を導入することで、これらの課題を解決できるのです。
解決策
5 つのフェーズによる責務分離
ワークフローを「入力 → 前処理 → 生成 → 後処理 → 出力」という 5 つのフェーズに分離することで、各段階の責務を明確にします。
以下、各フェーズの役割と、含めるべきノード、含めるべきでないノードを詳しく見ていきましょう。
フェーズ 1:入力フェーズ
責務:外部からデータを取得し、ワークフローに供給する
入力フェーズは、ワークフローの起点となります。画像、テキスト、パラメータなど、処理に必要なすべての素材をここで読み込みます。
| # | 含めるべきノード | 説明 |
|---|---|---|
| 1 | Load Image | 画像ファイルの読み込み |
| 2 | Load Checkpoint | モデルファイルの読み込み |
| 3 | Text Input | プロンプトの入力 |
| 4 | Primitive ノード | パラメータ値の設定 |
| 5 | Load LoRA | 追加モデルの読み込み |
このフェーズで避けるべきこと
- 画像の加工や変換(前処理フェーズの責務)
- プロンプトの加工や結合(前処理フェーズの責務)
- AI モデルの実行(生成フェーズの責務)
入力フェーズは、あくまで「データを取得する」ことに専念します。データの加工は次のフェーズに委ねることで、入力元を変更したい場合の影響範囲を最小化できるのです。
フェーズ 2:前処理フェーズ
責務:入力データを生成フェーズに適した形式に整形する
前処理フェーズでは、入力データを AI モデルが処理しやすい形式に変換します。画像のリサイズ、プロンプトの結合、条件分岐などを行います。
| # | 含めるべきノード | 説明 |
|---|---|---|
| 1 | Image Resize | 画像サイズの調整 |
| 2 | Image Crop | 画像の切り抜き |
| 3 | Text Concatenate | プロンプトの結合 |
| 4 | CLIP Text Encode | テキストのエンコード |
| 5 | ControlNet Preprocessor | ControlNet 用の前処理 |
| 6 | VAE Encode | 画像の潜在空間への変換 |
このフェーズで避けるべきこと
- AI による画像生成(生成フェーズの責務)
- 生成後の品質向上処理(後処理フェーズの責務)
- ファイルへの保存(出力フェーズの責務)
前処理フェーズは、「生成の準備」に集中します。ここでデータの形式を統一することで、生成フェーズに渡すデータの品質が安定し、予測可能な結果を得やすくなります。
フェーズ 3:生成フェーズ
責務:AI モデルを使用して画像を生成する
生成フェーズは、ワークフローの中核となる部分です。ここでは、AI モデルによる画像生成のみに専念します。
| # | 含めるべきノード | 説明 |
|---|---|---|
| 1 | KSampler | メインのサンプリング処理 |
| 2 | KSampler Advanced | 高度なサンプリング設定 |
| 3 | ControlNet Apply | ControlNet の適用 |
| 4 | IPAdapter Apply | IP-Adapter の適用 |
| 5 | VAE Decode | 潜在空間から画像への変換 |
このフェーズで避けるべきこと
- 入力画像の読み込み(入力フェーズの責務)
- 画像のリサイズや切り抜き(前処理フェーズの責務)
- アップスケールや色調整(後処理フェーズの責務)
- ファイルへの保存(出力フェーズの責務)
生成フェーズを純粋に保つことで、生成パラメータ(ステップ数、CFG スケールなど)の変更が、他のフェーズに影響を与えないようにできます。
フェーズ 4:後処理フェーズ
責務:生成された画像の品質を向上させる
後処理フェーズでは、AI が生成した画像をさらに洗練させます。アップスケール、ノイズ除去、色調整などを行います。
| # | 含めるべきノード | 説明 |
|---|---|---|
| 1 | Upscale Image | 画像の拡大 |
| 2 | Image Sharpen | シャープネス調整 |
| 3 | Color Correct | 色補正 |
| 4 | Face Restore | 顔の修復 |
| 5 | Image Blend | 複数画像の合成 |
このフェーズで避けるべきこと
- 新たな画像の生成(生成フェーズの責務)
- ファイルへの保存(出力フェーズの責務)
- プロンプトの変更(前処理フェーズの責務)
後処理フェーズを独立させることで、生成結果に満足できなかった場合、後処理だけを調整して品質を高めることができます。生成フェーズを再実行する必要がないため、時間を節約できるのです。
フェーズ 5:出力フェーズ
責務:最終的な成果物を保存・配信する
出力フェーズでは、完成した画像をファイルに保存したり、外部システムに送信したりします。
| # | 含めるべきノード | 説明 |
|---|---|---|
| 1 | Save Image | 画像ファイルとして保存 |
| 2 | Preview Image | プレビュー表示 |
| 3 | Send to API | 外部 API への送信 |
| 4 | Image to Base64 | Base64 エンコード |
このフェーズで避けるべきこと
- 画像の加工や変換(前処理・後処理フェーズの責務)
- AI モデルの実行(生成フェーズの責務)
出力フェーズを分離することで、保存形式や保存先を変更する際の影響範囲を限定できます。
フェーズ間のインターフェース設計
各フェーズは、前のフェーズから明確な形式でデータを受け取り、次のフェーズに渡します。このインターフェースを意識することで、フェーズ間の結合度を下げられます。
以下の図は、各フェーズ間で受け渡されるデータの種類を示しています。
mermaidflowchart LR
input["入力<br/>画像/モデル/テキスト"]
preprocess["前処理<br/>整形済みデータ"]
generation["生成<br/>生成画像"]
postprocess["後処理<br/>高品質画像"]
output["出力<br/>保存完了"]
input -->|"画像データ<br/>モデルオブジェクト<br/>テキスト"| preprocess
preprocess -->|"リサイズ済み画像<br/>エンコード済みプロンプト<br/>Latent"| generation
generation -->|"生成画像<br/>(Latent/Image)"| postprocess
postprocess -->|"最終画像"| output
output -->|"ファイルパス<br/>送信結果"| done["完了"]
図で理解できる要点
- 各フェーズは決まった形式のデータを受け渡す
- データの変換は各フェーズ内で完結する
- 後続のフェーズは前のフェーズの実装詳細を知る必要がない
責務分離の利点
この 5 フェーズ設計を採用することで、以下のメリットが得られます。
| # | メリット | 説明 |
|---|---|---|
| 1 | 可読性の向上 | ワークフローの構造が一目で理解できる |
| 2 | 保守性の向上 | 変更の影響範囲が限定され、修正が容易 |
| 3 | 再利用性の向上 | フェーズ単位で他のワークフローに移植可能 |
| 4 | テストしやすさ | 各フェーズを独立してテストできる |
| 5 | 並列化の可能性 | フェーズ内の独立した処理を並列実行できる |
具体例
基本的な Text-to-Image ワークフロー
責務分離の考え方を実践した、シンプルな Text-to-Image ワークフローを見ていきましょう。
入力フェーズの実装
まず、必要なモデルとプロンプトを読み込みます。
モデルとプロンプトの読み込み
typescript// CheckpointLoaderSimple ノード
// 役割: Stable Diffusion モデルの読み込み
{
"id": 1,
"type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "sd_xl_base_1.0.safetensors"
},
"outputs": {
"MODEL": "model_output",
"CLIP": "clip_output",
"VAE": "vae_output"
}
}
このノードは、画像生成に使用する AI モデルをメモリに読み込みます。出力される MODEL、CLIP、VAE は、後続のフェーズで使用されます。
ポジティブプロンプトの入力
typescript// CLIPTextEncode ノード(ポジティブ)
// 役割: 生成したい内容を記述
{
"id": 2,
"type": "CLIPTextEncode",
"inputs": {
"text": "a beautiful landscape with mountains and lake, sunset, highly detailed",
"clip": "clip_output" // ノード 1 から受け取る
},
"outputs": {
"CONDITIONING": "positive_conditioning"
}
}
ポジティブプロンプトは、画像に含めたい要素を AI に伝えます。CLIP モデルを使用してテキストをエンコードし、AI が理解できる形式に変換します。
ネガティブプロンプトの入力
typescript// CLIPTextEncode ノード(ネガティブ)
// 役割: 生成したくない内容を記述
{
"id": 3,
"type": "CLIPTextEncode",
"inputs": {
"text": "blurry, low quality, text, watermark",
"clip": "clip_output" // ノード 1 から受け取る
},
"outputs": {
"CONDITIONING": "negative_conditioning"
}
}
ネガティブプロンプトは、画像に含めたくない要素を指定します。これにより、望ましくない特徴を持つ画像が生成される確率を減らせます。
前処理フェーズの実装
前処理フェーズでは、空の潜在空間(Latent)を作成します。Text-to-Image の場合、入力画像がないため、ランダムノイズから開始します。
潜在空間の初期化
typescript// EmptyLatentImage ノード
// 役割: 生成の起点となる空の潜在空間を作成
{
"id": 4,
"type": "EmptyLatentImage",
"inputs": {
"width": 1024, // 生成画像の幅
"height": 1024, // 生成画像の高さ
"batch_size": 1 // 一度に生成する枚数
},
"outputs": {
"LATENT": "empty_latent"
}
}
このノードは、指定されたサイズの空の潜在空間を作成します。AI モデルは、このランダムノイズから徐々に画像を形成していきます。
生成フェーズの実装
生成フェーズでは、KSampler を使用して画像を生成します。
サンプリング処理
typescript// KSampler ノード
// 役割: AI モデルを使用して画像を生成
{
"id": 5,
"type": "KSampler",
"inputs": {
"model": "model_output", // ノード 1 から受け取る
"positive": "positive_conditioning", // ノード 2 から受け取る
"negative": "negative_conditioning", // ノード 3 から受け取る
"latent_image": "empty_latent", // ノード 4 から受け取る
"seed": 42, // 再現性のためのシード値
"steps": 20, // サンプリングステップ数
"cfg": 7.0, // プロンプトへの従属度
"sampler_name": "euler", // サンプリングアルゴリズム
"scheduler": "normal", // スケジューラータイプ
"denoise": 1.0 // ノイズ除去の強度
},
"outputs": {
"LATENT": "generated_latent"
}
}
KSampler は、指定されたステップ数だけ反復処理を行い、ランダムノイズから徐々に画像を形成します。CFG(Classifier-Free Guidance)スケールは、プロンプトへの従属度を制御します。
潜在空間から画像への変換
typescript// VAEDecode ノード
// 役割: 潜在空間の表現を通常の画像に変換
{
"id": 6,
"type": "VAEDecode",
"inputs": {
"samples": "generated_latent", // ノード 5 から受け取る
"vae": "vae_output" // ノード 1 から受け取る
},
"outputs": {
"IMAGE": "generated_image"
}
}
VAEDecode は、潜在空間で表現されている画像データを、通常の RGB 画像に変換します。この段階で、初めて人間が視認できる画像が得られます。
後処理フェーズの実装
後処理フェーズでは、生成された画像の品質を向上させます。
アップスケール処理
typescript// ImageUpscaleWithModel ノード
// 役割: AI を使用して画像を高解像度化
{
"id": 7,
"type": "ImageUpscaleWithModel",
"inputs": {
"upscale_model": "RealESRGAN_x4plus.pth",
"image": "generated_image" // ノード 6 から受け取る
},
"outputs": {
"IMAGE": "upscaled_image"
}
}
このノードは、専用の AI モデルを使用して画像を 4 倍に拡大します。単純な補間とは異なり、ディテールを復元しながら高解像度化できます。
シャープネス調整
typescript// ImageSharpen ノード
// 役割: 画像の輪郭を強調して鮮明さを向上
{
"id": 8,
"type": "ImageSharpen",
"inputs": {
"image": "upscaled_image", // ノード 7 から受け取る
"sharpen_radius": 1, // シャープネスの適用範囲
"sigma": 1.0, // ぼかしの強度
"alpha": 1.5 // シャープネスの強度
},
"outputs": {
"IMAGE": "final_image"
}
}
シャープネス処理により、画像の輪郭が強調され、より鮮明な印象になります。過度に適用すると不自然になるため、alpha 値の調整が重要です。
出力フェーズの実装
最後に、完成した画像を保存します。
画像の保存
typescript// SaveImage ノード
// 役割: 画像をファイルシステムに保存
{
"id": 9,
"type": "SaveImage",
"inputs": {
"images": "final_image", // ノード 8 から受け取る
"filename_prefix": "landscape_", // ファイル名の接頭辞
"format": "png", // 保存形式
"quality": 95 // 画質(JPEG の場合)
}
}
このノードは、処理済みの画像を指定された形式で保存します。filename_prefix にタイムスタンプが自動的に付加され、ファイル名の衝突を防ぎます。
ワークフロー全体の構造
以下の図は、上記のノードがどのように接続され、データが流れるかを示しています。
mermaidflowchart TB
subgraph input_phase["入力フェーズ"]
n1["CheckpointLoader<br/>モデル読み込み"]
n2["CLIPTextEncode<br/>ポジティブ"]
n3["CLIPTextEncode<br/>ネガティブ"]
end
subgraph preprocess_phase["前処理フェーズ"]
n4["EmptyLatentImage<br/>潜在空間初期化"]
end
subgraph generation_phase["生成フェーズ"]
n5["KSampler<br/>画像生成"]
n6["VAEDecode<br/>画像変換"]
end
subgraph postprocess_phase["後処理フェーズ"]
n7["ImageUpscale<br/>高解像度化"]
n8["ImageSharpen<br/>鮮明化"]
end
subgraph output_phase["出力フェーズ"]
n9["SaveImage<br/>保存"]
end
n1 -->|MODEL| n5
n1 -->|CLIP| n2
n1 -->|CLIP| n3
n1 -->|VAE| n6
n2 -->|CONDITIONING| n5
n3 -->|CONDITIONING| n5
n4 -->|LATENT| n5
n5 -->|LATENT| n6
n6 -->|IMAGE| n7
n7 -->|IMAGE| n8
n8 -->|IMAGE| n9
この図からわかるように、各フェーズは明確に分離されており、データは一方向に流れています。
図で理解できる要点
- 各ノードは対応するフェーズ内に配置される
- フェーズをまたぐ接続は必要最小限
- データフローが追跡しやすい
Image-to-Image ワークフローの例
次に、既存の画像を加工する Image-to-Image ワークフローを見てみましょう。
入力フェーズ:画像の読み込み
入力画像の読み込み
typescript// LoadImage ノード
// 役割: 加工元となる画像を読み込む
{
"id": 11,
"type": "LoadImage",
"inputs": {
"image": "input_photo.jpg"
},
"outputs": {
"IMAGE": "input_image",
"MASK": "input_mask"
}
}
このノードは、ファイルシステムから画像を読み込みます。IMAGE 出力は RGB 画像データ、MASK 出力は透明度情報(アルファチャンネル)を含みます。
前処理フェーズ:画像の準備
画像のリサイズ
typescript// ImageScale ノード
// 役割: 画像をモデルに適したサイズに調整
{
"id": 12,
"type": "ImageScale",
"inputs": {
"image": "input_image", // ノード 11 から受け取る
"width": 1024, // 目標の幅
"height": 1024, // 目標の高さ
"upscale_method": "lanczos", // リサイズアルゴリズム
"crop": "center" // クロップ方法
},
"outputs": {
"IMAGE": "resized_image"
}
}
モデルは特定のサイズの画像で学習されているため、入力画像をそのサイズに合わせることで、より良い結果が得られます。
画像のエンコード
typescript// VAEEncode ノード
// 役割: 画像を潜在空間に変換
{
"id": 13,
"type": "VAEEncode",
"inputs": {
"pixels": "resized_image", // ノード 12 から受け取る
"vae": "vae_output" // CheckpointLoader から受け取る
},
"outputs": {
"LATENT": "encoded_latent"
}
}
VAEEncode は、通常の画像を潜在空間の表現に変換します。KSampler は潜在空間で動作するため、この変換が必要です。
生成フェーズ:画像の変換
サンプリング処理(Image-to-Image)
typescript// KSampler ノード
// 役割: 既存の画像を基に新しい画像を生成
{
"id": 14,
"type": "KSampler",
"inputs": {
"model": "model_output",
"positive": "positive_conditioning",
"negative": "negative_conditioning",
"latent_image": "encoded_latent", // ノード 13 から受け取る
"seed": 123,
"steps": 20,
"cfg": 7.0,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 0.7 // 重要: 1.0 より小さい値で元画像を保持
},
"outputs": {
"LATENT": "transformed_latent"
}
}
Image-to-Image では、denoise パラメータが重要です。1.0 の場合は完全に新しい画像を生成し、0.0 に近いほど元画像を保持します。0.7 程度が、元画像の特徴を残しつつ変換する良いバランスです。
ControlNet を使用した高度なワークフロー
ControlNet を使用すると、生成画像の構図をより細かく制御できます。
前処理フェーズ:ControlNet の準備
ControlNet プリプロセッサ
typescript// ControlNetPreprocessor ノード
// 役割: 入力画像から構図情報を抽出
{
"id": 21,
"type": "CannyEdgePreprocessor",
"inputs": {
"image": "input_image",
"low_threshold": 100, // エッジ検出の下限閾値
"high_threshold": 200 // エッジ検出の上限閾値
},
"outputs": {
"IMAGE": "canny_edges"
}
}
このノードは、Canny エッジ検出アルゴリズムを使用して、入力画像から輪郭線を抽出します。この輪郭情報が、生成画像の構図を制御するために使われます。
ControlNet の読み込み
typescript// ControlNetLoader ノード
// 役割: ControlNet モデルを読み込む
{
"id": 22,
"type": "ControlNetLoader",
"inputs": {
"control_net_name": "control_v11p_sd15_canny.pth"
},
"outputs": {
"CONTROL_NET": "controlnet_model"
}
}
ControlNet モデルは、特定の種類の制御情報(エッジ、深度、ポーズなど)を扱うように学習されています。
生成フェーズ:ControlNet の適用
ControlNet の適用
typescript// ControlNetApply ノード
// 役割: 生成プロセスに構図制御を追加
{
"id": 23,
"type": "ControlNetApply",
"inputs": {
"conditioning": "positive_conditioning",
"control_net": "controlnet_model", // ノード 22 から受け取る
"image": "canny_edges", // ノード 21 から受け取る
"strength": 0.8 // 制御の強度
},
"outputs": {
"CONDITIONING": "controlled_conditioning"
}
}
このノードは、ポジティブコンディショニングに ControlNet の制御情報を追加します。strength パラメータで、元のプロンプトと ControlNet の影響のバランスを調整できます。
制御されたサンプリング
typescript// KSampler ノード
// 役割: ControlNet で制御された画像生成
{
"id": 24,
"type": "KSampler",
"inputs": {
"model": "model_output",
"positive": "controlled_conditioning", // ノード 23 から受け取る
"negative": "negative_conditioning",
"latent_image": "empty_latent",
"seed": 456,
"steps": 20,
"cfg": 7.0,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1.0
},
"outputs": {
"LATENT": "controlled_latent"
}
}
ControlNet で拡張されたコンディショニングを使用することで、指定した構図に従った画像が生成されます。
バッチ処理ワークフローの例
複数の画像を一度に処理する場合も、責務分離の原則は有効です。
入力フェーズ:複数画像の読み込み
バッチ画像の読み込み
typescript// LoadImageBatch ノード
// 役割: フォルダ内の複数画像を読み込む
{
"id": 31,
"type": "LoadImageBatch",
"inputs": {
"directory": "input_photos", // 読み込むフォルダ
"image_load_cap": 10, // 最大読み込み枚数
"start_index": 0, // 開始インデックス
"load_always": false // 常に再読み込み
},
"outputs": {
"IMAGE": "batch_images"
}
}
このノードは、指定されたフォルダ内のすべての画像をバッチとして読み込みます。バッチ処理により、複数の画像に同じ処理を効率的に適用できます。
生成フェーズ:バッチ処理
バッチでのサンプリング
typescript// KSampler ノード
// 役割: 複数画像を一度に処理
{
"id": 32,
"type": "KSampler",
"inputs": {
"model": "model_output",
"positive": "positive_conditioning",
"negative": "negative_conditioning",
"latent_image": "batch_latent",
"seed": 789,
"steps": 20,
"cfg": 7.0,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 0.5
},
"outputs": {
"LATENT": "batch_output_latent"
}
}
バッチ入力の場合、KSampler は各画像に対して同じパラメータで処理を実行します。効率的に複数の画像を変換できます。
出力フェーズ:バッチ保存
バッチでの保存
typescript// SaveImage ノード
// 役割: 複数画像を連番で保存
{
"id": 33,
"type": "SaveImage",
"inputs": {
"images": "batch_output_images",
"filename_prefix": "batch_output_",
"format": "png",
"quality": 95
}
}
SaveImage ノードは、バッチ内の各画像に自動的に連番を付けて保存します。
実践的な設計パターン
パターン 1:プロンプトテンプレートの活用
複数のワークフローで共通のプロンプト構成を使用する場合、プロンプトテンプレートを入力フェーズに集約します。
typescript// Text Multiline ノード(ベースプロンプト)
// 役割: 共通のプロンプト要素を定義
{
"id": 41,
"type": "Text Multiline",
"inputs": {
"text": "masterpiece, best quality, highly detailed, 8k"
},
"outputs": {
"STRING": "base_prompt"
}
}
typescript// Text Concatenate ノード
// 役割: ベースプロンプトと個別プロンプトを結合
{
"id": 42,
"type": "Text Concatenate",
"inputs": {
"text1": "base_prompt", // ノード 41 から受け取る
"text2": "a beautiful forest", // 個別の内容
"separator": ", " // 区切り文字
},
"outputs": {
"STRING": "combined_prompt"
}
}
このパターンにより、品質向上のための共通フレーズを一箇所で管理でき、すべてのワークフローに反映できます。
パターン 2:条件分岐による動的処理
入力に応じて処理を変更する場合、前処理フェーズで条件分岐を行います。
typescript// Image Size Detector ノード
// 役割: 画像サイズを判定
{
"id": 51,
"type": "Image Size Detector",
"inputs": {
"image": "input_image"
},
"outputs": {
"WIDTH": "image_width",
"HEIGHT": "image_height"
}
}
typescript// Conditional Image Resize ノード
// 役割: サイズに応じてリサイズ方法を変更
{
"id": 52,
"type": "Conditional Image Resize",
"inputs": {
"image": "input_image",
"current_width": "image_width",
"current_height": "image_height",
"target_size": 1024,
"method": "adaptive" // サイズに応じて最適な方法を選択
},
"outputs": {
"IMAGE": "optimized_image"
}
}
条件分岐を前処理フェーズに集約することで、生成フェーズは入力の形式を気にせず、常に一貫した処理を実行できます。
パターン 3:多段階生成
粗い生成から徐々に詳細化する多段階生成も、責務分離で整理できます。
第 1 段階:低解像度での生成
typescript// EmptyLatentImage ノード(低解像度)
// 役割: まず小さなサイズで生成
{
"id": 61,
"type": "EmptyLatentImage",
"inputs": {
"width": 512,
"height": 512,
"batch_size": 1
},
"outputs": {
"LATENT": "low_res_latent"
}
}
typescript// KSampler ノード(第 1 段階)
// 役割: 全体的な構図を決定
{
"id": 62,
"type": "KSampler",
"inputs": {
"latent_image": "low_res_latent",
"steps": 30,
"cfg": 7.0,
"denoise": 1.0
},
"outputs": {
"LATENT": "low_res_result"
}
}
第 2 段階:高解像度化と詳細化
typescript// Latent Upscale ノード
// 役割: 潜在空間で 2 倍に拡大
{
"id": 63,
"type": "Latent Upscale",
"inputs": {
"samples": "low_res_result",
"upscale_method": "bilinear",
"width": 1024,
"height": 1024
},
"outputs": {
"LATENT": "upscaled_latent"
}
}
typescript// KSampler ノード(第 2 段階)
// 役割: 詳細を追加
{
"id": 64,
"type": "KSampler",
"inputs": {
"latent_image": "upscaled_latent",
"steps": 20,
"cfg": 7.0,
"denoise": 0.5 // 構図は保ちつつディテールを追加
},
"outputs": {
"LATENT": "high_res_result"
}
}
多段階生成では、各段階を独立したサブフェーズとして扱うことで、各段階のパラメータを独立して調整できます。
まとめ
ComfyUI のワークフロー設計において、「入力 → 前処理 → 生成 → 後処理 → 出力」という 5 つのフェーズに責務を分離することで、整理された構造を実現できます。
各フェーズの役割を再確認しましょう。
| # | フェーズ | 主な責務 | 含めるノード例 |
|---|---|---|---|
| 1 | 入力 | データの取得 | Load Image, Load Checkpoint, Text Input |
| 2 | 前処理 | データの整形 | Image Resize, CLIP Text Encode, VAE Encode |
| 3 | 生成 | AI による処理 | KSampler, ControlNet Apply, VAE Decode |
| 4 | 後処理 | 品質向上 | Image Upscale, Image Sharpen, Color Correct |
| 5 | 出力 | 保存・配信 | Save Image, Preview Image |
この設計パターンを採用することで、以下のメリットが得られます。
可読性の向上
ワークフローの構造が一目で理解でき、新しいメンバーでもすぐに全体像を把握できます。
保守性の向上
変更の影響範囲が明確になり、安心してパラメータ調整や機能追加ができます。
再利用性の向上
フェーズ単位で切り出して、他のワークフローに移植できます。テンプレート化も容易です。
テストのしやすさ
各フェーズを独立してテストでき、問題の切り分けが簡単になります。
責務分離は、小規模なワークフローでは過剰に感じるかもしれませんが、ワークフローが成長するにつれて、その価値が明確になります。最初から整理された構造を保つことで、長期的なメンテナンスコストを大幅に削減できるのです。
ComfyUI でワークフローを作成する際は、ぜひこの 5 フェーズ設計を意識してみてください。整理された美しいワークフローは、作業効率を高めるだけでなく、作業そのものを楽しくしてくれるはずです。
関連リンク
articleComfyUI ワークフロー設計 101:入力 → 前処理 → 生成 → 後処理 → 出力の責務分離
articleComfyUI チートシート:よく使うノード 50 個の役割と接続例早見表
articleComfyUI のインストール完全ガイド:Windows/Mac/Linux 別・最短手順
articleComfyUI とは?ノードベースで組む最新画像生成ワークフローを完全解説【2025 年版】
articleLangChain 再ランキング手法の実測:Cohere/OpenAI ReRank/Cross-Encoder の効果
articleJotai 非同期で Suspense が発火しない問題の切り分けガイド
articleJest moduleNameMapper 早見表:パスエイリアス/静的アセット/CSS を一網打尽
articleComfyUI ワークフロー設計 101:入力 → 前処理 → 生成 → 後処理 → 出力の責務分離
articleGitHub Copilot でリファクタ促進プロンプト集:命名・抽象化・分割・削除の誘導文
articleCodex で既存コードを読み解く:要約・設計意図抽出・依存関係マップ化
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来