DigiPress

Highly Flexible WordPress Theme

メニュー

OpenAI API で AI 画像生成 Web アプリを React で作成するサンプル

OpenAI API で AI 画像生成 Web アプリを React で作成するサンプル

話題沸騰中の AI チャットボット「ChatGPT」の API が先日公開されましたが、開発元の OpenAI では他にも 複数の API を提供しており、 その中でも今回は指定したキーワードや作成したい画像を説明する簡潔な文章から AI がイメージ画像を自動で作成してくれる画像生成 API を利用して React で簡単な Web アプリを作ってみたいと思います。

ChatGPT API を利用した React でのチャットアプリの作り方は別の記事にまとめていますのでそちらをご覧ください。

受け取るデータの種類(テキスト or 画像)が異なるだけで、同じ OpenAI のAPI なのでほぼそのまま転用できます。


ローカルの開発環境の作成

Node.js がインストールされていない場合は、以下を参考にインストールしておきましょう。

Node.js のインストールが完了したら、そのままターミナルで開発環境を作成するディレクトリまで移動しておきます。

アプリ出力先に移動
cd /Users/myname/LocalSites

上記は/Users/myname/LocalSites の場合。

npx コマンドで早速アプリの作成を行います。
ここでは「ai-image-sample-app」という名前のディレクトリに作成(出力)します。

アプリの作成
npx create-react-app ai-image-sample-app

プロンプトが正常に返ってきたら、「ai-image-sample-app」というアプリ専用のフォルダが作成されているので、そこに移動し、ローカルサーバーの動作確認をします。

サーバーの起動
# アプリディレクトリに移動
cd ai-image-sample-app

# ローカルのサーバーを起動
npm start

npm start でブラウザでローカルのページが自動的に開き、以下の表示になればローカルの開発環境は整いました。

作成直後の状態
初期状態(App.js を実行したもの)

参照できない場合は、URL を http://localhost:3000/ として直接開いてみてください。

OpenAI で API キーを発行

画像生成 API を利用するには、まず OpenAI の API キーが必要になるため、以下を参考に API キーを発行しましょう。

画像生成 API

画像生成をリクエストするエンドポイントは https://api.openai.com/v1/images/generations となっています。

https://platform.openai.com/docs/api-reference/images/create

この API に対してヘッダー(headers)に認証情報とリクエストボディに作成する画像に関するパラメータ(キーワードやフレーズ、枚数、サイズ、フォーマット)を渡してリクエストを行います。

主なパラメータ
prompt ※必須

作成したい画像に関するキーワードや短いフレーズを指定します。
この情報が細かいほど精度の高い画像が生成されます。

日本語でも単語に基づいた画像を生成できないわけではありませんが、現時点ではかなり精度は低いので基本的には英語でプロンプトは与えるようにした方が良いと思います。

例: two puppies, cute, playing in the park

プロンプトは最大1000文字まで

n

1回のリクエストに対して生成する画像の数を 1〜10の範囲で指定します。
デフォルトは 1 です。

1分間に生成できる画像は 50 枚までです。

size

生成する画像のサイズを 256x256512x5121024x1024 の3つのサイズ(文字列)から指定します。
現時点ではこの3段階の正方形しか選べず、任意のサイズ、比率を指定することはできません。
デフォルトは 1024×1024 です。

サイズが大きいほど生成に時間がかかります。

response_format

生成された画像を受け取る際のフォーマットを url (画像のパス)または b64_json (Base64にエンコードされたデータ)から指定します。
デフォルトは url です。

URL の場合は、1時間で URL が無効になります。

各データの状態管理

大量の API リクエストを発生させないよう、API と通信中はフォーム送信を無効にしてローディング表示をしたり、直前の生成画像を参照したい場合などに備え、必要な情報は useState フックで保持しておいて必要なタイミングで更新するようにします。

import { useState } from 'react';

// 画像データ
const [ imageData, setImageData ] = useState( '' );
// ローディング表示のフラグ
const [ isLoading, setIsLoading ] = useState( false );
// プロンプト
const [ prompt, setPrompt ] = useState( '' );
// エラー情報
const [ error, setError ] = useState( '' );
// 受け取る画像のフォーマット
const [ format, setFormat ] = useState( 'b64_json' );
// 生成する画像のサイズ
const [ generateSize, setGenerateSize ] = useState( '512x512' );

HTML(出力)の構成

プロンプト(画像の説明、キーワード等)、生成サイズ、フォーマットの情報をユーザーから受け付けるための HTML フォームを作成します。

ここでは適当に以下のようにします。

入力フォーム
<div className='generate-form'>
    <textarea
      value={ prompt }
      cols='40'
      maxLength='1000'
      onChange={ e => {
        setPrompt( e.target.value ) ;  // プロンプトの更新
      } }
    />
    <select
      onChange={ e => setGenerateSize( e.target.value ) } // 生成サイズの更新
      value={ generateSize }
    >
      <option value="256x256">256 x 256</option>
      <option value="512x512">512 x 512</option>
      <option value="1024x1024">1024 x 1024</option>
    </select>
    <select
      onChange={ e => setFormat( e.target.value ) } // フォーマットの更新
      value={ format }
     >
       <option value="url">URL</option>
       <option value="b64_json">Base64</option>
    </select>
    <button
      onClick={ generateImage } // 画像生成を指示
      disabled={ isLoading }
    >
    { isLoading ? '作成中...' : '画像作成' }
    </button>
</div>

後述する関数によって、API リクエストが成功し生成された画像が受け取られると、imageData 変数に代入されますが、imageData 変数に値が入った場合はフォームの下に画像を表示するようにします。

生成画像の表示
{ imageData && (
  <div className='generated-image-area'>
    <figure>
      <img 
        src={ format === 'b64_json'
          ? `data:image/png;base64,${ imageData }`  // Base64 エンコードの場合
          : imageData  // URL の場合
        }
        alt="Received Data"
        width={ imageSize }
        height={ imageSize }
      />
    </figure>
  </div>
) }

API へのリクエスト

前回の ChatGPT のサンプルアプリと同様に、API とのやり取りには扱いやすい axios ライブラリを今回も使用します。

axios のインストール
npm install axios --save

axios を利用する場合、以下のように記述することで API リクエストを送信できます。

リクエスト例
const API_KEY = '発行したAPIキー'; 

const response = await axios.post(
  'https://api.openai.com/v1/images/generations',
  { // リクエストボディ
    prompt: 'two puppies, cute, playing in the park',  // 生成したい画像に関する説明
    n: 1, //取得する枚数 
    size: '512x512', // 生成する画像サイズ
    response_format: 'b64_json', // 取得する画像のフォーマット( URL or Base64 フォーマット)
  },
  { // ヘッダー(認証情報)
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${ API_KEY }`
    }
  }
);

リクエストが成功すると、response に以下のようなレスポンスが返されます。

url の場合
{
  "created": 1589478378,
  "data": [
    {
      "url": "https://..."
    },
    {
      "url": "https://..."
    }
  ]
}
b64_json の場合
{
  "created": 1589478378,
  "data": [
    {
      "b64_json": "iVBORw0KGgoAAAA..."
    },
    {
      "b64_json": "iVBORw0KGgoAAAA..."
    }
  ]
}

リクエストは非同期処理として実行して try...catch...finally の構文でレスポンスの受け取り、エラーハンドリング、後始末の処理を行います。

一連の処理をまとめた関数
const generateImage = async() => {
  try {
    // API リクエスト
    const response = await axios.post(
      ...
    );

  } catch ( error ) {
    // エラーハンドリング
    console.log( error.message );

  } finally {
     // 後始末(ローディング表示の終了など)
  }
};

画像生成・取得を行う関数

API へのリクエストから画像データの取得、エラーハンドリングをまとめた generateImage メソッドは、不要なレンダリングを減らすために、useCallback フックを利用して、 format, generateSize, prompt が更新されたときのみ再レンダリングするようにし、以下のようにまとめます。

import { useCallback } from 'react';

const generateImage = useCallback( async () => {
  if ( !prompt ) {
    alert( 'プロンプトがありません。' );
    return;
  }

  if ( isLoading ) return;

  setIsLoading( true );

  try {
    const response = await axios.post(
      `${ API_URL }images/generations`,
      {
        prompt,
        n: 1,
        size: generateSize,
        response_format: format,
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${ API_KEY }`
        }
      }
    );

    // 画像データの保持
    setImageData( response.data.data[0][ format ] );

    } catch ( error ) {
      setError( error.toString() );

    } finally {
      setIsLoading(false);
    }
}, [ format, generateSize, prompt ] );

画像を生成してみる

App.js の入れ替え

npx コマンドで作成したアプリの初期状態で作成されている /i-image-sample-app/src/App.js の内容を、ここまでの処理をまとめたものと入れ替えます。

import { useCallback, useEffect, useState } from 'react';
import axios from 'axios';

const API_URL = 'https://api.openai.com/v1/';
const API_KEY='あなたのAPIキー';

const App = () => {
  const [ imageData, setImageData ] = useState( '' );
  const [ isLoading, setIsLoading ] = useState( false );
  const [ prompt, setPrompt ] = useState( '' );
  const [ error, setError ] = useState( '' );
  const [ format, setFormat ] = useState( 'b64_json' );
  const [ generateSize, setGenerateSize ] = useState( '512x512' );
  const [ imageSize, setImageSize ] = useState( 512 );

  const imageSizes = {
    "256x256": 256,
    "512x512": 512,
    "1024x1024": 1024
  };

 const generateImage = useCallback( async () => {
  if ( !prompt ) {
    alert( 'プロンプトがありません。' );
    return;
  }

  if ( isLoading ) return;

  setIsLoading( true );

  try {
    const response = await axios.post(
      `${ API_URL }images/generations`,
      {
        prompt,
        n: 1,
        size: generateSize,
        response_format: format,
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${ API_KEY }`
        }
      }
    );

    setImageData( response.data.data[0][ format ] );

    } catch ( error ) {
      setError( error.toString() );

    } finally {
      setIsLoading(false);
    }
  }, [ format, generateSize, prompt ] );

  useEffect(() => {
    setImageSize( imageSizes[ generateSize ] );
  }, [ generateSize ] );

  return (
    <div className='container'>
      <div className='generate-form'>
        <textarea
          value={ prompt }
          cols='40'
          maxLength='1000'
          onChange={ e => {
            setPrompt( e.target.value ) ;
          } }
        />
        <select
          onChange={ e => setGenerateSize( e.target.value ) }
          value={ generateSize }
        >
          <option value="256x256">256 x 256</option>
          <option value="512x512">512 x 512</option>
          <option value="1024x1024">1024 x 1024</option>
        </select>
        <select
          onChange={ e => setFormat( e.target.value ) }
          value={ format }
         >
           <option value="url">URL</option>
           <option value="b64_json">Base64</option>
        </select>
        <button
          onClick={ generateImage }
          disabled={ isLoading }
        >
        { isLoading ? '作成中...' : '画像作成' }
        </button>
      </div>
      { error && (
        <div className='error-message'>
          <pre>{ error }</pre>
        </div>
      ) }
      { imageData && (
        <div className='generated-image-area'>
          <figure>
            <img 
              src={ format === 'b64_json'
                ? `data:image/png;base64,${ imageData }`
                : imageData
              }
              alt="Received Data"
              width={ imageSize }
              height={ imageSize }
            />
          </figure>
	    </div>
      ) }
    </div>
  );
};

export default App;

index.js は特に修正する必要はありません。
必要に応じて index.css は適当に編集してください。

動作確認

それではフォームが表示されるか確認してみましょう。
http://localhost:3000/ を更新します。

入力フォームの確認

入力フォームは問題なく表示できました。

それでは実際にプロンプトを指定して AI に画像生成をリクエストして表示されるか試してみます。

画像生成の確認

プロンプトは、現時点では英語で指定するほうが、はるかに精度が高い画像が生成できます。
日本語の場合でも通じないことはありませんが、ChatGPT とは異なり AI が正しく理解できないと適当な料理の画像が返ってきたりします。

プロンプトを日本語で指定した場合
日本語でも理解してくれた場合

「公園で遊んでいる2匹の子犬」と指定しましたが、なんとこちらはめずらしくしっかり理解してくれました。
では「ドラゴンボールの孫悟空」と指定してみましょう。

日本語が理解されなかった場合

全く解釈されなかったわけではありませんが、孫悟空だけを切り取ったのか、サルがでてきました。

では英語で指定し直してみるとどうなるでしょうか。

それっぽいイラストが生成されました。

より具体的な指示(プロンプト)を指定してみましょう。
お母さんと赤ちゃんの微笑ましい画像を生成するよう「A baby, cute, playing with mother, smiling」と適当に指定してみます。

いい感じのそれっぽい画像ができました。
ただ、人物画像全般に言えることですが、AI はまだ手(指)の表現が苦手なようです。
親指がなかったり本数が少なかったり多かったり、指がぐちゃっとなっている画像が現時点では多い印象です。

同じ画像は2度と生成されないので、一番しっくり来る画像が生成されたら保存しておきましょう。


まとめ

ChatGPT に続き、React で OpenAI の画像生成 API を利用した Web アプリのサンプルを今回は作ってみました。
開発側では API をコールして受け取ったデータを表示しているだけなので、このような高度な API を誰でも簡単に利用してアプリが作れるようになったことがすごいですね。

次回は、ノーコードツールとして人気の Make というサービスを利用して、LINE でメッセージ(生成したい画像のプロンプト)を送信すると画像を生成して返信してくれる画像生成チャットボットを作るサンプルを紹介したいと思います。

文字通りノーコードなので、API キーさえあれば誰でも簡単に LINE のチャットボットが作れます。

サムネイル素材 : 著作者:rawpixel.com/出典:Freepik

Share / Subscribe
Facebook Likes
Tweets
Hatena Bookmarks
Pinterest
Pocket
Feedly
Send to LINE