【ComfyUI】バッチ処理で画像生成を自動化する方法と「OutOfMemoryError」の解決策

問題の概要:バッチ処理の自動化とメモリエラー

ComfyUIは、ノードベースの直感的なインターフェースでStable Diffusionのワークフローを構築できる強力なツールです。しかし、単一の画像生成を超え、複数のプロンプトやシード値で大量の画像を自動生成しようとすると、いくつかの課題に直面します。最も一般的なのは、バッチ処理の設定方法がわかりにくいこと、そして処理中に発生する「RuntimeError: CUDA out of memory.」などのメモリ不足エラーです。本記事では、コマンドラインやスクリプトを用いてComfyUIのワークフローをバッチ処理化する方法と、その過程で発生する典型的なエラーの解決策を詳しく解説します。

原因の解説:なぜバッチ処理でエラーが発生するのか

ComfyUIの標準的なWeb UIでは、一度に実行できるのは一つの「キュー」です。複数の条件で画像を生成するには、手動でパラメータを変更して何度もキューを実行する必要があり、非効率です。バッチ自動化を試みる際の主な障害とその原因は以下の通りです。

1. メモリ不足エラーの根本原因

「RuntimeError: CUDA out of memory. Trying to allocate 2.00 GiB…」といったエラーは、バッチ処理の設定を誤ると頻繁に発生します。原因は主に二つです。第一に、一つのバッチ内で同時に処理しようとする画像数(バッチサイズ)が、GPUのVRAM容量を超えている場合です。第二に、ワークフロー実行の間にGPUメモリが適切に解放されず、メモリリークが累積している場合です。ComfyUIのノードによっては、キャッシュが残り続けることがあります。

2. 自動化スクリプトの接続エラー

外部スクリプトからComfyUIのAPI(デフォルトでポート8188)を呼び出そうとした際に、「ConnectionRefusedError: [Errno 111] Connection refused」が発生することがあります。これは、ComfyUIサーバーが起動していない、または異なるポートで動作していることが原因です。

3. ワークフローの不整合エラー

保存したワークフロー(.json)をバッチ処理で使用する際、ノードのID(特に「Seed」などの入力値)が変わっていたり、使用したカスタムノードが読み込まれていなかったりすると、「KeyError: ‘6’(ノードID)」のようなエラーが発生し、処理が中断します。

解決方法:ステップバイステップでのバッチ自動化

ここでは、Pythonスクリプトを使用してComfyUIのAPIを呼び出し、安全にバッチ処理を行う方法を説明します。

ステップ1:ワークフローの準備とテンプレート化

まず、ComfyUIのWebインターフェースで望みの画像生成ワークフローを作成・テストします。問題なく動作したら、ワークフローをJSON形式でエクスポートします。このJSONファイルがバッチ処理のテンプレートとなります。バッチ処理で変更したい部分(プロンプト、ネガティブプロンプト、シード値、ステップ数など)のノード入力値を、後からスクリプトで置き換えやすいように確認しておきます。

ステップ2:バッチ処理用Pythonスクリプトの作成

ComfyUIは標準でWeb APIを提供しています。以下のようなスクリプトを作成し、テンプレートJSONを読み込み、パラメータを置き換えながら連続してAPIに送信します。

import json
import requests
import random
import time

# ComfyUIサーバーのアドレス
server_address = "127.0.0.1:8188"

# ワークフローテンプレートを読み込む
with open("path/to/your/workflow_template.json", "r", encoding="utf-8") as f:
    workflow_template = json.load(f)

def queue_prompt(prompt):
    """ComfyUI APIにプロンプトを送信する"""
    p = {"prompt": prompt}
    headers = {'Content-Type': 'application/json'}
    response = requests.post(f"http://{server_address}/prompt", json=p, headers=headers)
    return response.json()

# バッチ処理のパラメータリスト
batch_params = [
    {"positive": "masterpiece, best quality, a cute cat", "seed": 42},
    {"positive": "masterpiece, best quality, a majestic lion", "seed": 123},
    {"positive": "cyberpunk style, a robot dog", "seed": 999},
]

for i, params in enumerate(batch_params):
    print(f"Processing batch {i+1}/{len(batch_params)}...")
    
    # ワークフローのコピーを作成(元のテンプレートを変更しないため)
    current_prompt = json.loads(json.dumps(workflow_template))
    
    # ノードIDはワークフローによって異なります。実際のJSONを確認して置き換えてください。
    # 例: クリップテキストエンコードノード(positive)のIDが「6」の場合
    current_prompt["6"]["inputs"]["text"] = params["positive"]
    # 例: KSamplerノードのシード値のIDが「3」の場合
    current_prompt["3"]["inputs"]["seed"] = params["seed"]
    
    try:
        # APIにリクエストを送信
        result = queue_prompt(current_prompt)
        print(f"  Job queued with ID: {result.get('prompt_id')}")
        
        # メモリ負荷を分散させるため、またはAPIのレート制限を避けるために少し待機
        time.sleep(2)
        
    except requests.exceptions.ConnectionError:
        print("  Error: ComfyUIサーバーに接続できません。サーバーが起動しているか確認してください。")
        break
    except Exception as e:
        print(f"  Error: {e}")
        break

print("Batch processing finished.")

ステップ3:メモリエラーへの対処と最適化

上記のスクリプトでもメモリエラーが発生する場合は、以下の対策を施します。

対策A: バッチサイズを「1」に固定する
KSamplerノードの「batch_size」を1以上に設定していると、1回の実行で複数画像をメモリにロードしようとします。バッチ処理スクリプト自体が外側のループを担当するので、KSamplerのバッチサイズは1に設定し、メモリ使用量を最小化します。

対策B: 実行間隔を空ける & `torch.cuda.empty_cache()` の利用
スクリプトの`time.sleep(2)`をより長く(例: 5秒)するか、またはカスタムノードをインストールして、ワークフローの最後にメモリ解放ノードを追加する方法もあります。より直接的な方法は、ComfyUIの実行コマンドに`–lowvram`オプションを付与して起動することです。

# ComfyUI起動コマンド例(低VRAMモード)
python main.py --lowvram

対策C: エラーハンドリングとリトライ
スクリプトにエラーハンドリングを追加し、メモリエラーが発生した場合に少し長い待機時間を置いてからリトライするロジックを組み込むと堅牢性が増します。

import torch

def queue_prompt_with_retry(prompt, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = queue_prompt(prompt)
            return response
        except requests.exceptions.ConnectionError:
            print("  Connection error. Retrying...")
            time.sleep(5)
        # その他の例外もキャッチ可能
    print("  Failed after max retries.")
    return None

# ループ内で使用
result = queue_prompt_with_retry(current_prompt)
if result:
    # 成功時の処理
    pass

コード例・コマンド例:実践的な自動化の拡張

より実用的な例として、CSVファイルからプロンプトを読み込むバッチ処理スクリプトを示します。

import csv
import json
import requests
import sys

def load_prompts_from_csv(csv_path):
    prompts_list = []
    with open(csv_path, newline='', encoding='utf-8') as csvfile:
        reader = csv.DictReader(csvfile)
        for row in reader:
            # CSVのカラム名は 'positive', 'negative', 'seed' を想定
            prompts_list.append({
                'positive': row['positive'],
                'negative': row.get('negative', ''),
                'seed': int(row.get('seed', random.randint(1, 2**32)))
            })
    return prompts_list

# メイン実行部分
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python batch_comfy.py ")
        sys.exit(1)
    
    csv_file = sys.argv[1]
    batch_params = load_prompts_from_csv(csv_file)
    
    # 前述のqueue_prompt関数とループ処理をここに組み込む
    # ... (省略) ...

実行コマンド例:

python batch_comfy.py ./prompts.csv

まとめ・補足情報

ComfyUIのバッチ処理自動化は、APIを利用した外部スクリプト制御が鍵となります。成功させるには、(1)正しくパラメータが置換できるようワークフローJSONの構造を理解すること、(2)GPUメモリ不足を避けるためにKSamplerのバッチサイズを1にし、実行間隔を調整すること、が重要です。

補足情報:

  • ノードIDの確認方法: ComfyUIのWeb UIで、ワークフローを読み込んだ状態で「Ctrl+Shift+I」を押すとデバッグパネルが開き、各ノードに割り当てられた一意のIDを確認できます。これがJSON内のキーとなります。
  • より高度な自動化: `comfy-cli` などのコミュニティツールや、`aiohttp`を使った非同期処理の導入を検討すると、さらに効率化できます。
  • エラーログの確認: ComfyUIの起動ターミナルや `comfyui.log` ファイルには詳細なエラー情報が出力されます。問題解決時にはまずここを確認しましょう。

以上の手順と対策を踏まえることで、ComfyUIを用いた安定した大量画像生成の自動化が可能になります。最初は小さなバッチでテストし、徐々に規模を拡大していくことをお勧めします。

この記事は役に立ちましたか?