UnityとRealSenseで手(指関節)の認識

色々怪しい所が見え始めてくるRealSenseのHandModuleですが、
新しい情報はあまり公開されてないようなので、一例と思い、タイトル通りの記事を投稿します。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">意外とすんなり動いてくれた上に、結構まともに動いてます。まぁ、出来てる事がほぼLeapMotionなんですけどね。ただ、カメラ配置的に私の用途としてはRealSenseの思想が理想なのです。 <a href="https://t.co/6H0SB5DIiO">pic.twitter.com/6H0SB5DIiO</a></p>&mdash; 琴葉 葵（偽） (@Hv2RMjHzDyqXVIr) <a href="https://twitter.com/Hv2RMjHzDyqXVIr/status/871396560805822464">2017年6月4日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

Version
Unity:5.6.1f1 Personal
RealSenseSDK:2016 R3
Device:SR300

動かし方
空のシーンを作ってカメラに下のコードをコピペして作ったスクリプトをアタッチするだけです。

```csharp
using System;
using System.Collections.Generic;
using UnityEngine;
using Intel.RealSense;
using Intel.RealSense.Hand;

public class HandTest : MonoBehaviour
{
    SenseManager SenseM;
    HandModule HandM;
    HandData HandD;

    List<GameObject> PointsL = new List<GameObject>();
    List<GameObject> PointsR = new List<GameObject>();
    List<JointData> JointsL = new List<JointData>();
    List<JointData> JointsR = new List<JointData>();

    float SphereSize = 0.03f;

    void Start()
    {
        int n = Enum.GetNames(typeof(JointType)).Length;
        for (int i = 0; i < n; i++)
        {
            var goL = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            var goR = GameObject.CreatePrimitive(PrimitiveType.Sphere);
            goL.transform.localScale = new Vector3(SphereSize, SphereSize, SphereSize);
            goR.transform.localScale = new Vector3(SphereSize, SphereSize, SphereSize);
            PointsL.Add(goL);
            PointsR.Add(goR);
            JointsL.Add(new JointData());
            JointsR.Add(new JointData());
        }

        SenseM = SenseManager.CreateInstance();
        HandM = HandModule.Activate(SenseM);
        HandM.FrameProcessed += Hand_FrameProcessed;
        HandD = HandM.CreateOutput();
        SenseM.Init();
        SenseM.StreamFrames(false);
    }
    
    private void Hand_FrameProcessed(object sender, FrameProcessedEventArgs args)
    {
        HandD.Update();
        IHand[] hand;
        if (HandD.QueryHandData(AccessOrderType.ACCESS_ORDER_BY_ID, out hand) == Status.STATUS_NO_ERROR)
        {
            foreach (IHand h in hand)
            {
                int n = Enum.GetNames(typeof(JointType)).Length;
                if (h.BodySide == BodySideType.BODY_SIDE_LEFT)
                {
                    for (int i = 0; i < n; i++)
                    {
                        var jt = (JointType)Enum.ToObject(typeof(JointType), i);
                        JointsL[i] = h.TrackedJoints[jt];
                    }
                }
                if (h.BodySide == BodySideType.BODY_SIDE_RIGHT)
                {
                    for (int i = 0; i < n; i++)
                    {
                        var jt = (JointType)Enum.ToObject(typeof(JointType), i);
                        JointsR[i] = h.TrackedJoints[jt];
                    }
                }
            }
        }
    }

    void Update()
    {
        int n = Enum.GetNames(typeof(JointType)).Length;
        for (int i = 0; i < n; i++)
        {
            PointsL[i].transform.position = JointsL[i].positionWorld;
            PointsR[i].transform.position = JointsR[i].positionWorld;
        }
    }

    void OnDestroy()
    {
        HandM.FrameProcessed -= Hand_FrameProcessed;
        HandD.Dispose();
        SenseM.Dispose();
    }
}
```

※怪しいポイント※
・MIRROR_MODE_HORIZONTALを適用すると認識がおかしくなる。
・TRACKING_MODE_FULL_HANDを適用しなくても何故か動いてしまう。
・HandConfigurationの値をデバッガで見ても信用ならない。

参考
https://software.intel.com/sites/landingpage/realsense/camera-sdk/v2016r3/documentation/html/index.html?doc_devguide_introduction.html