【Stable Diffusion】外部デバイスからAPI経由で画像生成する方法をReact + TypeScriptで実装する

導入

いきなりですが、ハイスペックなゲーミングPCを持て余していないでしょうか?

「最近忙しくてゲームができていない……」「買ったはいいものの、そこまでハイスペックじゃなくてもよかった……」なんて人は、もしかしたらいるかもしれません。自分の場合も、最近はスマホばかり見ていて、せっかく買ったPCをなかなか使えずにいました。

そんなPCをそのまま放置するのは、ちょっともったいないですよね。

ですが、そのPCに入っているグラフィックボード(GPU)は、AIの画像生成において最も活躍するパーツなんです。

そこで「このGPU搭載PCを画像生成の専用サーバーにして、手元のスマホやノートPCから指示を送って動かせるようにすれば、持て余したマシンパワーを有効活用できるのでは?」と考えました。

本記事ではそれを実現するため、「外部デバイスからAPI経由で画像生成する方法」として、Stable Diffusion(以下、SD)をローカル環境でAPIサーバーとして起動し、外部デバイスから画像生成する機能をReact + TypeScriptで実装していきます。

※ 本記事ではローカルネットワーク内(家庭内LAN)での利用を前提としています。

ファイアウォール設定やネットワーク設定を変更するため、内容を理解した上で自己責任で実施してください。

また、カフェやホテルなど、不特定多数が利用するネットワーク環境での利用は推奨しません。

また、推奨スペックは以下です。

項目 推奨スペック 実機スペック(筆者環境)
GPU RTX 30シリーズやRTX 40シリーズ RTX 2060
VRAM 12GB以上 6GB
メモリ 16GB以上 16GB
CPU Intel Core i5 / Ryzen 5以上 Intel Core i7
ストレージ 512GB以上 1TB
OS Windows 10 / 11 Windows 11
PC デスクトップ型 ノートPC

画像生成までの流れ

この実装の流れは下記となります。

  1. GPU搭載PC(SD)のAPI設定
  2. 外部デバイスとのネットワーク接続確認
  3. React + TypeScriptでAPIリクエストの実装
  4. 画像生成

    GPU搭載PC(SD)のAPI設定

    SD WebUIのインストール

    まず、GPUを搭載しているPCにStable Diffusion WebUIをインストールします。

    SDは複数存在しますが、これから実装する際に使用するものは「AUTOMATIC1111」「Forge」のどちらかであれば問題ありません。

    今回は「Forge」を使用します。
    Forge はGPUメモリ(VRAM)の使用効率が良く、より快適に画像生成を行えます。

    下記のGithubリポジトリのREADMEに従ってインストールしてください。

    AUTOMATIC1111:https://github.com/automatic1111/stable-diffusion-webui

    Forge:https://github.com/lllyasviel/stable-diffusion-webui-forge?tab=readme-ov-file&_fsi=8i9eFX5y

    インストールし、webui-user.batrun.batで「http://127.0.0.1:7860」の画面が開けたらOKです。

    実は、この画面を外部デバイスから開いてプロンプトを入力すれば、APIを利用する必要はありません。

    ですが、本記事ではあえてWebUIを直接操作せず、API経由で画像生成を行っていきます。

    API化しておけば、自分好みのUIを作れたり、スマホ向けに使いやすくしたりと、より自由に画像生成機能を扱えるようになります。

    APIモードで起動する

    外部デバイスから画像生成を行うため、SDをAPIモードで起動します。

    Forge や AUTOMATIC1111 では、起動オプションを追加することでAPIを有効化できます。

    webui-user.batをメモ帳で編集し、下記のように COMMANDLINE_ARGS を設定してください。

    set COMMANDLINE_ARGS=--api --listen
    オプション 内容
    --api API機能を有効化
    --listen

    外部デバイスからアクセス可能にする

    設定後、webui-user.batファイルを起動します

    「Running on local URL: http://0.0.0.0:7860」が画面に表示されていればOKです。

    ※実際にブラウザからアクセスするURLは、GPU搭載PCのIPアドレス+ポート番号7860です。

    外部デバイスとのネットワーク接続確認

    IPアドレス確認と接続確認

    外部デバイスからGPU搭載PCへアクセスできるか確認します。

    まず、GPU搭載PCのIPアドレスを確認します。

    Windows の場合は、コマンドプロンプトで下記を実行してください。

    ipconfig

    すると下記のような情報が表示されます。(※ IPアドレスは環境によって異なります。)

    IPv4 アドレス . . . . . . . . . . . .: 192.168.××.×

    別PCから接続できるかも確認しておきましょう。

    ping 192.168.××.×

    応答が返ってくれば、外部デバイスからGPU搭載PCへネットワーク接続できています。

    192.168.××.× に ping を送信しています 32 バイトのデータ:
    192.168.××.× からの応答: バイト数 =32 時間 =6ms TTL=128

    もし下記のようなメッセージが表示される場合は、ネットワーク接続できていません。

    Request timed out.

    その場合は、下記を確認してください。

    • GPU搭載PCと別PCが同じWi-Fiに接続されているか
    • IPアドレスが正しいか
    • Windows Defender ファイアウォールで通信がブロックされていないか
    • セキュリティソフトによって通信が遮断されていないか

    次に、別PCのブラウザから下記URLへアクセスしてください。

    http://192.168.××.×:7860/docs

    下記のような画面が表示されればOKです。

    もし表示されない場合は、Windows Defender ファイアウォールや、その他セキュリティソフトによって通信がブロックされている可能性があります。

    また、別PCがMacの場合はローカルネットワーク設定がoffになっている可能性があります。
    設定の「プライバシーとセキュリティ」からローカルネットワークの設定を見てみてください。

    ファイアウォールに規則を追加

    Windows Defender ファイアウォールによって通信がブロックされる場合は、受信規則を追加します。

    まず、「Windows Defender ファイアウォール(詳細設定)」を開きます。
    左側メニューの「受信の規則」を選択し、右側の「新しい規則」をクリックしてください。

    続いて、下記の内容で設定します。

    項目 設定値
    規則の種類 ポート
    プロトコル TCP
    特定のローカルポート 7860
    操作 接続を許可する
    プロファイル プライベート
    名前 SD_7860 などわかりやすいもの

    また、接続しているWi-Fiのネットワークプロファイルの種類も「プライベート」に変更します。

    Windows の場合は、
    「設定」 → 「ネットワークとインターネット」 → 「Wi-Fi」 → SSID(接続しているネットワーク名)プロパティ
    を開き、「ネットワーク プロファイルの種類」を「プライベート ネットワーク」に変更します。

    ※ 「パブリック」のままだと、Windows が外部ネットワークとして扱い、通信がブロックされる場合があります。

    ※ 自宅Wi-Fiなど、信頼できるネットワークでのみ「プライベート ネットワーク」を使用してください。
    カフェやホテルなど、不特定多数が利用するWi-Fiで「プライベート ネットワーク」を設定すると、同一ネットワーク上の他デバイスからアクセスされる可能性があります。

    再度、別PCのブラウザから下記URLへアクセスしてください。

    http://192.168.××.×:7860/docs

    FastAPIの画面が表示されればOKです。

    これでAPI設定、接続確認完了です。

    React + TypeScriptでAPIリクエストの実装

    それでは、実際に React + TypeScript から Stable Diffusion のAPIを呼び出して画像生成を行います。

    今回はViteを使い、環境構築を行います。

    ※ここからの環境構築やコードの実装作業は、すべて手元の外部デバイス(ノートPCなど)で行います。

    環境構築

    まず、Node.jsをインストールします。下記公式サイトからインストールしてください。

    Node.js公式サイト:https://nodejs.org/ja

    インストール後、Windows PowerShellなどで下記を実行し、バージョンが表示されればOKです。

    node -v
    npm -v

    次に、ご自身が普段コードを管理している場所(デスクトップなど)に移動し、下記を実行してReact + TypeScript環境を構築します。

    # (プロジェクト名は「sd-api-sample」としています)
    npm create vite@latest sd-api-sample -- --template react-ts

    ※ 途中で Ok to proceed? (y) と聞かれた場合は、そのまま y を押して進めて進めてください。

    コマンドの実行が終わると、新しく sd-api-sample というフォルダが作成されます。 最後に、そのフォルダへ移動して必要なパッケージをインストールしましょう。

    cd sd-api-sample
    npm install

     

    環境構築ができたらエディタツールで作成した「sd-api-sample」フォルダを開きます。(今回はVSCode)

    ターミナルで下記を実行し、http://localhost:5173/ と表示され、開いたときにViteとReactの初期画面が表示されれば環境構築はOKです。

    実装

    実装に移ります。

    App.tsxに直接下記のソースを載せ、GPU搭載PCに接続ができていれば、http://localhost:5173/の「画像を生成する」ボタンで画像が生成できます。

    import { useState } from 'react';
    
    // ※「192.168.××.×」の部分は、先ほど確認した「GPU搭載PC」のIPアドレスに書き換えてください
    const SD_API_URL = 'http://192.168.××.×:7860/sdapi/v1/txt2img';
    
    function App() {
      const [prompt, setPrompt] = useState('a high quality photo of a cute cat, cinematic lighting');
      const [imageSrc, setImageSrc] = useState<string | null>(null);
      const [loading, setLoading] = useState(false);
      const [error, setError] = useState<string | null>(null);
    
      const generateImage = async () => {
        if (!prompt) return;
        setLoading(true);
        setError(null);
        setImageSrc(null);
    
        try {
          const payload = {
            prompt: prompt,
            negative_prompt: 'easynegative, low quality, bad anatomy, blurry',
            steps: 20,
            cfg_scale: 7,
            width: 512,
            height: 512,
          };
    
          const response = await fetch(SD_API_URL, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(payload),
          });
    
          if (!response.ok) {
            throw new Error(`APIエラーが発生しました。ステータス: ${response.status}`);
          }
    
          const data = await response.json();
          
          if (data.images && data.images.length > 0) {
            const base64Image = data.images[0];
            setImageSrc(`data:image/png;base64,${base64Image}`);
          } else {
            throw new Error('画像データがレスポンスに含まれていませんでした。');
          }
        } catch (err: unknown) {
    	  if (err instanceof Error) {
            console.error(err);
            setError(err.message || '画像の生成に失敗しました。');
    	  } else {
            setError('予期せぬエラーが発生しました。');
          }
        } finally {
          setLoading(false);
        }
      };
    
      return (
        <div>
         <h1>SD API 画像生成</h1>
          <textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} rows={4} />
          <button
            onClick={generateImage}
            disabled={loading}
          >
           {loading ? 'GPU搭載PCで画像を生成中...' : '画像を生成する'}
          </button>
    
          {error && <p>{error}</p>}
    
          {imageSrc && (
           <div>
            <h2>生成された画像</h2>
            <img 
              src={imageSrc} 
              alt="Generated Art" 
            />
           </div>
          )}
        </div>
      );
    }
    
    export default App;

    重要な部分は下記です。

    • payload
    • Base64

    payload

    APIのやり取りにおける「ペイロード」とは、一言でいうと「送りたいデータそのもの(中身)」のことです。

    今回は Stable Diffusion(サーバー)に対して、「このプロンプトで、ステップ数は20で、縦横512pxで生成してね」という設定パラメータの塊を指します。

    もし、「イラスト風のモデルに変えたい」といった指定をコードから行いたい場合は、この payload の中に override_settings という項目を追加することで、リクエストごとにモデルを切り替えることも可能です。

    そのほかにも、事前に確認したFastAPIのドキュメントページ(http://192.168.××.×:7860/docs)から /sdapi/v1/txt2img の項目を開くと、送信できるすべてのパラメータ(Request Body)が確認できますので、ぜひ参考にカスタマイズしてみてください。

    Base64

    Stable DiffusionのAPIは、生成した画像を「画像ファイル(.png)」としてではなく、「Base64」と呼ばれる、画像を長い文字列に変換したデータで返してきます。

    そのため、React側で画像として画面に表示できるように、data:image/png;base64, という文字列を頭にくっつけてから img タグの src に渡しています。

    画像生成

    では、画像を生成してみます。

    以下の流れで画像生成を行います。

    1. 外部デバイス(ノートPCなど)のVSCodeのターミナルで、先ほど環境構築の最後に動かした `npm run dev` が起動していることを確認します。
    2. ブラウザで `http://localhost:5173/` を開きます。画面に「SD API 画像生成」というタイトルとテキストエリア、ボタンが表示されていればOKです。
    3. コード(App.tsx)の SD_API_URL に入力したIPアドレスが、先ほど確認したGPU搭載PCのものに正しく書き換わっているかを再度確認します。
    4. テキストエリアにプロンプトがあるのを確認し、**「画像を生成する」** ボタンをクリックします。

    ボタンが「GPU搭載PCで画像を生成中…」に変わったら、「GPU搭載PC」のコマンドプロンプト(ForgeやAUTOMATIC1111の黒い画面)を覗いてみてください。

    100%|███████████████████████████████████████████| 20/20 [00:02<00:00,  8.20it/s]

    このように進捗バーが動き出していれば成功です。

    生成が完了すると、手元のデバイスにデータが送り返され、ブラウザ上に画像が表示されます。

    このような感じで画像が生成できました。

    もし、生成できない場合は、画像生成用のモデル(チェックポイント)が正しく読み込まれていない可能性があります。

    インターネット上に配布されているものもありますので、ダウンロードし、Stable Diffusionのインストールフォルダ内にある models/Stable-diffusion ディレクトリに入れると解決するかもしれません。

    さいごに

    SDのAPIを使用するまでには、意外と多くの手順や注意点があります。

    自分は外部デバイスにMacBookを使用しましたが、ローカルネットワーク設定がデフォルトで「オフ」になっていることに気づかず、原因究明で丸一日溶けました……。

    APIモードの起動だけで済めば簡単なのですが、ファイアウォールの設定など、ネットワークの少し踏み込んだ対応もしないといけないため、途中で断念する方ももしかしたらいるかもしれません。

    ですが、この記事を見て少しでもそのような方の助けになり、持て余していたPCを有効活用できるようになれば幸いです。

    プログラムから叩けるようになると、ここからさらに自分好みのUIを拡張していけるので、ぜひオリジナルの画像生成ツールを作ってみてください。

     

    記事を読んで興味を持った方はぜひコチラから↓