Azure FunctionsのBlob Input Bindingを用いたCognitive Serviceとの連携サンプル

※本記事は2019年11月時点での情報です。

#はじめに
本記事では、Azure Functions(v2,C#)のBlob Input Bindingの使用法をCognitive Serviceの利用を例として取り上げて紹介します。Blob Input Bindingの設定方法に関する記事が全く見つからなかったため、ドキュメントとして残しておくことにしました。今回はAzureポータルでの設定方法をご紹介します。

#事前準備
**・任意のCognitive Serviceのデプロイ**
Cognitive Serviceをデプロイし、エンドポイントとキーをコピーしておきます。今回のサンプルでは、Face APIのEmotionを利用します。

**・ストレージアカウントのデプロイ**
ストレージアカウントをデプロイします。Functionsから呼び出すためには、「汎用」SKUを選ぶ必要があります。作成後、Blob Storageに任意の名前のコンテナを作成し、人物の顔が載っている写真を何枚かアップロードしてください。

**・Functionsのデプロイ**
Azure Functionsをデプロイします。今回のサンプルでは、.NET CORE(C#)を使用します。

#Functionsの設定方法
本記事では、Azureポータルを使用した際の設定方法をご紹介します。

**1.Blob Input Bindingの設定**
Azure Functionsのデプロイ後、HTTP Triggerで任意の関数を作成します。作成後、「統合」タブをクリックすると、トリガーとバインディングを管理するための画面に遷移します。ここでは初めに、「入力」の設定をします。モジュールのインストールなどが完了したら、先ほど画像をアップロードしたストレージアカウントに接続をしてください。接続完了後、パスの設定を行います。パスは、コンテナ名/{name}としてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267543/8f0e54fd-4e50-822f-ecf1-27ce92113f5e.png)

**2.Suffixの設定**
Blob Storage内の画像ファイル名をパラメータとして受け取るために、APIのSuffixを設定します。統合タブから「トリガー」の設定画面に移り、「ルートテンプレート」を設定してください。このサンプルでは、FaceEmotion/picture={name}という形で設定を行いました。{name}には、クエリパラメータとして渡された画像ファイル名が入ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267543/061f8d9d-880c-0b97-f0c2-0a2cfd2af8d2.png)

**3.function.jsonの確認**
関数名をクリックし、ファイルの一覧からfunction.jsonを開いてください。このファイルには、トリガーやバインディングの設定情報が書かれています。Azureポータルからの設定では、先ほど設定内容がfunction.jsonに反映されないケースが稀にあるため、希望している設定がきちんと書かれているか、確認する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267543/ada438b5-464a-e01a-4d56-71af8df13777.png)

**4.ソースコードの作成**
Blob Input Bindingを使って取ってきた画像ファイルをFace APIに投げるサンプルを作成しました。Cognitive Serviceでは、BlobのPathをBodyとして登録しなければなりません。Pathを取得するために、今回はCloudBlockBlobクラスのUriプロパティを利用しました。

参照1: https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-storage-blob?tabs=csharp-script#input---usage
参照2: https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.storage.blob.cloudblockblob?view=azure-dotnet-legacy

```run.csx
#r "Newtonsoft.Json"
#r "Microsoft.WindowsAzure.Storage"

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Microsoft.WindowsAzure.Storage.Blob;

public static async Task<IActionResult> Run(HttpRequest req, CloudBlockBlob inputBlob, ILogger log)
{
    var client = new HttpClient();
    var queryString = HttpUtility.ParseQueryString(string.Empty);

    // Request headers
    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "<YOUR_SUBSCRIPTION_KEY>");
    queryString["returnFaceAttributes"] = "emotion";

    var uri = "https://<YOUR_FACE_API_SERVICE_NAME>.cognitiveservices.azure.com/face/v1.0/detect?" + queryString;
    
    HttpResponseMessage response;

    // Request body
    string body = "{\"url\": " + $"\" {inputBlob.Uri} \"" + "}";

    log.LogInformation($"C# Blob trigger function Processed blob\nURI:{body}");

    byte[] byteData = Encoding.UTF8.GetBytes(body);

    using (var content = new ByteArrayContent(byteData))
    {
        content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
        response = await client.PostAsync(uri, content);
    }

    string contentString = await response.Content.ReadAsStringAsync();
    
    return (ActionResult)new OkObjectResult($"{contentString}");
}
```

**5.テスト**
クエリパラメータとしてBlob Storageにアップロードした画像ファイル名を指定して、実験してみましょう。Face APIによって顔が検出されれば、顔の位置情報や感情スコアがJSON形式で表示されます。
※200OKでJSON配列が空の場合には、顔を検出できていないということになります。横顔などは検出されないため、お気を付けください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267543/35934523-8504-0c05-19f2-b86b8a20ba9c.png)

#さいごに
ソースコードはGitHubにも置いておきました。プルリクなどがありましたら、是非ともお願いいたします。

https://github.com/NoriMin/AzureFunctionsInputBindingSample-FaceAPI