OpenAI 画像生成API チュートリアル DALL-E3編(2/4)

# 📖 **Table of Contents: 目次**

- [📖 **Table of Contents: 目次**](#-table-of-contents-目次)
- [🤖 **OpenAI Image Generation API Tutorial: OpenAIの画像生成APIチュートリアル**](#-openai-image-generation-api-tutorial-openaiの画像生成apiチュートリアル)
  - [📌 **Project Overview: プロジェクトの概要**](#-project-overview-プロジェクトの概要)
  - [🚀 **Image Generation Implementation (DALL-E 3): 画像生成(DALL-E 3)の実装**](#-image-generation-implementation-dall-e-3-画像生成dall-e-3の実装)
    - [📁 **Folder Structure: フォルダ構成**](#-folder-structure-フォルダ構成)
    - [📝 **Implementation: 実装**](#-implementation-実装)
  - [🖥️ **Complete Code Overview: 今回のコード全体**](#-complete-code-overview-今回のコード全体)
  - [📝 **Conclusion: まとめ**](#-conclusion-まとめ)

# 🤖 **OpenAI Image Generation API Tutorial: OpenAIの画像生成APIチュートリアル**
第一回では、環境構築を行いました。
第二回では、画像生成(DALL-E 3)の実装を行います。
第三回では、画像分析(gpt-4-vision)の実装を行います。
第四回では、おまけとして画像編集と画像複製(DALL-E2)を行います。

今回は第二回ということで、画像生成(DALL-E 3)の実装を行います。

## 📌 **Project Overview: プロジェクトの概要**
- **第二回 - DALL-E 3による画像生成Webアプリケーション作成:**
  - ユーザーがテキストを入力（例: 「宇宙服を着た犬」）
  - 入力されたテキストに基づいてDALL-E 3が画像を生成

他の詳細については[第一回](https://qiita.com/yuyuyu2118/items/643f87538b86ce14605c)を参照してください。

## 🚀 **Image Generation Implementation (DALL-E 3): 画像生成(DALL-E 3)の実装**
今回の実装では、DALL-E 3を使用して画像を生成するWebアプリケーションを作成します。

### 📁 **Folder Structure: フォルダ構成**
このプロジェクトのフォルダ構成は最終的に以下の通りになります：
```
.
├── node_modules/
│   └── ... # 依存関係のモジュール
├── index.html # エントリポイントHTMLファイル
├── package-lock.json # 自動生成される依存関係の正確なバージョンを記録するファイル
├── package.json # プロジェクトのメタデータと依存関係を定義するファイル
├── script.js # クライアントサイド JavaScript
├── server.js # Node.jsサーバー
└── style.css # CSSスタイルシート
```

### 📝 **Implementation: 実装**
1. 第一回で作成したフォルダに移動して、VSCodeで開きます。
   ```powershell
    cd dalle3-tutorial
    code .
    ```

2. まず、script.jsにfetchを使用してバックエンドのAPIとの通信を行うコードを追加します。
   ```javascript
    const form = document.querySelector('form');
    const input = document.querySelector('input');
    const imageContainer = document.getElementById('image-container');

    form.addEventListener('submit', (event) => {
      event.preventDefault();
      const prompt = input.value;
      input.value = '';
      generateImage(prompt);
    });

    async function generateImage(prompt) {
      try {
        // /generateエンドポイントにPOSTリクエストを送信
        const response = await fetch('http://localhost:3000/generate', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ prompt })
        });

        // エラーハンドリング
        if (!response.ok) {
          throw new Error(errorMessage);
        }

        // レスポンスのJSONを取得する
        const data = await response.json();

        // 画像要素を生成して、imageContainerに追加
        if (data.image) {
          const imageElement = document.createElement('img');
          imageElement.src = data.image;
          imageContainer.appendChild(imageElement);
        } else {
          throw new Error('画像URLがレスポンスに含まれていません。');
        }
      } catch (error) {
        console.error('画像の生成に失敗しました。', error);
      }
    }
    ```
    このコードは、フォームが送信されたときに、入力されたテキストを取得し、バックエンドのAPIに送信します。
    送信時は、promptをJSON形式で送信します。
    また、レスポンスが成功した場合は、画像を生成して、imageContainerに追加します。

3. 次に、server.jsに/generateエンドポイントの記述をしていきます。
   ```javascript
    const OpenAI = require('openai');
    const express = require('express');
    const cors = require('cors');
    // デフォルトでOpenAI APIキーは環境変数から自動取得されます
    const openai = new OpenAI();
    const app = express();
    const port = 3000;

    app.use(cors());
    app.use(express.json());

    app.get('/', (req, res) => {
      res.send('Server is Running!');
    });

    app.post('/generate', async (req, res) => {
      try {
        // リクエストのJSONからpromptを取得
        const prompt = req.body.prompt;
        // 画像を生成
        const imageResponse = await openai.images.generate({
          model: "dall-e-3", // 画像生成モデル
          prompt: prompt, // 生成したい画像を文字で表したもの
          n: 1, // 生成する画像の数 DALL-E 3ではn=1のみサポートされています
          size: "1024x1024", // 生成する画像のサイズ DALL-E 3では1024x1024、1792x1024、1024x1792から選べます
        });

        // 画像のURLを取得 data[0]のように配列で取得する必要がある
        const imageUrl = imageResponse.data[0].url;
        if (!imageUrl) {
          return res.status(500).send({ error: '画像の生成に失敗しました。' });
        }

        // 成功した場合、画像URLを含むオブジェクトを返す
        res.status(200).json({ image: imageUrl });
      } catch (error) {
        res.status(500).send({
          error: '画像の生成に失敗しました。',
          details: error.message
        });
      }
    });

    app.listen(port, () => console.log('Listening on port', port));
    ```
    このコードは、/generateエンドポイントにPOSTリクエストが送信されたときに、画像を生成します。

    openai.images.generateに指定する内容は、以下のようになります：
    - `model`: 使用する画像生成モデルを指定します。`dall-e-2`や`dall-e-3`などがあります。
    - `prompt`: 生成したい画像を文字で表したもの。`dall-e-2`では最大1000文字、`dall-e-3`では最大4000文字まで可能です。
    - `n`: 生成する画像の数です。1から10の間で指定できます。ただし`dall-e-3`では`n=1`のみサポートされています。
    - `quality`: 生成する画像の品質を指定します。`standard`と`hd`(高解像度で細部まで緻密な画像を生成)があります。このパラメータは`dall-e-3`でのみ使用可能です。
    - `response_format`: 生成された画像の返却形式です。`url`では画像へのURL、`b64_json`ではBase64エンコードされたJSON形式で返されます。
    - `size`: 生成される画像のサイズを指定します。`dall-e-2`では`256x256`、`512x512`、`1024x1024`が選べ、`dall-e-3`では`1024x1024`、`1792x1024`、`1024x1792`から選べます。
    - `style`: 生成する画像のスタイルです。`vivid`はよりリアルでドラマチックな画像を生成し、`natural`は自然でリアルすぎない画像を生成します。このパラメータは`dall-e-3`でのみサポートされています。
    - `user`: エンドユーザーを識別するユニークなIDです。これによってOpenAIは利用状況の監視や不正利用の検出を行うことができます。詳細は[こちら](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids)を参照ください。

    また、画像生成に失敗した場合は、エラーメッセージを含むオブジェクトを返します。

4. 次に、サーバーを起動して、動作確認を行います。
   サーバーを起動して、画像生成ができるか確認してみましょう。
   ```powershell
    node start
    ```
   サーバーが起動したら、http://localhost:3000/ にアクセスしてみましょう。
   Server is Running!と表示されていれば成功です。

5. 最後に、クライアントを起動して、動作確認を行います。
   index.htmlを開いてプロンプトを入力して`Generate`ボタンを押します。

   ![スクリーンショット 2023-11-14 165829.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538434/c3a55870-5000-27e1-b7cf-e52e9bf00a6f.png)

   これで少し待つと

   ![宇宙服を着た猫.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538434/41fa265b-5af9-7e89-ae84-067198da84a0.png)

   :::note info
   画像生成には少し時間がかかります。また、画像生成に失敗することもあります。
   :::

## 🖥️ **Complete Code Overview: 今回のコード全体**
`index.html`ファイル
```html
<!DOCTYPE html>
<html lang="jp">
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="style.css" />
    <title>DALL-E 3 Tutorial</title>
  </head>
  <body>

    <h1>DALL-E 3 Tutorial</h1>

    <form>
      <input type="text" placeholder="Enter a prompt" />
      <button type="submit">Generate</button>
    </form>

    <div id="image-container"></div>

    <script src="script.js"></script>

  </body>
</html>
```

`style.css`ファイル
```css
body {
  font-family: sans-serif;
}
```

`script.js`ファイル
```javascript
const form = document.querySelector('form');
const input = document.querySelector('input');
const imageContainer = document.getElementById('image-container');

form.addEventListener('submit', (event) => {
  event.preventDefault();
  const prompt = input.value;
  input.value = '';
  generateImage(prompt);
});

async function generateImage(prompt) {
  try {
    const response = await fetch('http://localhost:3000/generate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ prompt: prompt })
    });

    if (!response.ok) {
      throw new Error(errorMessage);
    }

    const data = await response.json();
    if (data.image) {
      const imageElement = document.createElement('img');
      imageElement.src = data.image;
      imageContainer.appendChild(imageElement);
    } else {
      throw new Error('画像URLがレスポンスに含まれていません。');
    }

  } catch (error) {
    console.error('画像の生成に失敗しました。', error);
  }
}
```

`server.js`ファイル
```javascript
const OpenAI = require('openai');
const cors = require('cors');
const express = require('express');

// デフォルトでOpenAI APIキーは環境変数から自動取得されます
const openai = new OpenAI();
const app = express();
const port = 3000;

app.use(cors());
app.use(express.json());

app.get('/', (req, res) => {
  res.send('Server is Running!');
});

app.post('/generate', async (req, res) => {
  try {
    const prompt = req.body.prompt;
    const imageResponse = await openai.images.generate({
      model: "dall-e-3",
      prompt: prompt,
      n: 1,
      size: "1024x1024",
    });

    const imageUrl = imageResponse.data[0].url;
    if (!imageUrl) {
      return res.status(500).send({ error: '画像の生成に失敗しました。' });
    }

    // 成功した場合、画像URLを含むオブジェクトを返す
    res.status(200).json({ image: imageUrl });
  } catch (error) {
    res.status(500).send({
      error: '画像の生成に失敗しました。',
      details: error.message
    });
  }
});

app.listen(port, () => console.log('Listening on port', port));
```

以下はGitHubのリポジトリです。

https://github.com/yuyuyu2118/dalle3-tutorial-dalle3


## 📝 **Conclusion: まとめ**
お疲れ様でした！以上で、OpenAIの画像生成APIチュートリアルの第二回目は終了です。
この記事では、画像生成(DALL-E 3)の実装を行いました。
次回は、画像分析(gpt-4-vision)の実装を行います。