Nuitrack 1.5.0
3D スケルトン トラッキング ミドルウェア
 すべて クラス 名前空間 関数 変数 Typedefs 列挙型 列挙子 プロパティ イベント グループ ページ
RGB イメージ上にスケルトンを表示

このチュートリアルでは、Unity プロジェクト内のカラー イメージをセンサーから取得して表示する方法、Nuitrack から取得したユーザーのスケルトンを表示する方法を紹介します。

完成済みプロジェクトは Nuitrack SDK: [Unity 3D] > [NuitrackSDK.unitypackage] > [Tutorials] > [RGB and Skeletons]にあります。

このプロジェクトを作成するために必要なものは以下の通りです。

  • Nuitrack Runtime と Nuitrack SDK
  • サポートされているセンサー (サポートしているセンサーの一覧は、Nuitrack Webサイトを参照)
  • Unity 2019.2.11f 以上
skeleton2.gif

シーンのセットアップとセンサーからの RGB 出力の取得

  1. 新規プロジェクトを作成します。
  2. Nuitrack SDK をダウンロードし、プロジェクトに NuitrackSDK.unitypackage をインポート ([Assets] > [Import Package] > [Custom Package...]) しますが、Tutorials/RGBandSkeletons/FinalAssetsTutorials/FaceTracker/Final Assets は除きます。
  3. NuitrackScripts プレハブをシーンにドラッグ アンド ドロップします (このプレハブが、プロジェクト内で Nuitrack モジュールの使用を可能にします)。
  4. NuitrackScripts プレハブの Nuitrack Manager セクションで、必要なモジュールを選択します。Color Module On (RGB イメージの表示)、Skeleton Tracker Module On (スケルトンのトラッキング)

    Urgb_2.png

  5. 新しいスクリプトを DrawColorFrame として作成します。このスクリプトでは、シーンへのRGB イメージ出力を特定します。
  6. イメージを表示するためのパブリック フィールド RawImage background を作成します。Start では、各カラー フレームの更新を登録します。

    using UnityEngine;
    using UnityEngine.UI;
    public class DrawColorFrame :MonoBehaviour
    {
    [SerializeField] RawImage background;
    void Start()
    {
    NuitrackManager.onColorUpdate += DrawColor;
    }
    }
    注意
    このプロジェクトでは、Image ではなく RawImage を使用するのはなぜでしょう?Image は、スプライトの表示のみに使用されます。RawImage は、どんなタイプのテクスチャの表示にでも使用できます。Sprites は作業がしやすいですが、Sprite.Create や運用により多くの費用がかかります。時間がより多くかかり、メモリも多く使用します。RawImage を使用することで、スプライトを作成する手間が省けます。RawImage がテクスチャ (Nuitrack (フレーム) から取得したデータを使って ToTexture2D() で作成したテクスチャ) を受け入れます。
  7. DrawColor メソッドでは、ColorFrame のテクスチャを取得し、background テクスチャに渡します。

    public class DrawColorFrame :MonoBehaviour
    {
    NuitrackManager.onColorUpdate += DrawColor;
    }
    void DrawColor(nuitrack.ColorFrame frame)
    {
    background.texture = frame.ToTexture2D();
    }
    }
    注意
    メモリリークに気が付いたなら、新しいテクスチャを表示する前に、古いテクチャを削除します。例えば、Destroy(oldTexture を使用して削除することができます。
  8. シーンにキャンバスを新規作成します ([Create] > [UI] > [キャンバス])。その後、そのキャンバスに子オブジェクトを作成します([Create] > [UI] > [RAW イメージ])。取得したテクスチャ (センサーからのイメージ) は、Raw イメージの上に引き伸ばされます。
  9. [RAW Image]設定で、[Anchor Preset]を選択し、Altキー を押すことで、キャンバスの高さと幅いっぱいにオブジェクトを引き伸ばすことができます。

    Urgb_3.png

  10. RawImage を 180°X軸で回転させます (回転させない場合、イメージは反転されたままになります)。

    Urgb_4.png

  11. キャンバスの名前を ColorFrameCanvas に変更し、DrawColorFrame スクリプトを追加します。
  12. Raw イメージをスクリプトの背景フィールドにドラッグ アンド ドロップします。

    Urgb_5.png

  13. プロジェクトを実行します。センサーから取得したカラー イメージが画面に表示されるようになります。
Urgb_6.gif

スケルトンを表示

  1. センサーからのカラー イメージも楽しいですが、Nuitrack 本来の用途であるユーザー スケルトンを表示しないと意味がありません。スケルトンのトラッキングと表示を始めます。まず、[depth-to-color registration]をオンにします。これは、深度マップが RGB イメージとぴったり一致しないので、揃える必要があるからです。depth-to-color registration をオンにするには、nuitrack.config を開き、DepthProvider.Depth2ColorRegistration を true に設定します。
  2. ユーザーのスケルトンを表示するためのプレハブを 2つ作成します (関節部分になる球体と関節同士の接続部分になる直線)。オプションとして、NuitrackSDK.unitypackage からのプレハブを使用することもできます (Assets/NuitrackSDK/Tutorials/RGBandSkeletons/Prefabs (JointUI and ConnectionUI))。
  3. 新しいスクリプトを SimpleSkeletonAvatar として作成します。このスクリプトでは、1つのスケルトンに関するトラッキングと表示の定義を行います。
  4. 新しいブール変数 autoProcessing を作成します。処理を行うスケルトンが一つだけの場合、この変数の値をtrue に設定することで、Nuitrack はこのスケルトンに関するデータをすべて渡します。この段階では、一つのスケルトンのみを表示したいので、これで十分といえます。しかし、複数のスケルトンを表示する場合では、自動で複数のスケルトンを処理できないため、追加のコンポーネントが必要になります。
  5. 関節と接続部分のプレハブのために、2つのフィールド、jointPrefab とを connectionPrefab 作成します。

    using System.Collections.Generic;
    using UnityEngine;
    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    public bool autoProcessing = true;
    [SerializeField] GameObject jointPrefab = null, connectionPrefab = null;
    }

  6. jointsInfo 配列を、スケルトン表示に必要なすべての関節を含む一覧と共に作成します。Nuitrack がトラッキングを行う関節は全部で 19個です。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    public bool autoProcessing = true;
    [SerializeField] GameObject jointPrefab = null, connectionPrefab = null;
    nuitrack.JointType[] jointsInfo = new nuitrack.JointType[]
    {
    nuitrack.JointType.Head,
    nuitrack.JointType.Neck,
    nuitrack.JointType.LeftCollar,
    nuitrack.JointType.Torso,
    nuitrack.JointType.Waist,
    nuitrack.JointType.LeftShoulder,
    nuitrack.JointType.RightShoulder,
    nuitrack.JointType.LeftElbow,
    nuitrack.JointType.RightElbow,
    nuitrack.JointType.LeftWrist,
    nuitrack.JointType.RightWrist,
    nuitrack.JointType.LeftHand,
    nuitrack.JointType.RightHand,
    nuitrack.JointType.LeftHip,
    nuitrack.JointType.RightHip,
    nuitrack.JointType.LeftKnee,
    nuitrack.JointType.RightKnee,
    nuitrack.JointType.LeftAnkle,
    nuitrack.JointType.RightAnkle
    };
    }

  7. すべての接続部分を含む 2D 配列を作成します。最初と最後の関節を指定します (指定した 2つの関節の間に接続部分が作成されます)。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    nuitrack.JointType.RightAnkle
    };
    nuitrack.JointType[,] connectionsInfo = new nuitrack.JointType[,]
    {
    {nuitrack.JointType.Neck, nuitrack.JointType.Head},
    {nuitrack.JointType.LeftCollar, nuitrack.JointType.Neck},
    {nuitrack.JointType.LeftCollar, nuitrack.JointType.LeftShoulder},
    {nuitrack.JointType.LeftCollar, nuitrack.JointType.RightShoulder},
    {nuitrack.JointType.LeftCollar, nuitrack.JointType.Torso},
    {nuitrack.JointType.Waist, nuitrack.JointType.Torso},
    {nuitrack.JointType.Waist, nuitrack.JointType.LeftHip},
    {nuitrack.JointType.Waist, nuitrack.JointType.RightHip},
    {nuitrack.JointType.LeftShoulder, nuitrack.JointType.LeftElbow},
    {nuitrack.JointType.LeftElbow, nuitrack.JointType.LeftWrist},
    {nuitrack.JointType.LeftWrist, nuitrack.JointType.LeftHand},
    {nuitrack.JointType.RightShoulder, nuitrack.JointType.RightElbow},
    {nuitrack.JointType.RightElbow, nuitrack.JointType.RightWrist},
    {nuitrack.JointType.RightWrist, nuitrack.JointType.RightHand},
    {nuitrack.JointType.LeftHip, nuitrack.JointType.LeftKnee},
    {nuitrack.JointType.LeftKnee, nuitrack.JointType.LeftAnkle},
    {nuitrack.JointType.RightHip, nuitrack.JointType.RightKnee},
    {nuitrack.JointType.RightKnee, nuitrack.JointType.RightAnkle}
    };
    }

  8. すぐに使える接続部分を含む配列と、すぐに使える関節が含まれた辞書を作成します (key が関節の種類、value が生成されたオブジェクト)。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    {nuitrack.JointType.RightKnee, nuitrack.JointType.RightAnkle}
    };
    GameObject[] connections;
    Dictionary<nuitrack.JointType, GameObject> joints;
    }

  9. Start で、CreateSkeletonParts メソッドを呼び出します。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    Dictionary<nuitrack.JointType, GameObject> joints;
    void Start()
    {
    CreateSkeletonParts();
    }
    }

  10. CreateSkeletonParts メソッドで、すべての関節と結合部分を作成します。関節が生成され、オフにされ、関節一覧に追加されます。すべての接続部分に対して、同じことを行います。スケルトンが検出されると、関節と接続部分が "オンに切り替え"られます。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    CreateSkeletonParts();
    }
    void CreateSkeletonParts()
    {
    joints = new Dictionary<nuitrack.JointType, GameObject>();
    for (int i = 0; i < jointsInfo.Length; i++)
    {
    if (jointPrefab != null)
    {
    GameObject joint = Instantiate(jointPrefab, transform, true);
    joint.SetActive(false);
    joints.Add(jointsInfo[i], joint);
    }
    }
    connections = new GameObject[connectionsInfo.GetLength(0)];
    for (int i = 0; i < connections.Length; i++)
    {
    if (connectionPrefab != null)
    {
    GameObject connection = Instantiate(connectionPrefab, transform, true);
    connection.SetActive(false);
    connections[i] = connection;
    }
    }
    }
    }

  11. Nuitrack からのスケルトンを処理するための新しいメソッド ProcessSkeleton を作成します。0値を確認します (スケルトンが検出されていない状況では、メソッドの他の部分が実行できません)。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    connections[i] = connection;
    }
    }
    }
    public void ProcessSkeleton(nuitrack.Skeleton skeleton)
    {
    if (skeleton == null)
    return;
    }
    }

  12. 関節のループ処理を行います。特定の関節が検出された (信頼度 > 0.5) 場合に表示され、2D での位置が設定されます (Nuitrack から取得した射影座標を使用)。検出されない場合は、表示されません。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    publicvoid ProcessSkeleton(nuitrack.Skeleton skeleton)
    {
    return;
    for (int i = 0; i < jointsInfo.Length; i++)
    {
    nuitrack.Joint j = skeleton.GetJoint(jointsInfo[i]);
    if (j.Confidence > 0.5f)
    {
    joints[jointsInfo[i]].SetActive(true);
    // Bring proj coordinates from Nuitrack into accordance with screen coordinates
    joints[jointsInfo[i]].transform.position = new Vector2(j.Proj.X * Screen.width, Screen.height - j.Proj.Y * Screen.height);
    }
    else
    {
    joints[jointsInfo[i]].SetActive(false);
    }
    }
    }
    }
    注意
    現段階では、信頼度には、0 (Nuitrack thinks that this isn't a joint) と 0.75 (関節) の2つの値しかありません。
  13. 接続部分のループ処理を行います。接続部分を作成するために必要な関節モデルが両方表示されている場合 (最初と最後の関節)、接続を有効にしてください。接続部分の位置は、関連する関節モデルの位置に基づいて設定します。 接続部分を回転するには、最初と最後の関節の位置の違いを利用します。接続部分のサイズを計算します。

    • 最初と最後の関節の距離の違いを割り出します。
    • サイズを設定します。Vector3 (distance, 1, 1)

    接続部分を作成するために必要な関節モデルのいずれかが表示されない場合、接続部分も表示されません。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    public void ProcessSkeleton(nuitrack.Skeleton skeleton)
    {
    joints[jointsInfo[i]].SetActive(false);
    }
    }
    for (int i = 0; i < connectionsInfo.GetLength(0); i++)
    {
    GameObject startJoint = joints[connectionsInfo[i, 0]];
    GameObject endJoint = joints[connectionsInfo[i, 1]];
    if (startJoint.activeSelf && endJoint.activeSelf)
    {
    connections[i].SetActive(true);
    connections[i].transform.position = startJoint.transform.position;
    connections[i].transform.right = endJoint.transform.position - startJoint.transform.position;
    float distance = Vector3.Distance(endJoint.transform.position, startJoint.transform.position);
    connections[i].transform.localScale = new Vector3(distance, 1f, 1f);
    }
    else
    {
    connections[i].SetActive(false);
    }
    }
    }
    }

  14. Update で autoProcessing が true に設定されている場合、ProcessSkeleton を呼び出し、CurrentUserTracker.CurrentSkeleton パラメーターを渡します。

    public class SimpleSkeletonAvatar :MonoBehaviour
    {
    connections[i] = connection;
    }
    }
    }
    void Update()
    {
    if (autoProcessing)
    ProcessSkeleton(CurrentUserTracker.CurrentSkeleton);
    }
    public void ProcessSkeleton(nuitrack.Skeleton skeleton)

  15. 新しいキャンバス を作成し、SkeletonsCanvas と名前を付けます。[Sort Order]1 に設定することで、スケルトンが ColorFrameCanvas 上に表示されるよう設定します。

    Urgb_7.png

  16. SkeletonsCanvas (Create Empty) に子オブジェクトを作成し、Simple Skeleton Avatar と名前を付けます。このオブジェクトを使用して、スケルトンの表示に使用します。SimpleSkeletonAvatar スクリプトをこのオブジェクトにドラッグ アンド ドロップします。
  17. jointUI プレハブ (Tutorials/RGBandSkeletons/Prefabs) を Joint Prefab フィールドにドラッグ アンド ドラッグします。ConnectionUI プレハブ (同じフォルダー内) を Connection Prefab フィールドにドラッグ アンド ドロップします。

    Urgb_8.png

  18. プロジェクトを実行します。この時点で、センサーからの RGB イメージ上に、2D のスケルトンが表示されるようになります。しかし、複数のユーザーが存在する場合、すべてのユーザーのスケルトンが表示されているわけではありません。他のユーザーのスケルトンが表示されないのは不公平ですので、次の作業を行います。
skeleton2.gif

複数のスケルトンを表示

  1. 新しいスクリプトを SkeletonController として作成します。このスクリプトでは、複数のスケルトンのトラッキングと表示方法を紹介します。
  2. nuitrack 名前空間を追加します。SkeletonCount public 変数を作成し、範囲を 0 から 6 に設定します(トラッキングを行うスケルトンの数)。SimpleSkeletonAvatar フィールドを作成します (作成した SkeletonAvatar モデルのため)。SkeletonAvatars (表示されるスケルトン) の一覧を作成します。

    using nuitrack;
    using UnityEngine;
    using System.Collections.Generic;
    public class SkeletonController :MonoBehaviour
    {
    [Range(0, 6)]
    public int skeletonCount = 6;
    [SerializeField] SimpleSkeletonAvatar skeletonAvatar;
    List<SimpleSkeletonAvatar> avatars = new List<SimpleSkeletonAvatar>();
    }
    注意
    範囲を 0 から 6 に設定しますが、これは、Nuitrack がトラッキング可能なスケルトンの最大数が 6 だからです。
  3. Start で、シーンにスケルトンを作成し、SimpleSkeletonAvatar コンポーネントを各スケルトンンから取得し、autoProcessing を false に設定します (複数のスケルトンを処理するため)。スケルトンを、アバター一覧の avatars.Add(simpleSkeleton) に追加します。トラッキングを行うスケルトンの数を Nuitrack に渡します。Unity エディターで、スケルトンの数を必要に応じて設定できます。

    public class SkeletonController :MonoBehaviour
    {
    ...
    void Start()
    {
    for (int i = 0; i < skeletonCount; i++)
    {
    GameObject newAvatar = Instantiate(skeletonAvatar.gameObject, transform, true);
    SimpleSkeletonAvatar simpleSkeleton = newAvatar.GetComponent<SimpleSkeletonAvatar>();
    simpleSkeleton.autoProcessing = false;
    avatars.Add(simpleSkeleton);
    }
    NuitrackManager.SkeletonTracker.SetNumActiveUsers(skeletonCount);
    }
    }

  4. 取得したスケルトンに関するすべての情報 (skeletonData) を受け取る OnSkeletonUpdate メソッドを作成します。Unity で追跡を行うスケルトンの数として指定した数に応じて、生成されたすべてのスケルトンのループ処理を行います。Nuitrack から、スケルトンのアバターとして取得したスケルトンがある場合にのみ、処理され表示されます。

    public class SkeletonController :MonoBehaviour
    {
    NuitrackManager.SkeletonTracker.SetNumActiveUsers(skeletonCount);
    }
    void OnSkeletonUpdate(SkeletonData skeletonData)
    {
    for (int i = 0; i < avatars.Count; i++)
    {
    if (i < skeletonData.Skeletons.Length)
    {
    avatars[i].gameObject.SetActive(true);
    avatars[i].ProcessSkeleton(skeletonData.Skeletons[i]);
    }
    else
    {
    avatars[i].gameObject.SetActive(false);
    }
    }
    }
    }

  5. OnEnable メソッドでは、それぞれのフレームに対して、スケルトンが更新される度に呼び出される OnSkeletonUpdateEvent を登録します。スケルトン情報を更新します。

    public class SkeletonController :MonoBehaviour
    {
    List<SimpleSkeletonAvatar> avatars = new List<SimpleSkeletonAvatar>();
    void OnEnable()
    {
    NuitrackManager.SkeletonTracker.OnSkeletonUpdateEvent += OnSkeletonUpdate;
    }
    void Start()

  6. SimpleSkeletonAvatar プレハブを保存後に、シーンから削除します。
  7. SkeletonController スクリプトを SkeletonsCanvas に追加します。
  8. スライダーを使用して、トラッキングを行うスケルトンの数を設定します。

    Urgb_10.png

  9. Simple Skeleton Avatar プレハブを、SkeletonController スクリプトの Skeleton Avatar フィールドに追加します。

    Urgb_11.jpg

  10. プロジェクトを実行します。これで、複数のユーザーがトラッキングされ、RGB イメージに表示されます。おめでとうございます!
Urgb_1.gif