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

  • 5
    いいね
  • 0
    コメント

Microsoft Cognitive Services を使った表情分析 Web アプリ ( Xamarin.Forms 版)

Microsoft Cognitive Services とは?

Microsoft Cognitive Services とは、テキスト、画像解析などの自前で実装すると相当大変な機能を API を叩くだけで利用できるサービスです。
今回は、 そのなかの Emotion API を利用していきます。

Emotion API とは?

画像を送り込むだけで、顔を分析し、数値化した Json データを取得することが出来ます。

Xamarin とは?

C# を使い、 iOS, Android, Windows のアプリを作ることが出来る開発ツールです。
Xamarin には、Xamarin.Tradional, Xamarin.Forms の2種類の開発手法があります。今回は、 Xamarin.Forms の方を使っていきます。

詳しくは、日本Microsoft テクニカルエバンジェリスト 千代田まどか 氏の資料をご覧になってください。

Xamarin Overview

今回作るもの

今回は、 Xamarin を使い、 スマホから写真を撮影し、顔の表示位置と表情分析スコアを表示するスマホ用アプリを作成していきます。
Xamarin のプロジェクトテンプレートをベースに作成していきます。

完成図

Android の方は、Google Play からダウンロードしてみてください。

Xamarin & Cognitive Services の利用に必要な環境セットアップ

Visual Studio

Xamarin の開発を行うためには、 Visual Studio 2017(or Visual Studio for Mac) が必要です。
また、 Visual Studio 2017 のセットアップ時に .net によるモバイル開発 を選択している必要があります。

Microsoft Account, Microsoft Azure Account

Microsoft Cognitive Services を利用するために必要です。

詳しくは、 日本Microsoft テクニカルエバンジェリスト 大森彩子 氏の Qiita をご覧ください

また、 Emotion API の keyをリンク先に書いてある通りに取得してください!

Xamarin による実装

Visual Studio 2017 による Xamarin.Forms のプロジェクトの作成

Visual Studio のテンプレートから Xamarin.Forms のプロジェクトを作成
※ Visual Studio を起動し、上部のメニューバーから ファイル>新規作成>プロジェクト を選択します。

作成する Xamarin.Forms プロジェクトのタイプを選択

作成する UWP のバージョンはそのままでOKボタンをクリックしてください

Xamarin.Forms のプロジェクトが作成

Nuget Package

BCL Build パッケージのインストール

Emotion API 用クライアントのインストール

ソリューションのパッケージの管理ウィンドウの参照タブで「Emotion」や「Microsoft.Project.Oxford.Emotion」と検索してインストールします。

PCL Storage のインストール

PCL Storage は Xamarin.Forms でファイルの読み書きをする処理を共通コードで簡潔に記述するため行うために使用するライブラリです。
(Xamarin.Forms の PCL プロジェクトの名前空間に System.IO.File がないため、その代わりにとなるもの)
PCL Storage は、 Xamarin Plugin と呼ばれる公式ライブラリのうちの1つで、これらの Plugin ライブラリを使用すれば、簡単にカメラとか位置情報とかを使うことが出来ます。

ソリューションのパッケージの管理ウィンドウの参照タブで「PCLStorage」と検索してインストールします。

Media Plugin for Xamarin and Windows をインストール

カメラ・ギャラリーから画像をピックアップするために使います。

ソリューションのパッケージの管理ウィンドウの参照タブで「Xam.Plugin.Media」と検索してインストールします。

コーディング

Viewの編集

before

MainPage.Xaml
    <Label Text="Welcome to Xamarin Forms!" 
           VerticalOptions="Center" 
           HorizontalOptions="Center" />

After

MainPage.Xaml
 <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Image x:Name="ImagePreview" Grid.Row="0"/>
        <Button Text="解析" Grid.Row="1" Clicked="Button_OnClicked"/>
        <StackLayout HorizontalOptions="Center" Grid.Row="2">
            <Label x:Name="Anger">怒り</Label>
            <Label x:Name="Contempt">軽蔑</Label>
            <Label x:Name="Disgust">むかつき</Label>
            <Label x:Name="Fear">恐れ</Label>
            <Label x:Name="Happiness">喜び</Label>
            <Label x:Name="Neutral">無表情</Label>
            <Label x:Name="Sadness">悲しみ</Label>
            <Label x:Name="Surprise">驚き</Label>
        </StackLayout>
    </Grid>

Before

MainPage.Xaml.cs
public MainPage()
        {
            InitializeComponent();
        }

After

MainPage.Xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ProjectOxford.Common.Contract;
using Xamarin.Forms;
---
public MainPage()
        {
            InitializeComponent();
        }

        private async void Button_OnClicked(object sender, EventArgs e)
        {
            var photoUrl = "";

            var imageChoiceResult = await DisplayAlert("画像の元を選択してください", "", "カメラ", "ギャラリー");

            try
            {
                if (imageChoiceResult)
                {
                    // 写真を撮影し、保存したURLを取得
                    photoUrl = await PhotoClient.TakePhoto();
                }
                else
                {
                    // 写真を撮影し、保存したURLを取得
                    photoUrl = await galleryClient.PickPhoto();

                }
            }
            catch (Exception exception)
            {
                await DisplayAlert("Error", exception.Message, "OK");
            }

            // 撮影した画像を表示
            ImagePreview.Source = photoUrl;

            EmotionScores emotionResult;
            try
            {
                // Cognitive Services - Emotion API を叩く
                emotionResult = await EmotionAPIClient.AnalyzeAsync(photoUrl);
            }
            catch (Exception exception)
            {
                await DisplayAlert("Error", exception.Message, "OK");
                return;
            }

            // 表示するためにビューにセットしていく
            Anger.Text = "怒り: " + Math.Round(emotionResult.Anger, 6).ToString("0.000000");
            Contempt.Text = "軽蔑: " + Math.Round(emotionResult.Contempt, 6).ToString("0.000000");
            Disgust.Text = "むかつき: " + Math.Round(emotionResult.Disgust, 6).ToString("0.000000");
            Fear.Text = "恐れ: " + Math.Round(emotionResult.Fear, 6).ToString("0.000000");
            Happiness.Text = "喜び: " + Math.Round(emotionResult.Happiness, 6).ToString("0.000000");
            Neutral.Text = "無表情: " + Math.Round(emotionResult.Neutral, 6).ToString("0.000000");
            Sadness.Text = "悲しみ: " + Math.Round(emotionResult.Sadness, 6).ToString("0.000000");
            Surprise.Text = "驚き: " + Math.Round(emotionResult.Surprise, 6).ToString("0.000000");
        }

カメラを撮影するクラスの作成

プロジェクトを右クリックして、 新しいクラスを作成してください

PhotoClient という名前のクラスを作成

Before

PhotoClient.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
---

class PhotoClient
    {
    }

After

PhotoClient.cs
using System;
using System.Threading.Tasks;
using Plugin.Media;
using Plugin.Media.Abstractions;
---
public static class PhotoClient
    {
        public static async Task<string> TakePhoto()
        {
            // カメラを初期化
            await CrossMedia.Current.Initialize();

            // カメラを使えるかどうか判定
            if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
            {
                throw new NotSupportedException("You should Set up camera");
            }

            // 撮影し、保存したファイルを取得
            var photo = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions());

            // 保存したファイルのパスを取得
            return photo.Path;
        }
    }

galleryから画像を選択するクラスを作成

上と同じように galleryClient という名前でクラスを新規作成してください。

Before:galleryClient.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
---
class galleryClient
    {
    }

After:galleryClient.cs

using Plugin.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
---
public static class galleryClient
    {
        public static async Task<string> PickPhotoA()
        {
            // galleryから写真を選択させる
            var photo = await CrossMedia.Current.PickPhotoAsync();

            // 保存したファイルのパスを取得
            return photo.Path;
        }
    }

Emotion API を叩くクラスを作成

上と同じように EmotionClient という名前で、新規クラスを作成してください。

Before

EmotionClient.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
---
class EmotionClient
    {
    }

After

EmotionClient.cs
using System.Threading.Tasks;
using Microsoft.ProjectOxford.Common.Contract;
using Microsoft.ProjectOxford.Emotion;
using PCLStorage;
---
public static class EmotionAPIClient
    {
        // ここに Emotion API のsubscribeキーを入力してください
        private static readonly string SubscribeKey = "YOUR_Subscribe_KEY";

        public static async Task<EmotionScores> AnalyzeAsync(string PhotoURL)
        {
            var client = new EmotionServiceClient(SubscribeKey);
            var file = await FileSystem.Current.GetFileFromPathAsync(PhotoURL);
            var ImageStream = await file.OpenAsync(FileAccess.Read);
            var result = await client.RecognizeAsync(ImageStream);
            return result[0].Scores;
        }
    }

アプリケーションの動作確認

F5 または デバック>デバックの開始 をクリックして、プロジェクトのビルドおよび起動を行います。

Windows 環境で開発をしている人は UWP 版を実行してください。環境のせいで動かない。といったことが少なくなります。

無事に完成させることは出来ましたか?

動かないときに見る場所

iOS

プライバシーポリシーに対応

  1. Info.plist を開いてください。

右クリックメニューから ファイルを開くアプリケーションの選択をクリックしてください

image.png

XML (テキスト) エディタを選択し開いてください

image.png

  1. 以下のテキストを入力してください

Before

 <key>UILaunchStoryboardName</key>
      <string>LaunchScreen</string>

After

<key>UILaunchStoryboardName</key>
  <string>LaunchScreen</string>
<key>NSPhotoLibraryUsageDescription</key>
  <string>This app accesses the photo library to analyze facial expressions.</string>
<key>NSCameraUsageDescription</key>
  <string>This app accesses the camera to analyze facial expressions.</string>

Android

https://github.com/jamesmontemagno/MediaPlugin
の以下の部分を参考に実装してください。

Important Permission Information
Please read these as they must be implemented for all platforms.
Android

UWP アプリ

アプリマニフェスト>機能>Webカメラのところにチェックを入れ、UWPアプリから Web カメラを使うと宣言してください。
(ローカルでデバッグ実行する分には問題がないはずです)
image.png

ソース公開

今回作成したものの完成品を Github にて公開しています。もしうごかなければ、参考にしてみてください!

Github

追加課題

  • 複数人の表情を見れるようにしよう
  • MVVM の形に書きなおしてみよう
  • 撮影・選択した画像の顔の部分に枠を描画してみよう
  • Xamarin.Forms から Xamarin.Tradional へ書き直してみよう