こんにちは。エクセルソフトの田淵です。
本エントリーは Xamarin Advent Calendar 2018 - Qiita の 2日目のエントリーです。明日は @gnk263 さんです。よろしくお願いいたします。
さて、先日「初心者向けXamarinハンズオン! #5」で最新の Face API を使って画像の人の幸福度を測定するところまでをやりました。(実は大本の Xamarin Dev Days は 2016 年に開催したイベントなのですが、ちゃんとプロジェクトやドキュメントが定期的にメンテされています。凄いですよね!)
ドキュメントは以下に公開しているので、興味のある方はぜひやってみてください。
で!アドベントカレンダーのネタとして Azure の Cognitive Service でもざっと触りなおそうかなーと思っていたのですが、@tonkun_no さんが「久しぶりに何か書きます(Xamarin + Cognitive Servicesかなぁ)」と仰っていたのでそこはお譲りし、違うやつ、AWS の顔認識の PaaS を触ってみることにしました。
完成するとこんな感じです。
Amazon Rekognition とは
ということで前段が長くなってしまいましたが、【Amazon Rekognition】人工知能・機械学習に基づく画像認識・画像解析サービス | AWS です。
Amazon Rekognition では、画像分析と動画分析をアプリケーションに簡単に追加できます。Rekognition API に画像または動画を与えるだけで、このサービスが対象物、人、テキスト、シーン、アクティビティ、それに不適切なコンテンツまで検出します。
とあるように、
- 対象物体、シーン、アクティビティ検出(Computer Vision - 画像内のシーンおよびアクティビティ認識)
- 顔認識(Face - 画像内の顔検出)
- 顔分析(Face - 画像内の感情認識)
- 動線の検出(Video Indexer - ビデオ内のオブジェクト、シーン、アクティビティの検出(かな?))
- 安全でないコンテンツの検出(Content Moderator - 画像とビデオの性表現が露骨なコンテンツまたは不快感を与えるコンテンツのモデレート)
- 有名人の認識(Computer Vision - 画像内の有名人とランドマークの認識)
- 画像中のテキスト(Computer Vision - 画像内の光学式文字認識 (OCR)/手書き認識)
などが利用できます。(私は Microsoft Azure を良く使っているので、老婆心でカッコ内に Cognitive Services のディレクトリ | Microsoft Azure にある Azure Cognitive Services での同様のサービスを記載しましたw)
料金は こちら をご参照ください。
1年間の無料アカウントの方は「最初の 12 か月間は、1 か月あたり 5,000 枚の画像分析と、毎月最大 1,000 個の顔メタデータの保存が可能」で、一般アカウントでも、「1 か月あたり画像処理100 万枚までは処理画像 1,000 枚あたり 1.3 USD で顔メタデータのストレージは 1,000枚あたり 0.013 USD」です。
Xamarin からは .NET Standard 2.0 のライブラリで Rekognition にアクセスすれば良いでしょう。
早速やっていきます。
事前準備
いくつか必要なものがあります。
1)AWS アカウント
当然ですが、アカウントが必要です。新規に申し込むと1年間の無料利用権が付いていますので、今回のサンプルも無料で動かせます。まだアカウントを持っていない方は以下のドキュメントを参考にアカウントを作成してください。
2)IAM ユーザー
Root 権限を持つ上記のアカウントではなく、特定の権限のみをアタッチした IAM ユーザーでアクセスする必要があるようです。
ステップ 1: AWS アカウントを設定して IAM ユーザーを作成する - Amazon Rekognition
こちらに従って、任意の IAM ユーザーを作成してください。
私の場合は、コンソールにログインする Admin ユーザーと、今回使用する「プログラムによるアクセス」にだけチェックを付けた制限ユーザーを作成しました。
ユーザーを作成する時に一緒にグループも作成でき、グループに対してポリシーをアタッチできます。
「AmazonRekognitionFullAccess」をアタッチしたユーザーは以下のようになるはずです。
適切にユーザーを作成したら、「認証情報」タブで「アクセスキーの生成」をクリックします。
「アクセスキー ID」と「シークレットアクセスキー」を控えておきます。csv をダウンロードしても良いでしょう。
コンソールにログインできるようになれば、Rekognition Console からデモを試すことができますので、やってみてください。
3)AWSSDK for .NET のインストール
SDK があるようなので、インストールします。(必須ではありませんが、VS 拡張があるので入れておいた方が便利っぽいです。)
.NET の「インストール」で最新の AWSSDK をダウンロードできます。
SDK には AWS Toolkit for Visual Studio が含まれているため、インストールすると、以下のように Visual Studio 2017 でプロジェクトテンプレートが使えたり、
アクセスキー ID とシークレットキーをプロファイル毎に登録できたりします。
準備は以上です。
(ユーザーの API Key と Secret Key だけあれば Azure みたいにインスタンスを作らなくても良い。というのは新鮮でした。)
NET Standard ライブラリ作成
準備ができたので .NET Standard のライブラリを作成します。私の場合は、「RekognitionSample.Core」という名前でプロジェクトを作成しました。
Windows の Visual Studio 2017 では「Visual C#>.NET Standard>クラスライブラリ」
macOS の Visual Studio for Mac では「マルチプラットフォーム>ライブラリ>.NET Standard ライブラリ」で作成できます。
空のソリューションを最初に作成して、プロジェクトを追加するのが良いかもしれません。
AWS SDK Rekognition の NuGet パッケージを追加
Windows の場合は、Core プロジェクトを右クリックして、「NuGet パッケージの管理」をクリックし、表示される NuGet パッケージマネージャーで「awssdk.rekognition」と検索して、パッケージをインストールします。
macOS の場合は Core のプロジェクトを右クリックして、「追加>NeGetパッケージの追加」を選択し、表示されるパッケージマネージャーで「awssdk.rekognition」と検索して、パッケージをインストールします。
モデル追加
まずは解析した結果を受け取る DetectedFaceDetail
モデルを用意します。
public class DetectedFaceDetail { public string Gender { get; set; } public float GenderConfidence { get; set; } public float HappinessConfidence { get; set; } public int AgeRangeHigh { get; set; } public int AgeRangeLow { get; set; } }
サービスクラス作成
次に、ファイルパスを引数にもらい、Rekognition の結果を DetectedFaceDetail
の List
として返す RekognitionService
クラスを作成し、メソッドを二つ用意します。
Rekognition は画像を渡す際に S3Bucket のタグから、または Base64 でエンコードした文字列を使用できます。SDK を利用すると、Base64 でエンコードする必要はなく、
Amazon.Rekognition.Model.Image
を渡すことができます。何と便利な!(Image
のBytes
プロパティはMemoryStream
型なので、それを受けて、内部で Base64 エンコードしてくれているのでしょう。)
public async Task<List<DetectedFaceDetail>> GetFaceDetailsFromLocalFileAsync(string filePath) { //画像のMemoryStreamを作成 var imageStream = await GenerateImageStreamFromLocalFileAsync(filePath); if (imageStream == null) return null; try { //AWS Rekognition Client を作成 using (var rekognitionClient = new AmazonRekognitionClient(Secrets.AccessKey, Secrets.SecretKey, RegionEndpoint.APNortheast1)) { var request = new DetectFacesRequest { Image = new Image { Bytes = imageStream }, Attributes = new List<string> { "ALL" } }; //responseを受け取り、必要な情報を抽出 var response = await rekognitionClient.DetectFacesAsync(request); var faceList = new List<DetectedFaceDetail>(); foreach (var face in response.FaceDetails) { faceList.Add(new DetectedFaceDetail { Gender = face.Gender.Value, GenderConfidence = face.Gender.Confidence, HappinessConfidence = face.Emotions.Find(x => x.Type.Value == EmotionName.HAPPY).Confidence, AgeRangeHigh = face.AgeRange.High, AgeRangeLow = face.AgeRange.Low }); } return faceList; } } catch (Exception e) { Debug.WriteLine(e.Message); } return null; } private async Task<MemoryStream> GenerateImageStreamFromLocalFileAsync(string filePath) { try { using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { using (var memoryStream = new MemoryStream()) { await fileStream.CopyToAsync(memoryStream); return memoryStream; } } } catch (Exception e) { Debug.WriteLine(e.Message); } return null; }
少し長いですが、
AmazonRekognitionClient(string, string, RegionEndpoint)
を最初に控えておいた「アクセスキーID」「シークレットキー」とエンドポイントを指定してインスタンス化(エンドポイントは RegionEndpoint Class | AWS SDK for .NET V3 を参照してください。APNortheast1
が東京リージョンです。)DetectFacesRequest
クラスのインスタンスにAmazon.Rekognition.Model.Image
として画像のMemoryStream
と取得する属性を指定DetectFacesAsync
メソッドでレスポンスを受け取り
をやっているだけです。
DetectFacesResponse
オブジェクトが戻ってきますので、FaceDetails
プロパティ内の List<Amazon.Rekognition.Model.FaceDetail>
から必要な情報を取り出しています。
Amazon.Rekognition.Model.FaceDetail
クラスの詳細は こちら をご覧ください。
FaceDetail
に含まれる Emotions
プロパティの型は List<Amazon.Rekognition.Model.Emotion>
です。Emotion
クラスには Confidence
と Type
があり、Type
の型は EmotionName
クラスの Field
で定義されています。
各クラス、メソッドの詳細は API ドキュメントを参照してください。
AWS SDK for .NET V3 API Documentation
余談ですが、AWS SDK for .NET によるプログラミング - AWS SDK for .NET などのドキュメントは基本 .NET Framework クライアントアプリや ASP.NET MVC など向けに書かれていて、App.config
や Web.config
に設定を書き込み、コードから読み込ませる方法が紹介されています。
.NET Core を使用した AWS SDK for .NET の設定 - AWS SDK for .NET
上記のように .NET Core を使ったサンプルも紹介されているのですが、config ファイルの代わりに設定ファイルを読み込もう。という感じでした。
API ドキュメントに以下のような AmazonRekognitionClient()
のコンストラクタで直接アクセスキー、シークレットキーを指定するやり方があったのでそれを使用していますが、ちゃんとやる場合は設定ファイルで指定するのが良いのかもしれません。
名前 | 説明 |
---|---|
AmazonRekognitionClient(string, string, RegionEndpoint) |
Constructs AmazonRekognitionClient with AWS Access Key ID and AWS Secret Key |
とりあえず、これで動くようになっているはずです。
Console アプリから確認
var rekognition = new RekognitionService(); var task = rekognition.GetFaceDetailsFromLocalFileAsync(@"D:\family.jpg"); var res = task.Result;
こんな感じで List<DetectedFaceDetail>
が取得できるのがわかるかと思います。
Xamarin.Forms からの呼び出し
ここまでできれば、後は Xamarin.Forms から呼び出すだけです。
カメラで撮った写真を RekognitionService
に渡してあげましょう。
カメラの写真の取得は、James Montemagno さんが作成した Xam.Plugin.Media を使います。
Xamarin.Forms のプロジェクト全部にプラグインをインストールします。
特に Android では Permission 関連でやることが多いので、ドキュメント をしっかり読んで対応しましょう。
Android でやらないといけない処理は
OnRequestPermissionsResult
をMainActivity
に追加CrossCurrentActivity
のコードをMainActivity
のOnCreate
内に追加AndroidManifest.xml
の<application></application>
内にprovider
の記述を追加Resources
フォルダ内にfile_paths.xml
ファイルを作成し、XML コードを追加
です。
Android で初回ビルド後の実行時に
Java.Lang.Exception
とかが出る場合は、Android プロジェクトの bin, obj フォルダを一度削除してリビルドするとうまくいくケースが多いです。
iOS でやらないといけない処理は
info.plist
に Permission の処理を追加
です。
後は、MainPage.xaml
を次のように書き換えて、
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" ios:Page.UseSafeArea="true" xmlns:local="clr-namespace:RekognitionSample" x:Class="RekognitionSample.MainPage"> <ScrollView> <StackLayout> <Button Text="Take a photo" Clicked="Button_Clicked"/> <Image x:Name="image" Aspect="AspectFit"/> </StackLayout> </ScrollView> </ContentPage>
小ネタとして iPhone X 対応の
ios:Page.UseSafeArea="true"
が入っています。
コードビハインドの Button_Clicked
のイベントハンドラに以下のコードを追加します。このコードは Xam.Plugin.Media のページの Permission Recommendations のコードをそのまま拝借したものです。
private async void Button_Clicked(object sender, EventArgs e) { var cameraStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera); var storageStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage); if (cameraStatus != PermissionStatus.Granted || storageStatus != PermissionStatus.Granted) { var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Camera, Permission.Storage }); cameraStatus = results[Permission.Camera]; storageStatus = results[Permission.Storage]; } if (cameraStatus == PermissionStatus.Granted && storageStatus == PermissionStatus.Granted) { var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions { Directory = "Sample", Name = "test.jpg" }); if (file == null) return; image.Source = ImageSource.FromFile(file.Path); var rekognition = new RekognitionService(); var faceList = await rekognition.GetFaceDetailsFromLocalFileAsync(file.Path); var faceCount = faceList.Count(); var face = faceList.FirstOrDefault(); await DisplayAlert("Rekognition", $"Detected: {faceCount} Person\n\nAgeRange: {face.AgeRangeLow} - {face.AgeRangeLow}\nGender: {face.Gender}\nGender Confidence: {face.GenderConfidence}%\nHappiness: {face.HappinessConfidence}%", "OK"); } else { await DisplayAlert("Permissions Denied", "Unable to take photos.", "OK"); //On iOS you may want to send your user to the settings screen. //CrossPermissions.Current.OpenAppSettings(); } }
- Permission の許可が得られていれば
CrossMedia.Current.TakePhotoAsync
メソッドで写真を撮り、file
に保存 - MainPage の
Image
にファイルを表示する
後は Console アプリでもやったように、
RekognitionService
をインスタンス化して、GetFaceDetailsFromLocalFileAsync
メソッドでfaceList
を取得- (今回はサンプルとして)取得された最初の情報を
DisplayAlert
で表示
です。
ビルドして動作させると以下のようになります。
iOS だと 81% の確立で女性という結果だったり、Android だと男性になったけど 57歳って…w という不思議な結果が出ていますが、会社の同僚にやってみたらそれっぽいのが出たので、現在の私の風貌が分かりづらいんだろうなw と思っておくことにします。
サンプルのコードを以下に置いておきます。ご興味があれば触ってみてください。
まとめ
Microsoft Azure と違い、自分のアカウントで Rekognition のリソースを作らなくて良いのは便利だなと思いました。
これについて id:beachside さんと話していたら、Azure の Face などのリソースもインスタンスを立てているわけではなく、Key を作っているだけとのこと。逆に AWS ではリソース(サービス)毎に Key やアクセス権を管理できない(=ユーザー毎の管理になるため)ので面倒だ。と。なるほど、アプローチが違うだけで、同じでしたw
SDK はやっぱり便利です。Rest API で使うこともできますが、認証やレスポンスの JSON の整形を気にすることなく、オブジェクトとしてデータを扱えます。.NET Standard に対応しているので、今回の Core プロジェクトは Xamarin ネイティブからも当然使えます。
AWS SDK for .NET ドキュメント の「開発者ガイド」は非常に少ない記事とサンプルしかないので基本的には API リファレンス を見ていくことになります。
以上です。
Xamarin 気になった方は
Visual Studio 2017 をインストールして触ってみてください。手順書は こちらのエントリー をご覧ください。 学習用リソース や JXUG リンクページ に参考資料を纏めてますので併せてどうぞ。
Xamarin の導入支援サービスを始めました。ベースは基本的なアプリを一緒に作ることで Xamarin を使えるようになって頂く内容ですが、ご要望に応じて講習内容のカスタマイズも可能です。詳しくは田淵までお問い合わせください(^^)
Xamarin 有償トレーニング : XLsoft エクセルソフト
Xamarin の情報が欲しい方はこのブログも購読いただいたり、私のTwitterアカウントをフォローいただいたりすると嬉しいです。
私が所属している エクセルソフト の宣伝を少しさせてください。弊社は開発者向けの様々なソフトウェアを扱っています。Office/PDF ファイルを .NET/Java で操作するライブラリ Aspose(アスポーズ)、Windows アプリ、Web ページ、iOS/Android アプリの UI テストができる TestComplete などお勧めです(^^) また、Visual Studio Professional/Enterprise with MSDN も販売してますし、日本で売っていない海外のソフトウェア、開発ツールなどを弊社経由で日本円で購入頂くことも可能です。ご興味あれば 弊社ページ を覗いてみてください。
以上です。