🎨
A-Frame内でOpen AI APIの画像を使う方法
概要
- 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は下記の記事を参考に取得しておきます
A-Frame内からOpenAI APIを叩く
まずOpenAIのサンプルコードを参考にスクリプトを作ってみます
API_KEY
の部分に取得したAPI Keyを当てはめます。
APIのドキュメントはこちらになります
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