Next.js + OpenAI API で作った画像生成アプリで遊んでみた

OpenAI API と React(Next.js) を利用した簡単な画像生成アプリの作り方を、サンプルコード付きでご紹介しています。
2023.03.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、アノテーション テクニカルサポートの Shimizu です。

筆者の前回の記事では OpenAI API を使用した基本的な質問アプリを Next.js で作成しました。

そこで今回は OpenAI の画像生成 API を利用して、テキストで入力した通りの画像を生成してくれるアプリを作って遊んでみました。
本記事ではサンプルコード付きで画像生成アプリの作成手順をご紹介しています。

完成品イメージ

前回と同様に Docker コンテナ上の Next.js 環境で作成したアプリを、ブラウザ上で動かすという手順です。
それでは、いってみましょう!

step1. 事前準備

まず、事前に下記を用意しましょう。

(1) OpenAI のアカウント登録、API シークレットキーの取得
筆者は下記のサイトを参考にさせていただきました。

OpenAI | APIキーを取得する方法 | ONE NOTES

※ API キーの取得は無料ですが、API の使用は有料です。
アカウント登録時に $18 分のクレジットが付与されるため、その範囲内なら無料で使用できますが、それを超えると課金対象となることに注意しましょう。

(2) Docker コンテナ上で Next.js のプロジェクトを作成する
ここは筆者の前回の記事の step2 で詳しく解説していますので、まだ準備ができていない場合は参考にしてみてください。

[初心者向け] Next.js で初めての OpenAI API アプリを作ってみた(Tailwind CSS、Docker 利用) | DevelopersIO

上の記事の step2 を実施し、下記のような Next.js プロジェクトの初期画面をブラウザで表示できたら、アプリの作成に進みましょう!

step2. Next.js で画像生成アプリを作成する(サンプルコードあり)

Next.js のプロジェクトフォルダを VSCode 等のテキストエディタで開き、初期ページの実体ファイル「src > pages > index.js」を開きます

下記のサンプルコードを丸ごとコピーして index.js に貼り付け、10行目の const API_KEY = の部分のみ実際に取得した OpenAPI のシークレットキーに置き換えましょう。

※注意:OpenAI の API シークレットキーは、外部に公開しないようにしましょう!
今回はローカル環境でのテストのみのためコードに直接キーを記述していますが、作成したアプリを外部公開したり Git にコードを push する場合は必ず API キーを削除して環境変数へ格納するなどの実装をしましょう。

サンプルコード: index.js の内容

import { useState } from "react";
import Head from 'next/head'
import axios from 'axios';

export default function Home() {
  const [inputText, setInputText] = useState('a cat sitting on a couch');
  const [imageSize, setImageSize] = useState("256x256");
  const [numImages, setNumImages] = useState(1);
  const [imageURLs, setImageURLs] = useState([]);
  const API_KEY = 'step1 で取得した OpenAI API シークレットキー';

  const handleSubmit = async (event) => {
    event.preventDefault();
    try {
      const response = await axios.post(
        'https://api.openai.com/v1/images/generations',
        {
          model: 'image-alpha-001',
          prompt: inputText,
          size: imageSize,
          num_images: Number(numImages),
          response_format: "url"
        },
        {
          headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${API_KEY}`,
          },
        }
      );
      if (response.data && response.data.data) {
        setImageURLs(response.data.data);
      }
    } catch (error) {
      console.error(error);
    }
  };  

  const handleChange = (event) => {
    setImageSize(event.target.value);
  };

  return (
    <div>

      <Head>
        <title>Next.js で作る OpenAI 画像生成アプリ</title>
        <meta name="description" content="AI が画像を生成します" />
      </Head>

      <h1 className="text-2xl font-bold leading-3 text-gray-900 m-5">Next.js で作る OpenAI 画像生成アプリ</h1>

      <div className="mx-4 my-2 p-4 flex-auto bg-white shadow rounded-md">
        <form onSubmit={handleSubmit}>

          <div className="flex flex-wrap">

            <div className="w-1/2 pr-4 py-2">
            <h2 className="block text-sm font-medium leading-6 text-gray-900">生成したい画像の内容(英語:例 a cat sitting on the couch):</h2>
              <textarea
                rows={4}
                className="px-2 block w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:py-1.5 sm:text-sm sm:leading-6"
                value={inputText}
                onChange={(event) => setInputText(event.target.value)}                  
              />
            </div>

            <div className="w-1/2 pl-4 py-2">
              <div>
                <h2 className="block text-sm font-medium leading-6 text-gray-900">生成する画像の数(1 - 10 で指定):</h2>
                  <input
                    type="number"
                    min="1"
                    max="10"
                    value={numImages}
                    onChange={(event) => setNumImages(event.target.value)}
                    className="block w-1/6 rounded-md border-0 p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                  />
              </div>

              <div className="mt-4">
                <h2 className="block text-sm font-medium leading-6 text-gray-900">画像サイズ:</h2>
                <div className="flex block text-sm font-medium leading-6 text-gray-900">
                  <label className="mr-3">
                    <input
                      type="radio"
                      name="imagesize"
                      value="256x256"
                      checked={imageSize === "256x256"}
                      onChange={handleChange}
                    />
                    256x256
                  </label> 

                  <label className="mr-3">
                    <input
                      type="radio"
                      name="imagesize"
                      value="512x512"
                      onChange={handleChange}
                    />
                    512x512
                  </label> 

                  <label className="mr-3">
                    <input
                      type="radio"
                      name="imagesize"
                      value="1024x1024"
                      onChange={handleChange}
                    />
                    1024x1024
                  </label>
                </div>
              </div>

          </div>

            <div className="py-3">
              <button
                type="submit"
                className="inline-flex justify-center rounded-md bg-indigo-600 py-2 px-3 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500"
              >
                画像を生成する
              </button>
            </div> 
          </div>
        </form>
      </div>

      <div className="mx-4 my-2 p-4 flex-auto bg-white shadow rounded-md">
        <h2 className="block text-sm font-medium leading-6 text-gray-900">生成結果:</h2>
        {imageURLs.length > 0 && (
          <div className="flex">
            {imageURLs.map((item, index) => (
              <div className="mr-2">
                <img key={index} src={item.url} alt={`generated image ${index}`} />
              </div>
            ))}
          </div>
        )}            

      </div>
    </div>
  );
}

index.js にコードを貼り付けて保存すると、ブラウザで表示した初期ページに自動でリロードがかかり、下記のような画面に切り替わると思います。

実はこれだけで、画像生成アプリの作成は完了です!

なお今回は詳しく触れませんが、筆者の前回の記事と同じく Tailwind CSS で見栄えを整えています。前回の記事の step4 を参考に、Next.js で Tailwind CSS を使用するため設定もしておきましょう。

それでは、次は作成した画像生成アプリで実際に遊んでみましょう!

step3. 画像生成アプリで遊んでみる

使い方はとてもシンプルで、左側のテキストエリアに作成したい画像の内容(英文)を入力し、右側で画像のパターン数とサイズ(ピクセル)を選択して「画像を生成する」ボタンをクリックするだけです。

※ 画像パターン数を多く、サイズを大きくするほど OpenAI API のクレジットを多く消費するため、練習の時はこれらを低く設定しましょう。

まずは例にある a cat sitting on the couch と入力して画像を生成してみます。
すると指示した通り、カウチに座った猫の画像が生成されました。

これだけだと普通すぎて AI の力を実感できないので、今度は a cat with sun glass sitting on the couch と入力してみます。
すると今度は、サングラスをかけた猫の画像が生成されました。

※ 生成した画像を保存したい場合は、ブラウザ上の画像を「名前を付けて保存」などでローカルに保存しましょう。生成された画像の URL は 1 時間で有効期限が切れて見れなくなり、かつ全く同じキーワードで再作成しようとしても、毎回違う画像が生成されるためです。

それでは、もう少し遊んでみましょう!
次は完全に筆者の趣味ですが、映画「ブレードランナー」や「AKIRA」のようなサイバーな世界観の画像が欲しいので Cyberpunk motorcycle ryder in futuristic neon city と入力してみます。

すると、これだけでもかなりカッコいい画像が生成されました。このような cyberpunk などのキーワードは AI の得意分野のようです。

次は前回の指示に hyper realistic 8K detailed というキーワードを追加して再生成してみます。これは、よりリアルな画像を生成したい時のおまじないのようなものです。

すると、先ほどより緻密に描き込まれた画像が生成されました。

このように、シンプルながら充分に遊べる画像生成アプリを作ることができました。
お疲れさまでした!

注意事項

楽しくてつい画像を作りまくってしまいそうですが、step1 で述べた通り API の使用(画像の生成)のたびにクレジットが消費されるため、無料枠の $18 を超えると課金が発生することには注意しましょう。
OpenAI 管理画面の下記のページで現在の利用金額を確認でき、かつ指定した金額を超えるとアラートや制限がかかるような設定もできるので、最初に設定しておくことをおススメします。

また、作成した画像の著作権などについては下記の記事で解説されているので、ご一読いただけると幸いです。

【OpenAI】DALL-Eを使ってテスト用の画像データを生成してみた | DevelopersIO

さいごに

いかがでしたでしょうか。
画像を生成する AI サービスは以前からありましたが、それをここまで簡単に自作アプリに組み込めるようになったのは、やはり感動しました。

今回は画像生成 API をそのまま使用したシンプルなアプリを作りましたが、他の API との組み合わせなど、アイデア次第で色々と面白いものが作れそうです。

筆者も今後さらに高度で実用的なアプリ作りにチャレンジし、また機会があれば記事にしたいと思います。 この記事が皆様の OpenAI アプリ開発を始める一助となれば幸いです!

API Reference - OpenAI API