Kinect for Windows Sensor + SDK – その6 Skeleton Tracking


さて、漸くSkeleton Trachingを解説します。これもかなり興味深い機能です。

Skeletonの機能をおさらいしておきましょう。Kinectセンサーで取得できる人体の部位は、以下の20点です。

  • 頭 - Head
  • 肩中央 - ShoulderCenter
  • 左肩 - ShoulderLeft
  • 右肩 - ShoulderRight
  • 鳩尾(背骨) - Spine
  • 左肘 - ElbowLeft
  • 右肘 - ElbowRight
  • 左手首 - WristLeft
  • 右手首 - WristRight
  • 左手のひら - HandLeft
  • 右手のひら - HandRight
  • おしり中心 - HipCenter
  • おしりの左 - HipLeft
  • おしりの右 - HipRight
  • 左膝 - KneeLeft
  • 右膝 - KneeRight
  • 左足首 - AnkleLeft
  • 右足首 - AnkleRight
  • 左足 - FootLeft
  • 右足 - FootRight

※右側に書いた英文字名は、JointType列挙型で定義されたものを併記しています。

これらの部位の3次元位置座標の獲得が可能です。位置座標座標は右手系の座標です。センサー正面に立って、水平方向がx軸で右が正方向、垂直方向がy軸で上方向が正方向、センサーの正面方向がz軸でセンサー正面方向に向かって正方向になります。単位はメータです。例えば、センサーに対して、2m離れていて、センサー正面の軸より上に0.7m、右に0.5mの位置に右肩があれば、(x,y,z)=(0.5, 0.7, 2.0) という値になります。

※右手系座標は、通常の物理学で3次元を表すときの座標系と同一です。

Skeleton情報は深度情報を元に算出されているので、Skeletonを取得する為には、DepthStreamも同時に機能させる必要があります。
前のポストで説明しましたが、Windows版KinectセンサーにはXbox版センサーの0.8~4.0m計測に加えて、0.4mの至近距離から深度情報をセンスできるNearモードが用意されています。このモードによって、検出できるSkeletonの種類が変わります。それぞれ、

  • Defaultモードの場合
    前述のリストに挙げたJointType全てをセンス可能。最大2人までのSkeleton情報をトラッキング可能。
  • Nearモードの場合
    このモードの場合は、Spineのみをセンス可能です。人数は最大6人までトラッキング可能です。

です。Skeleton全体の塊を取得する方法は後述するとして、塊を取得後の各Jointを取り出すコードを例示すると、

        foreach (var skeleton in skeletons)    ← 各人毎のJoint群のデータの塊
        {
            int trakingId = skeleton.TrackingId;
            FrameEdges frameEdges = skeleton.ClippedEdges;
            if (skeleton.TrackingState == SkeletonTrackingState.PositionOnly)
            {
                // Nearモードの場合、この条件
                SkeletonPoint skeletonPosition = skeleton.Position;
            }
            else if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
            {
                // Defaultモードの場合、この条件
                SkeletonPoint skeletonPosition = skeleton.Position;
                foreach (Joint joint in skeleton.Joints)    ← 人の各部位
                {
                    JointType jointType = joint.JointType;
                    SkeletonPoint position = joint.Position;

                    if (joint.TrackingState == JointTrackingState.Inferred)
                    {
                        // 推測の場合
                    }
                    else if (joint.TrackingState == JointTrackingState.Tracked)
                    {
                        // 計測値
                    }
                    else
                    {
                        // 計測できていない
                    }
                }
            }
        }

 コードを見れば、ほぼ想像がつくでしょう。構造は、各人ごとのSkeletons情報をforeachでまわせば、各人のJoint(体の各部位)情報を取り出すことが出来、skeletonのJointsに各部位が格納されるという構成になっています。DepthStreamに対し、Nearモードを設定した場合は、skeletonのTrackingStateがPositionOnlyになっていて、Positionプロパティに体の中心(鳩尾というか丹田というか)の三次元の位置が格納されています。TrackingStateが、Trackedの場合、これはDepthStreamに対しDefaultモードを設定した場合ですが、この場合にJointsに各人の体の部位を格納するJointsからデータを取り出せます。
Jointsの各要素にも、TrackingStateが格納されていて、TrackedとInferredの場合には意味がある値が入っています。

Kinectセンサーの前に立ったとき、その距離によっては全身がKinectセンサーに映るとは限りません。足が切れることが多いです。そんな場合でも、Kinectセンサー+SDKは足の場所を推測してくれます。推測した場合には、Inferredになるわけです。

さて、上のコードのskeletonsを取得する方法ですが、SDKでは同期的、非同期的に値を取得するという二種類の方法が存在します。ここでは非同期的な取得について説明します。

先ず、アプリの初期化コードで、DepthFrameとSkeletonFrameを初期化します。

    mySensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
    mySensor.SkeletonStream.Enable();
    mySensor.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(mySensor_SkeletonFrameReady);

mySensorはKinectSensor型の暮らすメンバー変数を定義しているものとします。これで、DepthStreamとSkeletonStreamがEnableになり、Skeleton情報のTrackingの準備完了です。

更に、mySensor_SkeletonFrameReadyの中身で、

        void mySensor_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            using (var stream = e.OpenSkeletonFrame())
            {
                if (stream != null)
                {
                    Skeleton[] skeletons = new Skeleton[stream.SkeletonArrayLength];
                    stream.CopySkeletonDataTo(skeletons);

このハンドラが、Skeleton情報の準備が整うとコールされて、イベント引数eからSkeletonFrame変数を取り出すことができます。OpenSekeletonFrame()メソッドの戻り値はnullの場合もあるので、nullチェックは必ずやってください。

さぁ、これで必要なコードは全て説明しました。jointには三次元の位置情報が格納されているので、例えば、WPFのViewport3Dを使って三次元化(コードは煩雑なので省略)して表示すると、

こんな感じ。左側の小さな立方体が、Jointに格納されている三次元座標データを適用したものです。ちなみにこれはDepthStreamをDefaultモードにした時。対比としてDepthFrameで取得した画像を右側に表示しています。(左はチャックさん、右は永野マン)

そして、Nearモードの場合はこんな感じ。ちょっと小さくてわかりにくいですが、丹田(鳩尾?)のところに小さな立方体が表示されているのがわかるかな?

Comments (0)

Skip to main content