- 投稿日:2021-03-29T01:34:48+09:00
VContainer入門(2) - LifetimeScope
この記事について
前回に引き続きVContainerの説明をしていきます。
今回はLifetimeScopeの基本的な使い方を説明します。
LifetimeScopeを使うと前回紹介したIContainerBuilder
とIObjectResolver
を簡単に扱えます。LifetimeScopeを試してみる
動作確認用のクラスを作成
今回もサンプルとして前回と全く同じクラスを使います。念のためそのまま載せてきます。
Mock.csusing UnityEngine; using VContainer; // 先頭に[Logger]を付けてログ出力するクラス public sealed class Logger { public void Log(string message) => Debug.Log("[Logger] " + message); } // 足し算するだけのクラス public sealed class Calculator { public int Add(int a, int b) => a + b; } // LoggerとCalculatorに依存するクラス public sealed class HogeClass { private readonly Logger logger; private readonly Calculator calculator; [Inject] public HogeClass(Logger logger, Calculator calculator) { this.logger = logger; this.calculator = calculator; } public void LoggerTest() { logger.Log("LoggerTest"); } public void CalculatorTest(int a, int b) { int result = calculator.Add(a, b); logger.Log($"{a} + {b} = {result}"); } }LifetimeScopeを作成
これを使うLifetimeScopeは以下のようになります。
TestLifetimeScope.csusing VContainer; using VContainer.Unity; public sealed class TestLifetimeScope : LifetimeScope { protected override void Configure(IContainerBuilder builder) { builder.Register<Logger>(Lifetime.Singleton); builder.Register<Calculator>(Lifetime.Singleton); builder.Register<HogeClass>(Lifetime.Singleton); } }前回、VContainerを使う流れとして次の説明をしました。
- IContainerBuilderを生成する。
- IContainerBuilderに使いたいクラスを登録する。
- IContainerBuilderからIObjectResolverを生成する。
- IObjectResolverを通して使いたいクラスを生成する。
LifetimeScope
クラスを継承すると、この1,2,3を自動的にやってくれます。2の段階で
Configure
というメソッドを呼び出してくれるのでこれをoverrideして好きなクラスを登録します。引数に渡されたIContainerBuilder
のRegisterを呼び出してください。LifetimeScopeを使う
LifetimeScope
はMonoBehaviour
を継承したクラスなのでGameObjectにアタッチして使います。下の画像はTestLifetimeScopeのインスペクタです。
いくつか設定項目がありますが、ひとまずAutoRunにチェックが付いていることを確認してください。チェックが付いていればAwakeのタイミングで1~3をやってくれます。次にこのLifetimeScopeを使う
TestMonoBehaviour
クラスを作成します。
このクラスをTestlifetimeScope
とおなじGameObjecctにアタッチすれば動きます。TestMonoBehaviour.csusing UnityEngine; using VContainer; public sealed class TestMonoBehaviour : MonoBehaviour { public void Start() { TestLifetimeScope testLifetimeScope = GetComponent<TestLifetimeScope>(); HogeClass hogeClass = testLifetimeScope.Container.Resolve<HogeClass>(); hogeClass.CalculatorTest(2, 3); // 2 + 3 = 5 と表示される } }LifetimeScopeが生成したIContainerBuilderは
Container
プロパティに入れられています。そこからHogeClassをResolveして使っているだけです。ComponentにInjectする
先ほどの例ではLifetimeScopeのContainerプロパティに直接アクセスしていましたが、もっと便利な方法があります。
TestMonoBehaviour自体を登録して自動的にResolveしてもらいます。まずはLifetimeScopeでTestMonoBehaviourを登録します。
TestLifetimeScope.csusing UnityEngine; using VContainer; using VContainer.Unity; public sealed class TestLifetimeScope : LifetimeScope { // testMonoBehaviourをインスペクタで設定する [SerializeField] private TestMonoBehaviour testMonoBehaviour; protected override void Configure(IContainerBuilder builder) { builder.Register<Logger>(Lifetime.Singleton); builder.Register<Calculator>(Lifetime.Singleton); builder.Register<HogeClass>(Lifetime.Singleton); // testMonoBehaviourのインスタンスを登録する builder.RegisterComponent(testMonoBehaviour); } }
[SerializeField] private TestMonoBehaviour testMonoBehaviour;
とbuilder.RegisterComponent(testMonoBehaviour);
の行を追加しました。
RegisterComponent
はMonoBehaviourを登録するためのメソッドです。[Inject]属性がついたメンバが存在すればIObjectResolver
を生成した直後にそのインスタンスにInjectしてくれます。TestMonoBehaviourもInjectできるように書き換えます。
TestMonoBehaviour.csusing UnityEngine; using VContainer; public sealed class TestMonoBehaviour : MonoBehaviour { private HogeClass hogeClass; [Inject] public void Inject(HogeClass hogeClass) { this.hogeClass = hogeClass; } public void Start() { hogeClass.CalculatorTest(2, 3); // 2 + 3 = 5 と表示される } }
IObjectResolver
は[Inject]属性が付いたメソッドを発見して、登録されているものの中から合致するものを自動的にResolveして渡してくれます。この形なら
TestMonoBehaviour
側はどこからHogeClassが渡されてくるかを気にせずに実装できます。まとめ
LifetimeScope
クラスを継承して必要なLifetimeScopeを実装します。- LifetimeScopeはAutoRunのチェックが付いていればAwakeのタイミングで以下のことをしてくれます。
- IContainerBuilderを生成してConfigureに渡します。
- IContainerBuilderからIObjectResolverを生成してContainerプロパティに保持します。
RegisterComponent
でMonoBehaviourのインスタンスをわたしてInjectできます。次回はRegisterやRegsiterComponent以外の登録用メソッドを紹介する予定です。
- 投稿日:2021-03-29T00:28:50+09:00
UnityからRekognitionを使ってみた
R&D成果発表会Vol.2
こんにちは。ノベルワークスR&Dチームのともやんです。
今回はUnityと表情分析でうにゃうにゃした際の報告です。
この記事がVol.100くらいになるころには、なにかおもしろいものがお目見えしているかもしれません。
それでは早速!Amazon Rekognitionとは
Amazon Rekognitionとは、AWSのサービスの一つで、画像や動画を投げると、顔比較や感情分析など高度なことを、簡単に行える便利なものです。今回はこのサービスを使って、感情分析を行っていきます。
AWS SDK for .Netについて
unityでのAWSSDK導入方法については基本ここ参照。
https://qiita.com/nshinya/items/0a71d4658e7f4a650844Rekognitionを使いたいので、AWS SDK.Rekognition.dllを入れてください
やりかた
やり方として
1.unityでwebカメラを使う
2.Rekognitionにwebカメラの画像を投げる
3.返ってきたJson風データから感情値を取り出す
の三つで説明していきます
完成コード(雑です)
using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.IO; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Amazon.Rekognition; using Amazon.Rekognition.Model; public class DetectFaces : MonoBehaviour { public RawImage rawImage; WebCamTexture webCamTexture; //textureをtexture2dにする関数 public Texture2D ToTexture2D(Texture self) { var sw = self.width; var sh = self.height; var format = TextureFormat.RGBA32; var result = new Texture2D(sw, sh, format, false); var currentRT = RenderTexture.active; var rt = new RenderTexture(sw, sh, 32); Graphics.Blit(self, rt); RenderTexture.active = rt; var source = new Rect(0, 0, rt.width, rt.height); result.ReadPixels(source, 0, 0); result.Apply(); RenderTexture.active = currentRT; return result; } //Amazon Rekognition public async Task Example(Amazon.Rekognition.Model.DetectFacesRequest image) { //AmazonRekognitionClient AmazonRekognitionClient rekognitionClient = new AmazonRekognitionClient("AWSのアクセスキー", "AWSのシークレットキー"); var response = await rekognitionClient.DetectFacesAsync(image); foreach(var i in response.FaceDetails[0].Emotions) { Debug.Log(i.Confidence); Debug.Log(i.Type); } } //start public void Start() { webCamTexture = new WebCamTexture(); rawImage.texture = webCamTexture; webCamTexture.Play(); } public async void UseReko() { var img2d = ToTexture2D(webCamTexture); var img = img2d.EncodeToJPG(); var stream = new MemoryStream(img); List<string> att = new List<string>() { "ALL" }; var rekoImg = new Amazon.Rekognition.Model.Image { Bytes = stream }; var reqImg = new DetectFacesRequest {Attributes=att, Image = rekoImg }; await Example(reqImg); }1.Webカメラ
RawImageにwebカメラで取得した画像をはっつけます。顔見たいからです。別に顔を画面で見たくねえよって人はrawImageなんていりません。
略 public RawImage rawImage; WebCamTexture webCamTexture; 略 public void Start(){ webCamTexture=new WebCamTexture(); rawImage.texture=webCamTexture; webCamTexture.Play() } }んで、webCamTextreなんですが、これをそのままImageとしてRekognitionに投げるわけにはいきません。
というのも、Amazon.Rekognition.Model.DetectFacesRequestクラスのImageを投げなきゃいけないのです。ということで、
WebCamTexture>Texture2D>JPG形式>MemoryStream>DetectFacesRequestという順序で変換していきます。WebCamTexture>Texture2D
WebCamTextureは一見Texture2Dなんですが、残念ながら違うんです。WebCamTextureというクラスなんです。だから、いったんToTexture2D()をつかって変換します。
var img2d = ToTexture2D(webCamTexture);Texture2D>JPG
これも関数一発ドーンってやつです
var img = img2d.EncodeToJPG();JPG>MemoryStream
DetectFacesRequestを作るには、Byte列としてMemoryStreamを作る必要があります。MemoryStreamがなんだかはよくわかっていません。これもドーンです。
var stream = new MemoryStream(img);MemoryStream>DetectFacesRequest
これはnewするときの引き数にMemoryStreamを入れるだけです。
var rekoImg = new Amazon.Rekognition.Model.Image { Bytes = stream };これで投げる画像はOKです。あと、どんな情報が返ってくるかの指定として、ALLを指定しておきます。ALLじゃないと感情値とかは返ってきません。
List<string> att = new List<string>() { "ALL" };んで最後に
var reqImg = new DetectFacesRequest {Attributes=att, Image = rekoImg };というようにRekognitionに投げるものを作ってあげれば終了です。
RekognitionにWebカメラの画像を投げる
AmazonRekognitionClient rekognitionClient = new AmazonRekognitionClient("AWSのアクセスキー", "AWSのシークレットキー"); var response = await rekognitionClient.DetectFacesAsync(image);ここで、DetectFacesAsync()をつかって投げています。ここで大事なのが、DetectFacesじゃなくてDetectFacesAsyncを使わなくちゃいけないということです。非同期なのでそこら辺のことも書き足さなきゃデス。
Asynchronous operations (methods ending with Async) in the table below are for .NET 4.5 or higher. For .NET 3.5 the SDK follows the standard naming convention of BeginMethodName and EndMethodName to indicate asynchronous operations - these method pairs are not shown in the table below.
(APIリファレンスにちっちゃく書いてある)きちんとリファレンス読むべきですね。(Asyncないとerrorとしてinaccesible due to のやつが出てきます)
返ってきたJson風データから感情値を取り出す
返ってくるデータはこんな形です。
{ "FaceDetails": [ { "AgeRange": { "High": number, "Low": number }, "Beard": { "Confidence": number, "Value": boolean }, "BoundingBox": { "Height": number, "Left": number, "Top": number, "Width": number }, "Confidence": number, "Emotions": [ { "Confidence": number, "Type": "string" } ], "Eyeglasses": { "Confidence": number, "Value": boolean }, "EyesOpen": { "Confidence": number, "Value": boolean }, "Gender": { "Confidence": number, "Value": "string" }, "Landmarks": [ { "Type": "string", "X": number, "Y": number } ], "MouthOpen": { "Confidence": number, "Value": boolean }, "Mustache": { "Confidence": number, "Value": boolean }, "Pose": { "Pitch": number, "Roll": number, "Yaw": number }, "Quality": { "Brightness": number, "Sharpness": number }, "Smile": { "Confidence": number, "Value": boolean }, "Sunglasses": { "Confidence": number, "Value": boolean } } ], "OrientationCorrection": "string" }さっき、返ってくるデータ指定したけれど、DefaultだとBoundingBox, Confidence, Pose, Quality, Landmarksだけ返ってきます。
foreachでちょちょいと出してあげればOKです。以上、Rekognitionの簡単な使い方でした。感情値データを使えばいろいろなことができるので遊んでみてください。
あと、動くけれどここのやり方違うとかあるかもなので、そこんところは教えてくれると嬉しいです。