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

  • 0
    いいね
  • 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 のプロジェクトの作成

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

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

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

    Xamarin のプロジェクトが作成

    Nuget Package

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

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

    PCL Storage のインストール

    PCL は Xamarin でファイルの読み書きを行うために必要なものです。
    (Xamarin の名前空間に System.IO.File がないため、その代わりにとなるもの)
    PCLStorage は、 Xamarin Plugin と呼ばれるもので、このライブラリを使用すれば、簡単にカメラとか位置情報とかを使うことが出来ます。

    Xamarin Camera plugin をインストール

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

    コーディング

    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;
    using XamEmotion;
    ---
    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 System.Threading.Tasks;
    using Plugin.Media;
    ---
    public static class galleryClient
        {
            public static async Task<string> PickPhoto()
            {
                // 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 版を実行してください。環境のせいでうごかない。といったことが少なくなります。

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

    ソース公開

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

    Github

    追加課題

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