人工知能パーツ Microsoft Cognitive Services を使った表情分析アプリを作ろう！ (Emotion API × .NET Core 編)

#Microsoft Cognitive Services を使った表情分析 Web アプリ (.NET Core 版)
Microsoft Cognitive Services は 画像、文章、言語、情報を処理する機能を API 経由で利用できるサービスです。
Cognitive Services の一つである Emotion API では、画像を分析して人間の顔やその表情を数値化し、結果を JSON 形式で取得することができます。

今回は オンライン画像 (URL) を入力すると、顔の表示位置と表情分析スコアを表示する Web アプリを作成します。ベースは Microsoft .NET Core (v1.1) の Web アプリケーションテンプレートを利用し、Cognitive Services Emotion API C# ライブラリーを用いて呼び出しを行います。
> Bot Framework を利用した BOT 作成方法は [Emotion API × Bot Framework 編](http://qiita.com/annie/items/c9a8b83234dd869912c9) をご覧ください。
JavaScript で Emotion API を呼び出す方法については、[Emotion API × JavaScript 編](http://qiita.com/annie/items/96b37ef94885510c1b6b) をご覧ください。

この Web アプリは Visual Studio からローカル環境で稼働確認することが可能です。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_01.png)

#.NET Core & Cognitive Services 利用に必要な環境、サブスクリプションの準備

##開発環境
###Visual Studio 2017
無償の Visual Studio 2017 Community でOKなので、既存の環境がない場合は、ダウンロードしてインストールします。
[Visual Studio 2017 Community ダウンロードサイト](https://www.visualstudio.com/ja/vs/community/)

##Microsoft アカウント
Cognitive Services (と必要に応じて Azure) サブスクリプションの申し込みに必要ですので、持っていない場合は取得しておきます。
>[Microsoft アカウント登録手続き](https://www.microsoft.com/ja-jp/msaccount/signup/default.aspx)

##Cognitive Services サブスクリプション
試用は無料ですが、こちらの手順でサブスクリプション申し込みが必要です。
>申し込み方法は [Microsoft Cognitive Services サブスクリプション申し込み編](http://qiita.com/annie/items/251d4b85c86aa9321665) をご覧ください。


#Web アプリの実装
##Visual Studio 2017 による .NET Core アプリケーションの作成と構成
###Visual Studio テンプレートから .NET Core アプリケーションの作成
Visual Studio を起動し、上部のメニューバーから ファイル>新規作成>プロジェクト を選択します。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_02.png)

*Visual Studio > Visual C# > .NET Core* をクリック、中央の列に表示される [ASP.NET Core Web アプリケーション (.NET Core)] をクリックします。ひとまず、(プロジェクトの)名前、(プロジェクトファイルが保存される)場所、ソリューション名 はデフォルトのままで右下の OK をクリックします。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_03.png)

ASP.NET Core テンプレートから [Web アプリケーション] を選択、[OK] をクリックすると、必要なコンポーネントが読み込まれ、プロジェクトが構成されます。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_04.png)

###Cognitive Services Emotion API の C# ライブラリーのインストール
ソリューションエクスプローラーでプロジェクト名 (ソリューションの下) を右クリックして、*NuGet パッケージの管理* を選択します。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_05.png)

*参照* をクリックし、*project oxford* と入力して検索します。*Microsoft.ProjectOxford.Emotion* を選択し、*インストール* をクリックしてインストールします。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_06.png)
>.NET Core のバージョンによって *Microsoft.ProjectOxford.Emotion* が対応していない場合は、*Microsoft.ProjectOxford.Emotion.DotNetCore* をご利用ください。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_06-01.png)

Emotion API のライブラリーと、依存関係のあるライブラリーが合わせて表示されますので、*OK* をクリックしてインストールします。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_07.png)
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_08.png)


インストールが終了したら、NuGet のウインドウを閉じます。

###不要なページの削除
*Views* フォルダー をクリックして開きます。*Views > Home* フォルダー 配下のファイルをすべて選択し、delete または *右クリック＞削除* をクリックして削除します。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_09.png)

*Views > Shared* フォルダー をクリックして開き、*_Layout.cshtml* をクリックして開きます。
36,37行目にある About, Contact のページへのリンクを削除します。この2行を削除する、またはコメントアウトします。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_15.png)
> このテンプレートは Home, About, Contact の3ページで構成されており、この部分でナビゲーションバーを設定しています。今回は Home のみ使用します。

###表示ページの作成
*Views > Home* フォルダー を右クリック、*追加 > 新しい項目* をクリックします。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_10.png)

*新しい項目の追加* 画面で、画面中央の項目から *MVC ビューページ* を選択、画面下部の名前が *Index.cshtml* になっているのを確認して、[追加] をクリックします。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_11.png)
同様の手順で *Result.cshtml* という名前で MVC ビューページを追加します。

適時 Crtl+S または ツールバーから *ファイル* ＞ *(ソリューション名)の保存* をクリックして保存しておきます。

##画像 URL を取得して結果を表示するページの作成
###画像 URL 取得ページ (Index.cshtml)
*Views > Home* フォルダー に作成した *Index.cshtml* をクリックして表示し、こちらを編集します。内容を一旦全部削除し、下記の内容に書き換えます。

```Index.cshtml
@{
    ViewData["Title"] = "Microsoft Cognitive Services - Emotion API Sample";
}

<div class="row">
    <div class="col-md-12">
        <h2>Microsoft Cognitive Services - Emotion API Sample</h2>
        <p>分析したい画像のURLを入力してください。</p>
        @using (Html.BeginForm(
                            "Result",
                            "Home",
                            null,
                            FormMethod.Post
                            ))
        {
            @Html.TextBox("url", "", new { size = 100 })
            <input type="submit" value="分析する" />
        }
    </div>
</div>
```
>画像 URL を Textbox で取得し、ボタンをクリックすると、Result.cshtml に遷移します。
>[変更前]
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_12.png)
[変更後]
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_13.png)

###結果表示ページ (Result.cshtml)
*Views > Home* フォルダー に作成した *Result.cshtml* をクリックして表示し、こちらも同様に内容を一旦全部削除し、下記の内容に書き換えます。

```Result.cshtml
@{
    ViewData["Title"] = "Microsoft Cognitive Services - Emotion API Sample";
}

<div class="row">
    <div class="col-md-12">
        <h2>Microsoft Cognitive Services - Emotion API Sample</h2>
        <p>分析したい画像のURLを入力してください。</p>
        @using (Html.BeginForm(
                            "Index",
                            "Home",
                            null,
                            FormMethod.Post
                            ))
        {
            @Html.TextBox("url", "", new { size = 100, disabled = "disabled" })
            <input type="submit" value="リセット" />
        }
    </div>
</div>
<div class="row">
    <h3>@ViewData["Result"]</h3>
    <div class="col-md-2">
        <table border="0" cellpadding="0">
            <tr>
                <td>怒り</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Anger"])</td>
            </tr>
            <tr>
                <td>軽蔑</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Contempt"])</td>
            </tr>
            <tr>
                <td>ムカつき</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Disgust"])</td>
            </tr>
            <tr>
                <td>恐れ</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Fear"])</td>
            </tr>
            <tr>
                <td>喜び</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Happiness"])</td>
            </tr>
            <tr>
                <td>無表情</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Neutral"])</td>
            </tr>
            <tr>
                <td>悲しみ</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Sadness"])</td>
            </tr>
            <tr>
                <td>驚き</td>
                <td>: </td>
                <td>@Html.Encode(ViewData["Surprise"])</td>
            </tr>
        </table>
    </div>
    <div class="col-md-10">
        <img src="@ViewData["Url"]" width="400" />
    </div>
</div>
```
>Result.cshtml も上の部分は Index.cshtml とほぼ同様です。Textbox を "disabled" に設定、ボタンをクリックすると、Index.cshtml に戻ります。
>[変更後]
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_14.png)



##Emotion API を呼び出すロジックの記述
*Index.schtml* でボタンが押され、*Result.schtml* に遷移する際に Emotion API を呼び出して結果を取得、整形する動作を追加します。

### HomeController.cs に Result() を追加
*Controllers* フォルダー をクリックして開きます。*HomeController.cs* をクリックして表示し、こちらを編集していきます。

冒頭に、先ほど追加した Microsoft.ProjectOxford.Emotion を追加し、デフォルトで記述されている About(), Contact() を削除します。

```HomeController.cs
using Microsoft.ProjectOxford.Emotion;
using Microsoft.ProjectOxford.Emotion.Contract;
```

![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_16.png)

Result ページが呼び出される際の動作を、Result() として追加します。フォームからの入力(URL)を取得します。合わせて Emotion API を利用するための Subscription Key も設定しておきます。

```HomeController.cs
[HttpPost]
public async Task<IActionResult> Result(string url)
{
    // 取得可否を設定
    var res = "";
    // フォームからの入力(URL)を取得
    ViewData["Url"] = url;

    // Emotion API の Subscription Key をセット
    // お持ちの Subscription Key を YOUR_SUBSCRIPTION_KEY 部分にコピーしてください
    const string eApiKey = "YOUR_SUBSCRIPTION_KEY";

    // Emotion API を Call する EmotionServiceClient を生成
    // Emotion API を呼び出して結果を取得
    // 呼び出し結果を元に表示内容を作成
    // 結果を表示
    // ※次以降の項目で作成します
}
```

![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_17.png)

### Emotion API の呼び出し
Subscription Key を使って EmotionServiceClient を生成、RecognizeAsync で呼び出す部分を記述します。

```HomeController.cs
[HttpPost]
public async Task<IActionResult> Result(string url)
{
     : (中略)
    // Emotion API を Call する EmotionServiceClient を生成
    var eClient = new EmotionServiceClient(eApiKey);
    // 結果を取得する配列を定義
    Emotion[] eResult = null;

    // Emotion API を呼び出して結果を取得
    try
    {
        eResult = await eClient.RecognizeAsync(url);
    }
        catch (Exception e)
    {
        eResult = null;
    }

    // 呼び出し結果を元に表示内容を作成
    // 結果を表示
    // ※次以降の項目で作成します
}
```

### 結果の取得と整形
結果を配列に取得し、小数点6桁に整形して ViewData にセットします。
呼び出しに失敗する場合 (サブスクリプションキー不適合など) は eResult == null で検出します。呼び出しに成功した場合でも、結果なし (人間の顔を検出できなかった場合など) は eResult の読み取り例外で検出しています。

```HomeController.cs
[HttpPost]
public async Task<IActionResult> Result(string url)
{
     : (中略)
    // 呼び出し結果を元に表示内容を作成
    if (eResult != null)
    {
        //表情スコアを取得
        try
        {
            res = "結果:";
            var eScores = eResult[0].Scores;

            // 検出された表情スコアをセット
            ViewData["Anger"] = Math.Round(eScores.Anger, 6).ToString("0.000000");
            ViewData["Contempt"] = Math.Round(eScores.Contempt, 6).ToString("0.000000");
            ViewData["Disgust"] = Math.Round(eScores.Disgust, 6).ToString("0.000000");
            ViewData["Fear"] = Math.Round(eScores.Fear, 6).ToString("0.000000");
            ViewData["Happiness"] = Math.Round(eScores.Happiness, 6).ToString("0.000000");
            ViewData["Neutral"] = Math.Round(eScores.Neutral, 6).ToString("0.000000");
            ViewData["Sadness"] = Math.Round(eScores.Sadness, 6).ToString("0.000000");
            ViewData["Surprise"] = Math.Round(eScores.Surprise, 6).ToString("0.000000");
        }
        catch (Exception e)
        {
            res = "検出できませんでした";
        }
    }
    else
    {
        res = "API の呼び出しに失敗しました";
    }

    // 結果を表示
    ViewData["Result"] = res;
    return View();
}
```


Crtl+S または ツールバーから *ファイル* ＞ *(ソリューション名)の保存* をクリックして *HomeController.cs* を保存します。


#アプリケーションの動作確認
F5 または デバック＞デバックの開始 をクリックして、プロジェクトのビルドおよび起動を行います。
ブラウザが起動したら、オンライン画像の URL (例えばこちら↓) をコピーして送信します。
https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_002.jpg

分析結果の回答が返信されれば、アプリケーションは完成です。
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_01.png)

> EmotionServiceClient の生成で失敗する場合は、NuGet *Microsoft.ProjectOxford.Emotion* を一旦削除し、*Microsoft.ProjectOxford.Emotion.DotNetCore* をインストールしてご利用ください。(ソースコードはそのままで大丈夫です)
![](https://msdnshared.blob.core.windows.net/media/2017/05/bluesky_20170521_06-01.png)

#Appendix
同じソースを Azure Web App で Web 公開しています。動作確認にご利用ください。
http://emotionweb-netcore.azurewebsites.net/

また、完成形のソースコードを GitHub にて公開しました。
https://github.com/a-n-n-i-e/CognitiveEmotionAPI-EmotionWeb-NETCore