[2024.03.12更新！]Azure OpenAIで画像生成AI「DALL-E」がAPIで実行できるようになったので試す

# 2024.03.12Update!!
[SNSで仲良くさせていただいている方から、質問でこの記事の情報が`DALL E3`には対応していないことがわかりました。](https://twitter.com/dai_keiba_73/status/1766790535879299284)


https://powerdaaps.hatenablog.com/entry/2024/03/10/203618

せっかくなので`DALL-E 3`のフローを追記したいと思います。

:::note warn
2024.03.12時点の実証情報です
:::

## ざっくり手順
1. `DALL-E 3`に対応したリージョンでモデルをデプロイ（手順は省きます）
1. Power Automateの設定

## `DALL-E 3`に対応している地域（2024.03.12時点）
- **SwedenCentral リージョンに作成された Azure OpenAI リソース**

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/dall-e-quickstart?tabs=dalle3%2Ccommand-line&pivots=rest-api

`DALL-E 3`の場合、APIを利用した際の戻り値が異なります。
以前の私の記事で進めると、ここが罠になりますのでご注意ください。

# シンプルなPower Automateの例
1. Power Appsから`Prompt`を送信
1. `作成`アクションでエンドポイントを設定
    1. `Secure Inputs`をOn
1. `作成`アクションでAPIキーを設定
    1. `Secure Inputs`をOn
1. Azure OpenAIにHTTP要求を送信
1. JSONの解析
1. Power Appsに、生成された画像のURLを渡す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/f2e71202-ede6-3e86-8798-6239c253c8c6.png)

## DALL-E 2との違い
APIの戻り値のみ異なります。HTTP 詳細は割愛。
**`DALL-E 2`は一度要求を送信し、画像が生成されるまでステータスを確認し、作成完了まで待機する必要がありましたが、`DALL-E 3` は不要になります！**

上記が起因し、フローも非常にシンプルです。

スキーマはこちら！
```json:Shema.json
{
    "type": "object",
    "properties": {
        "created": {
            "type": "integer"
        },
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "content_filter_results": {
                        "type": "object",
                        "properties": {
                            "hate": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "self_harm": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "sexual": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "violence": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    },
                    "prompt_filter_results": {
                        "type": "object",
                        "properties": {
                            "hate": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "profanity": {
                                "type": "object",
                                "properties": {
                                    "detected": {
                                        "type": "boolean"
                                    },
                                    "filtered": {
                                        "type": "boolean"
                                    }
                                }
                            },
                            "self_harm": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "sexual": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "violence": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    },
                    "revised_prompt": {
                        "type": "string"
                    },
                    "url": {
                        "type": "string"
                    }
                },
                "required": [
                    "content_filter_results",
                    "prompt_filter_results",
                    "revised_prompt",
                    "url"
                ]
            }
        }
    }
}
```

Power Appsに生成された画像のURLを返す場合、戻り値が原則Arrayになっていますので、
一枚物を想定し、下記の通り実証では設定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/ddee0db0-ea25-9031-2903-90227520829a.png)

```
outputs('JSON_の解析')?['body']?['data'][0]?['url']
```

上記をイメージコントロールに設定すれば、Power Appsでも表示されます。
**是非試してみてください！**

:::note info
2024.03.12更新分はここまで！
:::

# ■ 以前の記事はこちらから！

:::note warn
以下の記事は DALL E2に対応
:::

2023.09.05に発表されたようですが、**画像生成AI「DALL-E」がAPIで実行できるようになりました。**

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/dall-e-quickstart?tabs=command-line&pivots=programming-language-studio#prerequisites

:::note warn
2023.09.05時点では**プレビュー機能**です
:::

さっそくなので[公式レファレンス](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/dall-e-quickstart?tabs=command-line&pivots=programming-language-studio#prerequisites)を読み解いて、**Power Automate**で実装したいと思います。

# 1. Azure OpenAIをデプロイ
まず最初に**Azure OpenAIをAzureでデプロイする必要**があります。
対象地域は現在`eastus`のみ（**重要**）

ここらへんは割愛して、**デプロイ後から解説します。**

## まずはKeyとEndPointを取得する
デプロイが完結したら、まずは`Key`と`EndPoint`を控えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/8c3b0e56-87d4-f905-ebac-3d93430b6212.png)

:::note warn
情報漏れを起こさないように注意しましょう
:::

# 2. レファレンスからAPIの呼び出し方を読み解く
さて、[公式レファレンス](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/dall-e-quickstart?tabs=command-line&pivots=programming-language-studio#prerequisites)を読み解いていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/a65d18b1-fcd5-d447-397f-636ef83b5ca7.png)

`Python`のサンプルコードがあるので、引用・転記します。

```python
import requests
import time
import os

api_base = '<your_endpoint>'  # Enter your endpoint here
api_key = '<your_key>'        # Enter your API key here

# Assign the API version (DALL-E is currently supported for the 2023-06-01-preview API version only)
api_version = '2023-06-01-preview'

# Define the prompt for the image generation
url = f"{api_base}openai/images/generations:submit?api-version={api_version}"
headers= { "api-key": api_key, "Content-Type": "application/json" }
body = {
    "prompt": "a multi-colored umbrella on the beach, disposable camera",  # Enter your prompt text here
    "size": "1024x1024",
    "n": 1
}
submission = requests.post(url, headers=headers, json=body)

# Call the API to generate the image and retrieve the response
operation_location = submission.headers['operation-location']
status = ""
while (status != "succeeded"):
    time.sleep(1)
    response = requests.get(operation_location, headers=headers)
    status = response.json()['status']
image_url = response.json()['result']['data'][0]['url']

print(image_url)
```

ここから読み解ける手順としては下記の通り。
1. Azure OpenAIのEndPointを組み合わせたURLにPOSTメソッドでAPIをたたく
1. 結果となる画像データは即座には出てこないので、`opelation_location`に対して`GET`メソッドを実行し、完了するまで繰り返す。（完了していない場合は待機する）

というイメージになります。

# 3. Power Automateに書き換えてみる
`Power Apps`に実装する前提でフローを作ってみます。
エラー処理を考えない、**サンプルコードを起こしたフローがコチラ！**

### ■ 全体図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/e6ecf494-1354-8db3-0293-611d05f4bd09.png)

### ■ Do Untilの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/4b5f1b56-83a7-45c4-0d8c-db4029451580.png)

#### 1. Power AppsからPromptを受け取る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/97c57f24-4aa8-f08a-e103-dece64dddca3.png)

**まずは画像生成AIの肝となるテキストを取得します。**
画像の出力する枚数やサイズ感も指定できますが、一旦例として挙げるのは`prompt`のみ引数とする方法とします。

#### 2. API Keyを変数に格納する
情報漏れにお気をつけください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/ab2ef2bb-c123-6e0d-fd74-c5318f42ed58.png)

#### 3. 画像生成AI「DALL-E」を実行する
**HTTP**要求を送信します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/8278b682-c2b7-48cb-b743-9c0d960a8ac3.png)

`方法`は**POST**。

**肝心なところはURLですね。**

```plaintext
{EndPoint}openai/images/generations:submit?api-version=2023-06-01-preview
```

**{EndPoint}は、Azureの`Key and Endpoint`で控えたエンドポイントに置き換えてください。**
この形で組み立てるURLが**画像生成AI**用のURLになります。

上記の値をそのままURLに全て入れて`クエリ`の部分は無くしても良いですし、?api~を削って、クエリに書く方法でも好みで構いません。

■ ヘッダー
|Key|Value
|---|---
|api-key|控えたKey
|Content-Type|application/json

■ クエリ（URLから?api~を省く場合
|Key|Value
|---|---
|api-varsion|2023-06-01-preview

■ 本文
```json:body
{
  "prompt": "@{triggerBody()['text']}",
  "size": "1024x1024",
  "n": 1
}
```
Keyに対する値については、レファレンスで見つけきれないため、断定は避けます。
試しながら値を調整してみてください。

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/models#dall-e-preview

## 画像は即座に生成されない
ある種その通りですが、`prompt`を渡し、画像が生成されるまで`一定の時間がかかります`。
そして結果を取得するために、今回`POST`した要求から、`operation_location`という値を控えておく必要があります。

この値はヘッダーに含まれる値です。

#### 4. 結果を取得するためのURLを変数に格納する

```plaintext:operation-location
@{outputs('HTTP　DALL-Eの開始')['headers']?['operation-location']}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/f065de6e-94ac-05d9-91c7-5c80de77c35a.png)

#### 5. Do Untilの状態を表す変数を宣言する

そして、後続の`Do Until`の状態を判定する変数を先に宣言しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/3ea1dfbc-c290-2a40-86bf-8a20b38eb721.png)

#### 6. Do Until　画像が生成されるまで待機と処理を繰り返す
`operation-location`に繰り返し、HTTP要求を送信し、処理が完了するまで`GET`メソッドを繰り返します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/77ae0ca9-dcf8-d9b7-02e0-1f3023fa1992.png)

待機時間は別に1秒でなくてもいいと思います。1枚の画像に対して4秒で完了する印象です。

#### 6-1. 結果を取得する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/cecf5c7e-28c3-f485-4211-679a10676bf3.png)

`GET`メソッドで`operation-location`に要求を送信します。ヘッダーは前回と同様です。

こちらの戻り値（body）で
```plaintext
@{body('HTTP_Result')?['status']}
```
`status`の値が`succeeded`になった場合に、**処理が完了となります。**

#### 7. 結果を返す
```plaintext:結果の画像URL
@body('HTTP_Result')?['result']?['data'][0]?['url']
```
`result`の`data`に配列で画像のURLが返ってきます。こちらからデータを取得します。
今回は**Power Apps**で活用することを見込んで、一番最初の値を取得しています。

## 挙動のイメージ

![dall.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/9dc21600-3b6d-a2d5-a466-397a72617196.gif)

`Power Apps`のImageコントロールにURLを設定することで生成された画像を参照できます。
**生成AIとPower Appsのコラボ実現ですね！**

# 最後に
いかがでしたでしょうか！？本日レファレンスを見て、
**うわ、来てるじゃん！** と思ってビックリしました！
進化が早いですね！**Function Calling**と合わせるとさらに面白そうです。

**良いPower Lifeを! Good night!**