AI活用

ComfyUI APIへ画像を送る方法|img2img・Inpainting入力を自動化する

ComfyUI APIで入力画像を渡すには、POST /upload/imageにmultipartで送り、返ってきたnameをAPI形式JSONのLoad Imageノードのimageに書き込んでから/promptへ投入します。アップロードの仕様、Node書き換え、img2imgとInpaintingのマスク連携、ファイル名の検証を解説します。

この記事の目次
  1. 結論:画像は先に送って保存し、Workflowには「名前」だけを渡す
  2. 画像入力APIはどの順で処理されるか
  3. 画像をどうアップロードするか
  4. Load Image入力をどう書き換えるか
  5. img2imgをAPIからどう動かすか
  6. 再現実験:同じ画像をGUIとAPIから投入して比べる
  7. Inpaintingでマスクをどう渡すか
  8. 入力をどう検証するか
  9. よくある質問
  10. 画像をBase64でJSONに埋め込んで/promptに送れますか?
  11. 同じ画像を何度も送るとファイルが増えますか?
  12. subfolderは使うべきですか?
  13. まとめ

ComfyUI APIで入力画像を渡すには、POST /upload/imageにmultipartで送り、返ってきたnameをAPI形式JSONのLoad Imageノードのimageに書き込んでから/promptへ投入します。アップロードの仕様、ノードの書き換え、img2imgとInpaintingへの接続、ファイル名の検証を順に説明します。

情報確認日:2026年8月22日(日本時間)

結論:画像は先に送って保存し、Workflowには「名前」だけを渡す

責任範囲

  • Upload→サーバー保存→Workflow参照の三段階の関係を示す
  • 現行Server API(2026年8月時点)の/upload/imageのフィールドとレスポンスを、server.pyで確認した範囲で載せる
  • API形式JSONのLoad Imageノードを構造で書き換え、img2imgとInpaintingのWorkflowに接続する
  • 利用者が送ってきたファイル名やパスをそのまま信用しない検証の最小線を決める
  • denoiseの理論はComfyUIでimg2imgを使う方法、マスクと境界の品質はComfyUIでインペイントする方法に任せる
スポンサーリンク

画像入力APIはどの順で処理されるか

Server APIには「画像を含めてWorkflowを送る」エンドポイントはありません。画像は単独でアップロードされてinputディレクトリに保存され、Load Imageノードはそのファイル名をimage入力として受け取ります。APIからの画像入力は次の三つを順に行う作業です。

  1. 1
    Upload

    POST /upload/imageにmultipartで画像を送る。レスポンスで保存後のnameが返る

  2. 2
    サーバー保存

    ComfyUI/input/type=inputのとき)に書かれる。同名があれば連番で改名される

  3. 3
    Workflow参照

    API形式JSONのLoad Imageノードのinputs.imagenameを入れ、POST /promptで投入する

大事なのは、2で返るnameが送ったファイル名と同じとは限らない点です。

画像をどうアップロードするか

server.pyのimage_upload関数(2026年8月22日確認)が受け取るフィールドは四つです。

フィールド 必須 意味
image 必須 画像ファイル本体。ファイル名が空なら400
type 任意 保存先の種別。既定はinput
subfolder 任意 input内のサブフォルダ。ディレクトリ外に出る指定は400
overwrite 任意 "true""1"で同名を上書き。それ以外は連番で改名

レスポンスは{"name": 保存後のファイル名, "subfolder": ..., "type": ...}です。overwriteを付けずに同名のファイルがあると、サーバーは内容ハッシュを比べ、同じ内容なら保存を省き、違う内容ならphoto (1).pngのように改名して保存します。だから「送った名前」ではなく「返ってきたname」を必ず使います。

// Node 22 以上。確認日 2026-08-22(server.py の image_upload に基づく)
import { readFile } from "node:fs/promises";

const SERVER = "http://127.0.0.1:8188";

export async function uploadImage(path, nameOnServer) {
  const form = new FormData();
  form.append("image", new Blob([await readFile(path)], { type: "image/png" }), nameOnServer);
  form.append("type", "input");
  // form.append("overwrite", "true"); // 同名を上書きしたいときだけ
  const res = await fetch(`${SERVER}/upload/image`, { method: "POST", body: form });
  if (!res.ok) throw new Error(`upload failed: ${res.status}`);
  return res.json(); // { name, subfolder, type }
}

FormDataで送れば境界はランタイムが付けるので、Content-Typeは自分で書きません。subfolderはLoad Imageでの参照の仕方が変わるため、最初は使わない構成で動かします。

Load Image入力をどう書き換えるか

API形式JSONでは、Load Imageノードは"class_type": "LoadImage"、入力は"inputs": {"image": "ファイル名"}です。ノードのキー("10"など)はJSONごとに違うので、キーを決め打ちせずclass_typeで探して書き換えます。

// workflow_api.json は File → Export Workflow (API) で書き出したもの(現行UI、2026年8月時点)
export function setLoadImage(prompt, uploadedName, nodeKey = null) {
  const key = nodeKey ?? Object.keys(prompt).find((k) => prompt[k].class_type === "LoadImage");
  if (!key) throw new Error("LoadImage node not found");
  prompt[key].inputs.image = uploadedName; // /upload/image が返した name
  return prompt;
}

const prompt = JSON.parse(await readFile("workflow_api.json", "utf8"));
const { name } = await uploadImage("./photo.png", "photo.png");
setLoadImage(prompt, name);
const res = await fetch(`${SERVER}/prompt`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ prompt }),
});
console.log(await res.json()); // { prompt_id, number, node_errors }

Load Imageが複数あるWorkflowではfindは最初の一つしか返さないので、書き出したJSONでキーを控えてnodeKeyで指定します。投入時のnode_errorsが空でなければ、どのノードのどの入力が不正かが入っています。ファイル名の綴り違いはここで弾かれます。

結果の取得はComfyUI APIの使い方/history/viewの手順と同じです。

img2imgをAPIからどう動かすか

img2imgのWorkflowは、Load Image→VAE Encode→KSampler(latent_imageにエンコード結果、denoiseは1未満)という形です。API側でやることは、前の節と同じ「アップロードしてimageを書き換える」に、denoiseの差し替えが加わるだけです。denoiseが何を変えるかはComfyUIでimg2imgを使う方法で扱っているので、ここでは接続だけに絞ります。

// GUIで動作確認済みの img2img workflow を書き出した前提
const sampler = Object.keys(prompt).find((k) => prompt[k].class_type === "KSampler");
prompt[sampler].inputs.denoise = 0.55;  // 値の決め方は img2img 記事を参照
prompt[sampler].inputs.seed = 12345;    // 比較のため固定

再現実験:同じ画像をGUIとAPIから投入して比べる

配線が正しいかは「GUIとAPIで同じ結果になるか」で確かめられます。結果は環境ごとに違うため、手順と記録表だけを示します。

  1. GUIでimg2img Workflowにphoto.pngを読み込み、seedを固定して1枚生成し、File → Export Workflow (API)で書き出す
  2. 同じphoto.png/upload/imageで送る。overwrite=trueを付けるか、返ってきたnameを使う
  3. 書き出したJSONのimageを返ってきたnameに、seedとdenoiseをGUIと同じ値にして/promptへ投入する
  4. GUIの出力とAPIの出力を並べ、sha256か目視で比べる
投入元 Load Imageのimage値 seed denoise 出力ファイル sha256(先頭8桁) 一致
GUI
API

一致しないときに最初に見るのはLoad Imageのimage値です。連番改名のせいでGUIとAPIが別ファイルを指していることがよくあります。次にseed・denoise・解像度を確かめ、それでも違えばGUI側でseedがrandomizeになっていないかを疑います。

Inpaintingでマスクをどう渡すか

Inpaintingは元画像に加えて「どこをやり直すか」のマスクが要ります。APIから渡す方法は二つあり、アップロードの仕組みは前の節と同じです。

方法 送るもの Workflow側のノード 向く場面
A. マスクを別画像として送る 元画像と、白黒のマスク画像を/upload/imageで2回 Load Image(元画像)+Load Image Mask(channelで読む色を選ぶ) マスクを自分のコードで生成する場合。仕組みが単純
B. alphaに埋め込む alphaチャンネルにマスクを持つPNGを1枚 Load ImageのMASK出力をそのまま使う GUIのマスクエディタで作った画像を流用する場合

server.pyには/upload/maskもあります。original_refで指定した既存画像のalphaに送ったマスクのalphaを書き込んだコピーを作る、GUIのマスクエディタ向けの経路です。自分のコードでマスクを作るなら方法Aのほうが依存が少なく済みます。

マスクは、KSamplerに渡す前にVAEEncodeForInpaintpixelsvaemaskgrow_mask_by)か、SetLatentNoiseMasksamplesmask)を通します。どちらを使うかで仕上がりが変わりますが、その選び方と境界の処理はComfyUIでインペイントする方法の範囲です。API側で守ることは次の二つです。

  • 元画像とマスクの幅・高さを一致させる。ずれていると、マスクが意図しない場所にかかるか、ノードでエラーになる
  • マスクの「白=やり直す」「黒=残す」の向きを、Load Image MaskのchannelとWorkflowの前提にそろえる。反転していると、直したい場所以外が全部描き変わる

入力をどう検証するか

Webアプリから画像を受け取ってComfyUIに流す構成では、利用者のファイル名・パス・内容をそのまま信用しないのが最低線です。server.pyにもディレクトリ外に出るsubfolderを400で弾く検査はありますが、頼り切らずアプリ側で次を行います。

検証 やること 理由
ファイル名を作り直す 利用者の名前を捨て、<uuid>.pngなど自分で生成した名前で/upload/imageに送る パス文字や非ASCII、同名衝突による連番改名を避ける
内容を確かめる 拡張子ではなくマジックバイトで画像形式を判定し、デコードして幅・高さを得る 拡張子だけ偽った非画像ファイルを通さない
サイズと解像度に上限を置く 受け付けるバイト数と画素数を決め、超えたら拒否する 巨大画像はVAE EncodeでVRAMを使い切る
Workflowの書き換え範囲を限る 利用者の入力で変えるのはimageseeddenoiseなど決めた項目だけ。JSON全体を受け取らない 任意のノードを実行させないため
返ってきたnameを使う 送った名前ではなく、レスポンスのnameimageに書く 改名された場合に別ファイルを参照しないため

Server APIには認証がありません。上の検証は自分のアプリが入口になる前提で、8188番ポート自体をLANや外部に見せるなら認証とプロキシが要ります。その設計は別記事で扱います。

よくある質問

画像をBase64でJSONに埋め込んで/promptに送れますか?

標準のLoad Imageはinputディレクトリのファイル名しか受け取らないため、送れません。先に/upload/imageで保存し、名前で参照するのが現行の方法です。

同じ画像を何度も送るとファイルが増えますか?

同名で内容も同じなら、サーバーは内容ハッシュを比べて保存を省きます。名前が同じで内容が違うときだけ連番で増えます。名前をuuidにする設計なら毎回増えるので、古いファイルの掃除を自分で決めます。

subfolderは使うべきですか?

利用者ごとに分けたい場合には便利ですが、Load Imageのimageに入れる値の形が変わります。使うなら、GUIでそのサブフォルダの画像を選んだ状態でAPI形式に書き出し、imageの値がどう書かれるかを確かめてから揃えてください。

まとめ

ComfyUI APIへの画像入力は、/upload/imageでサーバーに保存し、返ってきたnameをLoad Imageのimageに書いて/promptへ投入する三段階です。img2imgはこれにdenoiseの差し替え、Inpaintingはマスク画像のアップロードとLoad Image Maskの接続が加わります。

GUIと同じ画像・seed・denoiseで出力が一致すれば配線は正しく、一致しなければまずimage値を疑ってください。利用者の入力が入口になる構成では、ファイル名を作り直し、内容とサイズを確かめ、書き換える項目を限ることを先に決めておきます。

スポンサーリンク