🎨

A-Frame内でOpen AI APIの画像を使う方法

2023/04/05に公開

概要

  • WebGLフレームワークであるA-Frame内でOpen AI APIから生成された画像を使う方法について解説します
  • 2023年3月に8th Wall March Challenge: Sky Effectsが行われ、Sky EffectsにAIで生成した画像を使うという作品を制作しました。その過程で得られたノウハウになります。
     - (流行りだったのもあり同一のアイディア作品が多数ありましたw)
  • 試行錯誤の過程も載せているので、最終的なコードが欲しい方は記事の最後をご覧ください

準備

今回はA-FrameをGlitchで動かしました
OpenAI APIのAPI Keyは下記の記事を参考に取得しておきます
https://auto-worker.com/blog/?p=6988

A-Frame内からOpenAI APIを叩く

まずOpenAIのサンプルコードを参考にスクリプトを作ってみます
API_KEYの部分に取得したAPI Keyを当てはめます。
APIのドキュメントはこちらになります
https://platform.openai.com/docs/api-reference/images/create
promptに生成したい画像のプロンプトを入力します。日本語でも指定が可能です

<html>
  <head>
    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
    <script>
    AFRAME.registerComponent('ai-gen', {
        init(){
          const endpointUrl = "https://api.openai.com/v1/images/generations";
          const apiKey = "API_KEY";
          
          console.log("request start");

          // リクエストボディの作成
          const requestData = {
            "model": "image-alpha-001",
            "prompt": `white cat`,
            "num_images": 1,
            "size": "512x512",
          };

          // fetch APIを使用してOpenAI APIにリクエストを送信
          fetch(endpointUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              'Authorization': `Bearer ${apiKey}`
            },
            body: JSON.stringify(requestData)
          })
          .then(response => {
            // レスポンスが正常に取得できた場合
            if (response.ok) {
              return response.json();
            }
            // レスポンスがエラーの場合
            else {
              throw new Error('Network response was not ok');
            }
          })
          .then(responseData => {
	    //結果をログ出力
            console.log(JSON.stringify(responseData));
          })
          .catch(error => {
            console.error('Error:', error);
          });
        }
      });
    </script>
  </head>
  <body>
    <a-scene>
      <a-box position="-1 0.5 -3" rotation="0 45 0" ai-gen></a-box> <!--このBOXに画像を貼り付ける -->
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>
  </body>
</html>

こんな感じのレスポンスが返ってきます
urlをブラウザで開くと画像が表示されます

{"created":1680674684,
 "data":[
  {"url":"https://oaidalleapiprodscus.blob.core.windows.net/private/org-vzM73EerIZKGMKVbzmSTpVzp/user-pDnnu3AcqC7cfWCZhWAVfARb/img-...."}]}

このURLをそのままmaterialに突っ込めば使えるのかな?と考え、このように書き換えます

console.log(JSON.stringify(responseData));
+ this.el.setAttribute('material','src', responseData['data'][0]['url']);

するとCORS(Cross-Origin Resource Sharing)のエラーで怒られてしまいます

Access to XMLHttpRequest at 'https://oaidalleapiprodscus.blob.core.windows.net/private/org-vzM73EerIZKGMKVbzmSTpVzp/user-pDnnu3AcqC7cfWCZhWAVfARb/img-' from origin 'https://ripe-maddening-amaryllis.glitch.me' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

CORSを回避する

OpenAIのサーバ上にある画像に直接アクセスするのがダメなので、base64形式で取得しmaterialに適用します

OpenAI APIを叩く際にオプションを追加するとbase64形式でデータを渡してもらえます

// リクエストボディの作成
  const requestData = {
    "model": "image-alpha-001",
    "prompt": `white cat`,
    "num_images": 1,
    "size": "512x512",
+   "response_format": "b64_json"
  };

取得したbase64の画像データは下記のように変換してからmaterialに適用します

console.log(JSON.stringify(responseData));
+ const image = responseData['data'][0]['b64_json']
+ const imageData = "data:image/png;base64,"+image;
+ this.el.setAttribute('material','src', 'url(' + imageData + ')');

これを実行するとAIで生成した画像をBOXに貼り付けることができました

最終的なコードです

<html>
  <head>
    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
    <script>
    AFRAME.registerComponent('ai-gen', {
        init(){
          
          const endpointUrl = "https://api.openai.com/v1/images/generations";
          const apiKey = "API_KEY";

          // リクエストボディの作成
          const requestData = {
            "model": "image-alpha-001",
            "prompt": `white cat`,
            "num_images": 1,
            "size": "512x512",
            "response_format": "b64_json"
          };

          // fetch APIを使用してOpenAI APIにリクエストを送信
          fetch(endpointUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              'Authorization': `Bearer ${apiKey}`
            },
            body: JSON.stringify(requestData)
          })
          .then(response => {
            // レスポンスが正常に取得できた場合
            if (response.ok) {
              return response.json();
            }
            // レスポンスがエラーの場合
            else {
              throw new Error('Network response was not ok');
            }
          })
          .then(responseData => {
            console.log(JSON.stringify(responseData));
            const image = responseData['data'][0]['b64_json']
            const imageData = "data:image/png;base64,"+image;
            this.el.setAttribute('material','src', 'url(' + imageData + ')');
          })
          .catch(error => {
            console.error('Error:', error);
          });
          
        }
      });
    </script>
  </head>
  <body>
    <a-scene>
      <a-box position="-1 0.5 -3" rotation="0 45 0" ai-gen></a-box> <!-- このBOXに画像を張り付ける -->
      <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
      <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>
  </body>
</html>

Discussion