
話題沸騰中の 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
でブラウザでローカルのページが自動的に開き、以下の表示になればローカルの開発環境は整いました。

参照できない場合は、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
-
生成する画像のサイズを
256x256
、512x512
、1024x1024
の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 ライブラリを今回も使用します。
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
に以下のようなレスポンスが返されます。
{ "created": 1589478378, "data": [ { "url": "https://..." }, { "url": "https://..." } ] }
{ "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