問題の概要:API一括生成時の接続エラーと予期せぬ出力
Stable Diffusion WebUI(以下、SD WebUI)は、直感的なWebインターフェースで画像生成が可能な優れたツールです。さらに、その内部APIを利用することで、Pythonスクリプトからプロンプトやパラメータをプログラムで制御し、大量の画像を自動生成するバッチ処理が可能になります。
しかし、APIを利用した一括生成スクリプトの作成・実行において、特に初心者から中級者の開発者が頻繁に遭遇する問題が2つあります。
1. APIサーバーへの接続エラー
スクリプト実行時に、SD WebUIのAPIサーバーに接続できないというエラーです。具体的には、以下のようなエラーメッセージが表示されます。
requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=7860):
Max retries exceeded with url: /sdapi/v1/txt2img (Caused by NewConnectionError(':
Failed to establish a new connection: [WinError 10061] 対象のコンピューターによって拒否されたため、接続できませんでした。'))
2. パラメータ指定不整合によるエラーまたは期待しない出力
接続は成功したものの、APIに渡すリクエストボディのJSON形式やパラメータ名が正しくない場合に発生します。エラーが返されることもあれば、エラーなく処理が完了しても、期待した画質や構図の画像が生成されない「サイレント失敗」に陥ることがあります。
# エラーレスポンスの例
{
"error": "Validation error: `steps` must be a positive integer",
"detail": "..."
}
# または、エラーは出ないが、常に同じ画像しか生成されない、シードが効かないなどの不具合。
原因の解説
これらの問題が発生する主な原因は以下の通りです。
接続エラーの原因
1. SD WebUIの起動モード: デフォルトではWebUIはローカルネットワークからのみアクセス可能なモードで起動します。スクリプトから`localhost`や`127.0.0.1`でアクセスするためには、WebUIが確実に起動している必要があります。また、`–listen`オプションなしで起動すると、外部IPからのアクセスは拒否されます(スクリプトが同じPC上であれば問題ありません)。
2. ポート番号の不一致: WebUIの起動ポート(デフォルトは`7860`)と、スクリプト内で指定するポート番号が一致していない場合に接続できません。別のアプリケーションが同じポートを使用している可能性もあります。
パラメータ不整合の原因
1. API仕様のバージョン差異: SD WebUIはアップデートが頻繁で、APIのエンドポイントや利用可能なパラメータが変更されることがあります。古いチュートリアルに記載されたパラメータ名(例:`sampler_name`)が現在のバージョンでは非推奨になっているケースがあります。
2. JSON構造の誤り: `requests`ライブラリでデータを送信する際、Pythonの辞書型を正しくJSON形式に変換できていない、または必須パラメータが欠けている場合にエラーとなります。
3. シード(seed)管理の問題: バッチ生成で毎回異なる画像を生成したい場合、シード値を固定せず(`-1`にする)、またはループ内で動的に変更する必要があります。シードを固定したまま他のパラメータだけ変更しても、同じ画像が生成され続けることがあります。
解決方法:ステップバイステップガイド
ステップ1: SD WebUIの正しい起動
まず、APIを受け付ける状態でSD WebUIを起動します。コマンドラインまたは`webui-user.bat`(Windows)の設定を変更します。
# webui-user.bat 内の COMMANDLINE_ARGS を編集する例(Windows)
set COMMANDLINE_ARGS=--api --listen
# ターミナルから直接起動する例(Linux/macOS)
./webui.sh --api --listen
`–api`: API機能を有効化します。これが必須です。
`–listen`: ローカルホスト以外からの接続を許可します(同じマシン上のスクリプトの場合は必須ではありませんが、明示的に指定することをお勧めします)。
起動後、ブラウザで `http://127.0.0.1:7860` にアクセスできることを確認してください。
ステップ2: 最小限の動作確認スクリプトの作成
以下のシンプルなスクリプトで、API接続と基本的な画像生成が可能かテストします。
import requests
import json
import io
from PIL import Image
# 1. APIエンドポイントの設定
url = "http://127.0.0.1:7860"
# 2. テキストから画像生成するためのパラメータ
payload = {
"prompt": "a beautiful sunset over mountains, masterpiece, high quality",
"negative_prompt": "blurry, ugly, deformed",
"steps": 20,
"width": 512,
"height": 512,
"cfg_scale": 7,
"sampler_name": "Euler a", # サンプラー名はWebUIの画面で確認可能
"seed": -1, # -1 はランダムシード
}
# 3. APIリクエストの送信
try:
response = requests.post(url=f'{url}/sdapi/v1/txt2img', json=payload)
response.raise_for_status() # HTTPエラーがあれば例外を発生させる
except requests.exceptions.ConnectionError as e:
print(f"接続エラー: {e}")
print("SD WebUIが '--api' オプションを付けて起動しているか確認してください。")
exit(1)
except requests.exceptions.RequestException as e:
print(f"リクエストエラー: {e}")
exit(1)
# 4. レスポンスの処理
r = response.json()
print("生成成功!")
# 5. 画像の保存 (レスポンスはBase64エンコードされた画像のリストを含む)
for i, img_base64 in enumerate(r['images']):
image = Image.open(io.BytesIO(base64.b64decode(img_base64.split(",",1)[0])))
image.save(f'test_output_{i}.png')
print(f"画像を 'test_output_{i}.png' として保存しました。")
ステップ3: 一括生成スクリプトへの発展とエラー対策
テストスクリプトが動作したら、複数のプロンプトを処理するバッチスクリプトを作成します。この際、以下の点に注意して実装します。
import requests
import base64
import io
import time
from PIL import Image
import json
def generate_image(prompt, negative_prompt, seed=-1, index=0):
"""単一画像生成関数"""
url = "http://127.0.0.1:7860"
payload = {
"prompt": prompt,
"negative_prompt": negative_prompt,
"steps": 28,
"width": 768,
"height": 512,
"cfg_scale": 7.5,
"sampler_name": "DPM++ 2M Karras",
"seed": seed,
"batch_size": 1
}
try:
response = requests.post(url=f'{url}/sdapi/v1/txt2img', json=payload, timeout=60)
# HTTPステータスコードのチェック
if response.status_code != 200:
print(f"エラー: HTTP {response.status_code}")
print(response.text)
return None
r = response.json()
# 実際のSD WebUIのAPIレスポンス構造を確認
if 'images' not in r or len(r['images']) == 0:
print("エラー: レスポンスに画像データが含まれていません。")
print(json.dumps(r, indent=2)[:500]) # レスポンスの一部を表示
return None
img_data = base64.b64decode(r['images'][0].split(",",1)[0])
return img_data
except requests.exceptions.Timeout:
print(f"タイムアウト: プロンプト '{prompt[:20]}...' の処理に時間がかかりすぎています。")
return None
except Exception as e:
print(f"予期せぬエラー: {type(e).__name__} - {e}")
return None
# メイン処理: プロンプトリストを一括処理
prompt_list = [
("1girl, in a cyberpunk city, neon lights", "low quality, monochrome"),
("ancient castle in a fantasy forest, digital art", "people, modern"),
("a cute cat wearing a wizard hat, detailed illustration", "dog, realistic"),
]
for idx, (prompt, neg_prompt) in enumerate(prompt_list):
print(f"処理中 [{idx+1}/{len(prompt_list)}]: {prompt}")
# シードをランダム(-1)またはインデックスに基づいて変更可能
img_data = generate_image(prompt, neg_prompt, seed=-1, index=idx)
if img_data:
image = Image.open(io.BytesIO(img_data))
filename = f"batch_output_{idx:03d}.png"
image.save(filename)
print(f" -> 保存完了: {filename}")
else:
print(f" -> 生成失敗")
# サーバー負荷軽減のための短い間隔(オプション)
time.sleep(1)
print("一括生成処理が完了しました。")
ステップ4: よくあるパラメータ関連のトラブルシューティング
サンプラー名が無効というエラーが出る場合: 使用可能なサンプラー名はAPIエンドポイント `/sdapi/v1/samplers` にGETリクエストを送ることで取得できます。
import requests
url = "http://127.0.0.1:7860"
samplers = requests.get(url=f'{url}/sdapi/v1/samplers').json()
print("利用可能なサンプラー:")
for sampler in samplers:
print(f" - {sampler['name']}")
常に同じ画像が生成される場合: `”seed”: -1` が設定されていることを確認してください。特定のシードから開始して変化させたい場合は、ループ内で `seed = initial_seed + i` のようにインクリメントします。
メモリ不足エラーが発生する場合: `”width”`、`”height”`、`”batch_size”` の値を小さくしてみてください。高解像度や大きなバッチサイズはVRAMを大量に消費します。
まとめ・補足情報
SD WebUIのAPIを利用した一括画像生成は、創作やデータセット作成の効率を飛躍的に高める強力な手法です。成功の鍵は以下の3点に集約されます。
1. 確実な接続環境の構築: `–api` オプションを付けたWebUIの起動を習慣化し、簡単なテストスクリプトで通信を確認することから始めましょう。
2. 正しいパラメータの把握: APIの仕様は更新されるため、利用しているWebUIのバージョンに対応したパラメータを、実際にAPIから取得する情報も参考にしながら使用しましょう。
3. 堅牢なエラーハンドリング: ネットワークタイムアウト、サーバーエラー、不正なレスポンスなど、様々な異常系を想定したコードを書くことで、長時間実行するバッチ処理を安定させられます。
さらに発展的な使い方として、`/sdapi/v1/options` エンドポイントにPOSTリクエストを送ることで、モデルの切り替え(`”sd_model_checkpoint”`)などWebUIの設定そのものをスクリプトから変更することも可能です。APIをマスターすることで、Stable Diffusionの可能性をさらに広げられるでしょう。