20210329のUnityに関する記事は2件です。

VContainer入門(2) - LifetimeScope

この記事について

前回に引き続きVContainerの説明をしていきます。

今回はLifetimeScopeの基本的な使い方を説明します。
LifetimeScopeを使うと前回紹介したIContainerBuilderIObjectResolverを簡単に扱えます。

LifetimeScopeを試してみる

動作確認用のクラスを作成

今回もサンプルとして前回と全く同じクラスを使います。念のためそのまま載せてきます。

Mock.cs
using 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.cs
using 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を使う流れとして次の説明をしました。

  1. IContainerBuilderを生成する。
  2. IContainerBuilderに使いたいクラスを登録する。
  3. IContainerBuilderからIObjectResolverを生成する。
  4. IObjectResolverを通して使いたいクラスを生成する。

LifetimeScopeクラスを継承すると、この1,2,3を自動的にやってくれます。

2の段階でConfigureというメソッドを呼び出してくれるのでこれをoverrideして好きなクラスを登録します。引数に渡されたIContainerBuilderのRegisterを呼び出してください。

LifetimeScopeを使う

LifetimeScopeMonoBehaviourを継承したクラスなのでGameObjectにアタッチして使います。

下の画像はTestLifetimeScopeのインスペクタです。
TestLifetimeScopeのインスペクタ
いくつか設定項目がありますが、ひとまずAutoRunにチェックが付いていることを確認してください。チェックが付いていればAwakeのタイミングで1~3をやってくれます。

次にこのLifetimeScopeを使うTestMonoBehaviourクラスを作成します。
このクラスをTestlifetimeScopeとおなじGameObjecctにアタッチすれば動きます。

TestMonoBehaviour.cs
using 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.cs
using 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.cs
using 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以外の登録用メソッドを紹介する予定です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/0a71d4658e7f4a650844

Rekognitionを使いたいので、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の簡単な使い方でした。感情値データを使えばいろいろなことができるので遊んでみてください。
あと、動くけれどここのやり方違うとかあるかもなので、そこんところは教えてくれると嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む