うら干物書き

ゲームを作っています。

【Unity】MicrosoftのFaceAPIを使って顔認識で遊んでみよう

 先日参加したCrossOverGameJam04で使用したMicrosoftのFaceAPIについてまとめてみましたよ。

f:id:urahimono:20170723141844p:plain


この記事にはUnity2017.1.0f3を使用しています。

FaceAPIとは

どんなものなの

 顔の画像から、年齢や感情などの情報を取得できちゃう楽しいものですぜ。
 アプリ内にSDKを組み込まなくても、ネットワーク経由で画像情報を送ることで簡単に情報が取得できちゃう。
 逆から言えば、ネットワークに繋がっている状態が必須だよ。
 送られてくる顔認識の情報はJson形式になっているみたい。

 MicrosoftAzureの機能なので、MicrosoftAzureへの登録が必須だよ。

どういう風に登録するの

 MicrosoftAzureへの登録とFaceAPIの登録のやり方については、参加したCrossOverGameJamの運営さんのブログが分かりやすいかな。
 こちらを参考にしてみてくださいね。
 
CrossOver GameJam 4回目 はじまるよ〜crossoverlink.wordpress.com

顔認識情報を取得しよう

 ではスクリプトを組んでいきますよ。
 今回のFaceAPIを使うためのベースのクラスがこちらです。

TestFaceAPI.cs

using UnityEngine;
using System.Collections;

public class TestFaceAPI : MonoBehaviour
{

    private IEnumerator Start()
    {
        yield return null;
    }

} // class TestFaceAPI

 これにいろいろな機能を組み込んでいきますよー。
 まずはリクエストするURLを作成する必要がありますね。

private IEnumerator Start()
{
    string url = GetFaceAPIURL();

    yield return null;
}

private string GetFaceAPIURL()
{
    return string.Empty;
}

地域指定しなきゃ

 MicrosoftAzureにてFaceAPIを登録した際に、どの地域にするかを指定したと思います。
 地域ごとでリスエストするURLが違うため、その対応を行いましょう。
 こんな感じにURLは作られます。

https://[location].api.cognitive.microsoft.com/face/v1.0/detect[?returnFaceId][&returnFaceLandmarks][&returnFaceAttributes]

 この[location]の部分に地域用の文字列を仕込みます。
 地域用の変数などを用意しましょう。

private enum ELocation
{
    WestUS,
    EastUS2,
    WestCentralUS,
    WestEurope,
    SoutheastAsia,
}

[SerializeField]
private ELocation   m_location  = default( ELocation );

 あとはこれらを利用してURLを作成します。

private string GetFaceAPIURL()
{
    string localURL = GetLocationURL( m_location );

    Debug.AssertFormat( !string.IsNullOrEmpty( localURL ), "不正な地域設定です。m_location={0}", m_location );

    return string.Format( "https://{0}.api.cognitive.microsoft.com/face/v1.0/detect", localURL );
}

private string GetLocationURL( ELocation i_location )
{
    return i_location.ToString().ToLower();
}

取得するパラメーターを追加しなきゃ

 地域を指定すれば基本的なリクエストするURLを完成なのですが、取得する顔認識情報が寂しいものになります。
 URLの部分に追加でパラメーターを指定してあげることで、もっといろいろな情報が送られてくるようになります。

FaceId

 FaceIdそのものについては後述。
 FaceIdを取得するかどうかをtruefalseで指定できます。
 指定しない場合はtrueになります。

[SerializeField]
private bool        m_requestFaceId         = true;

private string GetFaceIdField( bool i_flag )
{
    return string.Format( "?returnFaceId={0}", i_flag );
}

FaceLandmarks

 FaceLandmarksそのものについては後述。
 FaceLandmarksを取得するかどうかをtruefalseで指定できます。
 指定しない場合はfalseになります。

[SerializeField]
private bool        m_requestFaceLandmarks  = false;

private string GetFaceLandmarksField( bool i_flag )
{
    return string.Format( "&returnFaceLandmarks={0}", i_flag );
}

FaceAttributes

 FaceAttributesそのものについては後述。
 この情報が一番楽しいはず。
 FaceAttributesにはいろいろな種類があり、欲しい分だけパラメーターを追加してあげる必要がありますよ。
 文字列でガンガン足していけばいいのですが、外部からしてしやすいようにFlagsタイプのEnumを使いましょう。
 FlagsタイプのEnumをエディタ上で使いやすくするために、以下の記事のエディタ拡張を使わせてもらっています。
 
baba-s.hatenablog.com

[System.Flags]
private enum EFaceAttributes
{
    age             = 1 << 0,
    gender          = 1 << 1,
    smile           = 1 << 2,
    facialHair      = 1 << 3,
    headPose        = 1 << 4,
    glasses         = 1 << 5,
    emotion         = 1 << 6,
    hair            = 1 << 7,
    makeup          = 1 << 8,
    accessories     = 1 << 9,
    occlusion       = 1 << 10,
    blur            = 1 << 11,
    exposure        = 1 << 12,
    noise           = 1 << 13,
}

[SerializeField, EnumFlags]
private EFaceAttributes m_requestFaceAttributes = 0;

private string GetFaceAttributesField( EFaceAttributes i_attributes )
{
    string attributesStr    = string.Empty;
    foreach( EFaceAttributes attribute in System.Enum.GetValues( typeof( EFaceAttributes ) ) )
    {
        if( ( i_attributes & attribute ) != 0 )
        {
            attributesStr  += string.Format( "{0},", attribute );
        }
    }
    
    if( string.IsNullOrEmpty( attributesStr ) )
    {
        return string.Empty;
    }

    // 最後の","はいらないので削除(もっといい方法があるような気がするけど……)。
    attributesStr = attributesStr.Remove( attributesStr.Length - 1 );

    return string.Format( "&returnFaceAttributes={0}", attributesStr );
}

取得するパラメーターを追加しなきゃ2

 上記で作成したパラメーターをリクエストするURLに合体させてみましょう。

private string GetFaceAPIURL()
{
    string localURL         = GetLocationURL( m_location );
    string idField          = GetFaceIdField( m_requestFaceId );
    string landmarksField   = GetFaceLandmarksField( m_requestFaceLandmarks );
    string attributesField  = GetFaceAttributesField( m_requestFaceAttributes );

    Debug.AssertFormat( !string.IsNullOrEmpty( localURL ), "不正な地域設定です。m_location={0}", m_location );

    return string.Format( "https://{0}.api.cognitive.microsoft.com/face/v1.0/detect{1}{2}{3}", localURL, idField, landmarksField, attributesField );
}

 これでリクエストするURLの完成です。

ヘッダー情報を追加しなきゃ

 情報を送る際にヘッダー情報もカスタマイズする必要があるのです。
 FaceAPIには二種類の方法で顔の画像を送ることが出来ます。

  • 画像を直接送る
  • 画像のURLを送る

 送る方法にて、ヘッダーの情報が変わってきます。
 どちらの方法でもFaceAPIのサブスクリプションキーは絶対に送る必要があります。
 サブスクリプションキーはMicrosoftAzureからFaceAPIを登録した際に取得できると思いますよ。

[SerializeField]
private string  m_subscriptionKey   = string.Empty;

 では顔の画像を送る方法毎のヘッダー情報を作成していきましょう。

画像を直接送る場合

 画像データを直接送る場合のヘッダー情報はこんな感じになります。

private Dictionary<string, string> GetBinaryTypeHeader( string i_key )
{
    var header = new Dictionary<string, string>()
    {
        { "Content-Type",               "application/octet-stream" },
        { "Ocp-Apim-Subscription-Key",  i_key },
    };

    return header;
}

画像のURLを送る場合

 画像のURLを送る場合のヘッダー情報はこんな感じになります。

private Dictionary<string, string> GetURLTypeHeader( string i_key )
{
    var header = new Dictionary<string, string>()
    {
        { "Content-Type",               "application/json; charset=UTF-8" },
        { "Ocp-Apim-Subscription-Key",  i_key },
    };

    return header;
}

ヘッダー情報を追加しなきゃ2

 折角なので、上記二種の送り方をエディタ上で設定できるようにしましょう。

private enum EPostType
{
    Binary,
    URL,
}

[SerializeField]
private EPostType   m_postType  = default( EPostType );

 そして、送る方式によって先ほど作ったヘッダー取得関数を切り替わるようにします。
 

private IEnumerator Start()
{
    string url = GetFaceAPIURL();
    Dictionary<string, string> headers  = null;

    if( m_postType == EPostType.Binary )
    {
        headers = GetBinaryTypeHeader( m_subscriptionKey );
    }
    else
    {
        headers = GetURTypeLHeader( m_subscriptionKey );
    }

    yield return null;        
}

画像の情報を作らなきゃ

 さて最後に、送る顔の画像情報を作成しましょう。
 これも直接送るかURLを送るかで違っていきますよ。

画像を直接送る場合

 バイト型の配列にしなくてはいけません。
 Texture2Dなら簡単に取得できるので、Texture2Dで送る顔の画像を指定するようにしてみましょう。

[SerializeField]
private Texture2D   m_texture   = null;

private byte[] GetTextureBinary( Texture2D i_texture )
{
    if( i_texture == null )
    {
        return null;
    }

    return i_texture.EncodeToPNG();
}

画像のURLを送る場合

 URLを送る場合でも、やっぱりバイト型の配列にしなくてはいけません。

[SerializeField]
private string  m_textureURL    = null;

private byte[] GetTextureURLBinary( string i_url )
{
    if( string.IsNullOrEmpty( i_url ) )
    {
        return null;
    }

    string json = string.Format( "{{ \"url\":\"{0}\" }}", i_url );
    return System.Text.Encoding.UTF8.GetBytes( json );
}

画像の情報を作らなきゃ2

 上記の二種の関数をヘッダー情報を取得した際と同じように使用できるようにします。

private IEnumerator Start()
{
    string url      = GetFaceAPIURL();
    Dictionary<string, string> headers  = null;
    byte[] postData = null;

    if( m_postType == EPostType.Binary )
    {
        headers     = GetBinaryTypeHeader( m_subscriptionKey );
        postData    = GetTextureBinary( m_texture );
    }
    else
    {
        headers     = GetURTypeLHeader( m_subscriptionKey );
        postData    = GetTextureURLBinary( m_textureURL );
    }

    Debug.AssertFormat( postData != null && postData.Length > 0, "Postデータが不正です。" );


    yield return null;
}

WWWを使って情報を送らなきゃ

 さて、下準備が整いました。
 作成したすべての情報をWWWを使って送信しましょう。
 ……本当はUnityWebRequestを使って送信しようかと思ったのですが、UnityWebRequestはまだまだ勉強不足なので旧型WWWでいきます。

private IEnumerator Start()
{
    string url      = GetFaceAPIURL();
    Dictionary<string, string> headers  = null;
    byte[] postData = null;

    if( m_postType == EPostType.Binary )
    {
        headers     = GetBinaryTypeHeader( m_subscriptionKey );
        postData    = GetTextureBinary( m_texture );
    }
    else
    {
        headers     = GetURTypeLHeader( m_subscriptionKey );
        postData    = GetTextureURLBinary( m_textureURL );
    }

    Debug.AssertFormat( postData != null && postData.Length > 0, "Postデータが不正です。" );

    string receivedJson = null;
    using( WWW www = new WWW( url, postData, headers ) )
    {
        yield return www;
        receivedJson    = www.text;
    }

    Debug.Log( receivedJson );  
}

 これでJson形式にて情報が取得出来ているのが分かります。
 やったね。

取得した顔認識情報にて

 送った顔画像の情報の取得に成功しました。
 おめでとうございます。
 あとはごゆるりとお使いくださいませ……、と言いたいところですが、Jsonデータのままだと情報が扱いにくいので、クラスか何かに変換して使いやすい形にしましょう。

意地でもJsonUtilityを使わなきゃ

 Jsonを変換するにはMiniJsonLitJsonを使えばいいと思いますが、Unityで作業しているならJsonUtilityを使えばSDKなどを組み込まずに済むので楽ちんそうです。
 ただ、受け取った顔認識のJsonデータはJsonUtilityで使えるフォーマットではなかったりします。
 ぐぬぬ、これは厄介。
 いや、大丈夫だ。
 受けとったJsonデータを改竄してしまえば、JsonUtilityでも変換できるようになるはずだ!
 早速やってみよう。
※ 筆者はJsonUtilityを意地でも使うという謎の使命感に取りつかれています。
  以下の内容は参考程度にとどめておいたほうがよいかと思われます。

 まずJsonデータは以下のような形で受け取れます。

[{“faceId”:“d241307e-a331-4664-a3cf-63bfffed2ca5”,“faceRectangle”:{“top”:298,“left”:694,“width”:234,“height”:234},“faceAttributes”:{“age”:29.0}}]

 この情報のJsonUtilityを使った変換先のクラスを以下のように作成しましょう。(各クラスの詳細は後述)

[System.Serializable]
public class FacePersonData
{
    [SerializeField]
    public string           faceId          = null;
    [SerializeField]
    public FaceRectangle    faceRectangle   = null;
    [SerializeField]
    public FaceLandmarks    faceLandmarks   = null;
    [SerializeField]
    public FaceAttributes   faceAttributes  = null;

} // class FacePersonData

 送った顔の画像情報に複数の顔がある場合は以下のように複数の情報が記述された状態で受け取れます。

[{“faceId”:“13472612-1001-4d8d-8470-d2caf6e4ca76”,“faceRectangle”:{“top”:140,“left”:242,“width”:64,“height”:64},“faceAttributes”:{“age”:28.5}},{“faceId”:“a67283ef-684a-4857-8208-7001557f4896”,“faceRectangle”:{“top”:168,“left”:436,“width”:61,“height”:61},“faceAttributes”:{“age”:30.9}}]

 これは先ほど作成したFacePersonDataを配列で持っている形になっています。
 そのためFacePersonDataを配列として持っているクラスを作成します。

[System.Serializable]
public class FaceData
{
    [SerializeField]
    public FacePersonData[] persons = null;

} // class FaceData

 しかし、このままでは最初に話した通り、JsonUtilityでは変換できません。
 JsonUtilityを使用するにはフィールド名が必ず必要になります。
 すなわち、受け取ったJsonデータにフィールド名を足してやれば問題なく変換できるようになるのです。

private FaceData ConvertJson( string i_json )
{
    Debug.AssertFormat( !string.IsNullOrEmpty( i_json ), "Json情報が設定されていません。" );

    // 無理やりJsonUtilityで使える形に変更する。
    string json = string.Format( "{{\"persons\":{0}}}", i_json );

    FaceData faceData   = null;

    try
    {
        faceData    = JsonUtility.FromJson<FaceData>( json );
    }
    catch( System.Exception i_exception )
    {
        Debug.LogWarningFormat( "Json情報をクラス情報へ変換することに失敗しました。exception={0}", i_exception );
        faceData    = null;
    }

    return faceData;
}

 この形なら変換が可能になります。
 ついでにエラーの対応も行いましょう。
 FaceAPIが何らかの理由でエラーを吐き出す可能性もあります。
 その場合はエラー専用のJsonデータを受け取ることになります。
 エラー用のJsonデータと受け取りクラスを作成しましょう。

[System.Serializable]
public class FaceError
{
    [SerializeField]
    public FaceErrorDetail  error   = null;

} // class FaceError

[System.Serializable]
public class FaceErrorDetail
{
    [SerializeField]
    public string   code        = null;
    [SerializeField]
    public string   message     = null;

} // class FaceErrorDetail
private FaceData ConvertJson( string i_json )
{
    Debug.AssertFormat( !string.IsNullOrEmpty( i_json ), "Json情報が設定されていません。" );

    // Jsonデータがエラーの場合(もっといいエラーの判定があればいいんだけど……)。
    if( i_json.IndexOf( "error" ) > 0 )
    {
        FaceError faceError = null;
        try
        {
            faceError   = JsonUtility.FromJson<FaceError>( i_json );
        }
        catch( System.Exception )
        {
            faceError   = new FaceError();
        }

        Debug.LogWarningFormat( "顔情報の取得に失敗しているJson情報です。code={0}, message={1}", faceError.error.code, faceError.error.message );

        return null;            
    }

    // 無理やりJsonUtilityで使える形に変更する。
    string json = string.Format( "{{\"persons\":{0}}}", i_json );

    FaceData faceData   = null;

    try
    {
        faceData    = JsonUtility.FromJson<FaceData>( json );
    }
    catch( System.Exception i_exception )
    {
        Debug.LogWarningFormat( "Json情報をクラス情報へ変換することに失敗しました。exception={0}", i_exception );
        faceData    = null;
    }

    return faceData;
}

 これを顔画像を送る際の流れに組み込みましょう。

private IEnumerator Start()
{
    string url      = GetFaceAPIURL();
    Dictionary<string, string> headers  = null;
    byte[] postData = null;

    if( m_postType == EPostType.Binary )
    {
        headers     = GetBinaryTypeHeader( m_subscriptionKey );
        postData    = GetTextureBinary( m_texture );
    }
    else
    {
        headers     = GetURTypeLHeader( m_subscriptionKey );
        postData    = GetTextureURLBinary( m_textureURL );
    }

    Debug.AssertFormat( postData != null && postData.Length > 0, "Postデータが不正です。" );

    string receivedJson = null;
    using( WWW www = new WWW( url, postData, headers ) )
    {
        yield return www;
        receivedJson    = www.text;
    }

    FaceData faceData = ConvertJson( receivedJson );

}

 これで完成です。
 FaceAPIに顔の画像を送り、Jsonデータとして受け取り、クラスに変換するという流れが完了しました。

 あとはFaceAPIを使ってどんな情報が取得できるのかを、JsonUtilityを使った変換先クラスと一緒に簡単にまとめてみます。

faceId

 顔のIDです。
 取得するパラメータでfaceIdの取得をtrueにすることで受け取れます。
 受け取って何に使えばいいのかはわかりませんが……。
 Jsonとしては以下のように受け取れます。

“faceId”: “bd872b53-289b-46d1-9fe6-7ac707e6135e”

 単純なstring型なのでクラスは必要ないです。

faceRectangle

 送った画像の顔部分の範囲情報です。
 なんの追加パラメータを指定しなくても、この情報は受け取れます。
 Jsonとしては以下のように受け取れます。

“faceRectangle”: { “top”: 189, “left”: 444, “width”: 152, “height”: 152 },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class FaceRectangle
{
    [SerializeField]
    public int  top     = 0;
    [SerializeField]
    public int  left    = 0;
    [SerializeField]
    public int  width   = 0;
    [SerializeField]
    public int  height  = 0;

} // class FaceRectangle

faceLandmarks

 目や鼻などの位置情報です。
 取得するパラメータでfaceLandmarksの取得をtrueにすることで受け取れます。
 結構種類数がありますが、個別指定はできないようです。
 Jsonとしては以下のように受け取れます。

“faceLandmarks”: { “pupilLeft”: { “x”: 486.2, “y”: 230.0 }, “pupilRight”: { “x”: 553.8, “y”: 230.1 }, “noseTip”: { “x”: 523.0, “y”: 266.3 }, },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class FaceLandmarks
{
    [SerializeField]
    public Vector2  pupilLeft           = Vector2.zero;
    [SerializeField]
    public Vector2  pupilRight          = Vector2.zero;
    [SerializeField]
    public Vector2  noseTip             = Vector2.zero;
    [SerializeField]
    public Vector2  mouthLeft           = Vector2.zero;
    [SerializeField]
    public Vector2  mouthRight          = Vector2.zero;
    [SerializeField]
    public Vector2  eyebrowLeftOuter    = Vector2.zero;
    [SerializeField]
    public Vector2  eyebrowLeftInner    = Vector2.zero;
    [SerializeField]
    public Vector2  eyeLeftOuter        = Vector2.zero;
    [SerializeField]
    public Vector2  eyeLeftTop          = Vector2.zero;
    [SerializeField]
    public Vector2  eyeLeftBottom       = Vector2.zero;
    [SerializeField]
    public Vector2  eyeLeftInner        = Vector2.zero;
    [SerializeField]
    public Vector2  eyebrowRightInner   = Vector2.zero;
    [SerializeField]
    public Vector2  eyebrowRightOuter   = Vector2.zero;
    [SerializeField]
    public Vector2  eyeRightInner       = Vector2.zero;
    [SerializeField]
    public Vector2  eyeRightTop         = Vector2.zero;
    [SerializeField]
    public Vector2  eyeRightBottom      = Vector2.zero;
    [SerializeField]
    public Vector2  eyeRightOuter       = Vector2.zero;
    [SerializeField]
    public Vector2  noseRootLeft        = Vector2.zero;
    [SerializeField]
    public Vector2  noseRootRight       = Vector2.zero;
    [SerializeField]
    public Vector2  noseLeftAlarTop     = Vector2.zero;
    [SerializeField]
    public Vector2  noseRightAlarTop    = Vector2.zero;
    [SerializeField]
    public Vector2  noseLeftAlarOutTip  = Vector2.zero;
    [SerializeField]
    public Vector2  noseRightAlarOutTip = Vector2.zero;
    [SerializeField]
    public Vector2  upperLipTop         = Vector2.zero;
    [SerializeField]
    public Vector2  upperLipBottom      = Vector2.zero;
    [SerializeField]
    public Vector2  underLipTop         = Vector2.zero;
    [SerializeField]
    public Vector2  underLipBottom      = Vector2.zero;

} // class FaceLandmarks

faceAttributes

 顔についてのいろいろな情報群です。
 取得するパラメータでFaceAttributesとして指定した分だけ受け取ることが出来ます。

[System.Serializable]
public class FaceAttributes
{
    [SerializeField]
    public float            age         = 0.0f;
    [SerializeField]
    public string           gender      = null;
    [SerializeField]
    public float            smile       = 0.0f;
    [SerializeField]
    public FacialHair       facialHair  = null;
    [SerializeField]
    public HeadPose         headPose    = null;
    [SerializeField]
    public string           glasses     = null;
    [SerializeField]
    public Emotion          emotion     = null;
    [SerializeField]
    public Hair             hair        = null;
    [SerializeField]
    public Makeup           makeup      = null;
    [SerializeField]
    public Accessories[]    accessories = null;
    [SerializeField]
    public Occlusion        occlusion   = null;
    [SerializeField]
    public Blur             blur        = null;
    [SerializeField]
    public Exposure         exposure    = null;
    [SerializeField]
    public Noise            noise       = null;

} // class FaceAttributes

age

 年齢情報です。
 Jsonとしては以下のように受け取れます。

“age”: 29.9,

 単純なfloat型なのでクラスは必要ないです。

gender

 性別情報です。
 "male"か"female"の文字列で格納されています。
 Jsonとしては以下のように受け取れます。

“gender”: “male”,

 単純なstring型なのでクラスは必要ないです。

smile

 笑顔情報です。
 0から1float値で格納されています。
 後述のemotionのhappinessも同等な値と思われます。
 Jsonとしては以下のように受け取れます。

“smile”: 0.007,

 単純なfloat型なのでクラスは必要ないです。

facialHair

 口ひげなどの毛領域の情報がfloat値で格納されています。
 Jsonとしては以下のように受け取れます。

“facialHair”: { “moustache”: 0.3, “beard”: 0.1, “sideburns”: 0.1 },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class FacialHair
{
    [SerializeField]
    public float    moustache   = 0.0f;
    [SerializeField]
    public float    beard       = 0.0f;
    [SerializeField]
    public float    sideburns   = 0.0f;

} // class FacialHair

headPose

 顔の回転情報がfloat値で格納されています。
 Jsonとしては以下のように受け取れます。

“headPose”: { “pitch”: 0.0, “roll”: -0.1, “yaw”: 4.3 },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class HeadPose
{
    [SerializeField]
    public float    pitch   = 0.0f;
    [SerializeField]
    public float    roll    = 0.0f;
    [SerializeField]
    public float    yaw     = 0.0f;

} // class HeadPose

glasses

 メガネ情報が文字列で格納されています。
 以下のような種類があるみたいですよ。

  • NoGlasses
  • ReadingGlasses
  • Sunglasses
  • SwimmingGoggles

 Jsonとしては以下のように受け取れます。

“glasses”: “NoGlasses”,

 単純なstring型なのでクラスは必要ないです。

emotion

 感情情報がfloat値で合計が1になるように格納されています。
 Jsonとしては以下のように受け取れます。

“emotion”: { “anger”: 0.002, “contempt”: 0.0, “disgust”: 0.0, “fear”: 0.014, “happiness”: 0.007, “neutral”: 0.0, “sadness”: 0.0, “surprise”: 0.977 },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Emotion
{
    [SerializeField]
    public float    anger       = 0.0f;
    [SerializeField]
    public float    contempt    = 0.0f;
    [SerializeField]
    public float    disgust     = 0.0f;
    [SerializeField]
    public float    fear        = 0.0f;
    [SerializeField]
    public float    happiness   = 0.0f;
    [SerializeField]
    public float    neutral     = 0.0f;
    [SerializeField]
    public float    sadness     = 0.0f;
    [SerializeField]
    public float    surprise    = 0.0f;

} // class Emotion

hair

 髪の色や有無などの髪情報が格納されています。
 Jsonとしては以下のように受け取れます。

“hair”: { “bald”: 0.05, “invisible”: false, “hairColor”: [ { “color”: “black”, “confidence”: 1.0 }, ] }

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Hair
{
    [SerializeField]
    public float        bald        = 0.0f;
    [SerializeField]
    public bool         invisible   = false;
    [SerializeField]
    public HairColor[]  hairColor   = null;

} // class Hair

[System.Serializable]
public class HairColor
{
    [SerializeField]
    public string   color       = null;
    [SerializeField]
    public float    confidence  = 0.0f;

} // class HairColor

makeup

 目元と唇の化粧の有無が格納されています。
 Jsonとしては以下のように受け取れます。

“makeup”: { “eyeMakeup”: true, “lipMakeup”: false },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Makeup
{
    [SerializeField]
    public bool eyeMakeup   = false;
    [SerializeField]
    public bool lipMakeup   = false;

} // class Makeup

accessories

 顔にあるアクセサリー情報が配列で含まれています。無い場合は空です。
 以下のような種類があるみたいですよ。

  • headWear
  • glasses
  • mask

 Jsonとしては以下のように受け取れます。

“accessories”: [ { “type”: “mask”, “confidence”: 0.99 } ],

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Accessories
{
    [SerializeField]
    public string   type       = null;
    [SerializeField]
    public float    confidence  = 0.0f;

} // class Accessories

occlusion

 目や口などが塞がれているかの有無の情報が格納されています。
 Jsonとしては以下のように受け取れます。

“occlusion”: { “foreheadOccluded”: false, “eyeOccluded”: false, “mouthOccluded”: false },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Occlusion
{
    [SerializeField]
    public bool foreheadOccluded    = false;
    [SerializeField]
    public bool eyeOccluded         = false;
    [SerializeField]
    public bool mouthOccluded       = false;

} // class Occlusion

blur

 顔のぼかしレベルが格納されています。
 レベルは以下のような段階があるようです。

  • Low
  • Medium
  • High

 Jsonとしては以下のように受け取れます。

“blur”: { “blurLevel”: “low”, “value”: 0.25 },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Blur
{
    [SerializeField]
    public string   blurLevel   = null;
    [SerializeField]
    public float    value       = 0.0f;

} // class Blur

exposure

 顔の露出レベルが格納されています。
 レベルは以下のような段階があるようです。

  • GoodExposure
  • OverExposure
  • UnderExposure

 Jsonとしては以下のように受け取れます。

“exposure”: { “exposureLevel”: “overExposure”, “value”: 0.8 },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Exposure
{
    [SerializeField]
    public string   exposureLevel   = null;
    [SerializeField]
    public float    value           = 0.0f;

} // class Exposure

noise

 顔のノイズ(?)レベルが格納されています。
 レベルは以下のような段階があるようです。

  • Low
  • Medium
  • High

 Jsonとしては以下のように受け取れます。

“noise”: { “noiseLevel”: “low”, “value”: 0.0 },

 以下のクラスでJsonUtilityを使って変換できます。

[System.Serializable]
public class Noise
{
    [SerializeField]
    public string   noiseLevel  = null;
    [SerializeField]
    public float    value       = 0.0f;

} // class Noise

振り返り

 僕がこのFaceAPIを使った、CrossOverGameJam04では主にemotionの値を利用していました。
 happinessなど喜んでいる感情は読み取りやすかったのですが、sadnessなど悲しいなど感情はなかなか読み取りにくいみたいです。
 そしてアニメのキャラクターの顔画像などでは、なかなか顔と認識してくれないみたいです。

 というわけで、FaceAPIを使って手軽に顔認識を楽しんでみてくださいね。

コード完全版

【Unity】MicrosoftのFaceAPIを使って顔認識で遊んでみよう

参考リンク

azure.microsoft.com
https://westus.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395236westus.dev.cognitive.microsoft.com
www.atmarkit.co.jp