2012年8月30日木曜日

KINECTサンプル(C#) SkeletonTracking for SDK1.5

今回は骨格追跡のサンプルになります。
骨格追跡は座標系がRGBカメラやDepthカメラと全然違うので(厳密に言うとDepthもですけど)色々やらなければいけないことが増えています。

サンプルを実行すると、認識した人物の各関節部分に円を表示します。
認識できている部位は緑色で、推定している部位は赤色で表示します。
それから頭部の座標をSkeletonFrameの場合と、Depthデータとマッピングした時の場合を表示しています。

MainWindow.xaml
------------------------------------------------------------------------------
<Window x:Class="KinectSkeletonforSDK15.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="519" Width="1175">
    <Grid Width="1141">
        <Canvas Height="480" HorizontalAlignment="Left" Name="canvas1" VerticalAlignment="Top" Width="640" />
        <TextBox Height="40" HorizontalAlignment="Left" Margin="743,46,0,0" Name="skelx" VerticalAlignment="Top" Width="120" FontSize="20" />
        <TextBox Height="40" HorizontalAlignment="Left" Margin="869,46,0,0" Name="skely" VerticalAlignment="Top" Width="120" FontSize="20" />
        <TextBox Height="40" HorizontalAlignment="Left" Margin="995,46,0,0" Name="skelz" VerticalAlignment="Top" Width="120" FontSize="20" />
        <TextBox Height="40" HorizontalAlignment="Left" Margin="743,95,0,0" Name="depthx" VerticalAlignment="Top" Width="120" FontSize="20" />
        <TextBox Height="40" HorizontalAlignment="Left" Margin="869,95,0,0" Name="depthy" VerticalAlignment="Top" Width="120" FontSize="20" />
        <TextBox Height="40" HorizontalAlignment="Left" Margin="995,95,0,0" Name="depthz" VerticalAlignment="Top" Width="120" FontSize="20" />
        <Label Content="Skeleton" Height="40" HorizontalAlignment="Left" Margin="646,46,0,0" Name="label1" VerticalAlignment="Top" FontSize="20" />
        <Label Content="Depth" FontSize="20" Height="40" HorizontalAlignment="Left" Margin="671,95,0,0" Name="label2" VerticalAlignment="Top" />
        <Label Content="Z" FontSize="20" Height="40" HorizontalAlignment="Left" Margin="995,0,0,0" Name="label3" VerticalAlignment="Top" Width="120" HorizontalContentAlignment="Center" />
        <Label Content="X" FontSize="20" Height="40" HorizontalAlignment="Left" Margin="743,0,0,0" Name="label4" VerticalAlignment="Top" Width="120" HorizontalContentAlignment="Center" />
        <Label Content="Y" FontSize="20" Height="40" HorizontalAlignment="Left" Margin="869,0,0,0" Name="label5" VerticalAlignment="Top" Width="120" HorizontalContentAlignment="Center" />
    </Grid>
</Window>
------------------------------------------------------------------------------
今回はCanvasコントロールと座標データを表示するためのテキストボックスを配置しています。



MainWindow.xaml.cs
------------------------------------------------------------------------------
using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

using Microsoft.Kinect;

namespace KinectSkeletonforSDK15
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private KinectSensor kinect;

        /// <summary>
        ///
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
     
            //接続の確認
            if (KinectSensor.KinectSensors.Count == 0)
            {
                MessageBox.Show("KINECTがみつかりません。");
                Close();
            }

            kinect = KinectSensor.KinectSensors[0];

            //Depthカメラを有効にする
            kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
            //骨格追跡を有効にする
            kinect.SkeletonStream.Enable();

            //イベントの登録
            kinect.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(kinect_AllFramesReady);
         
            //KINECTのスタート
            kinect.Start();

        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void kinect_AllFramesReady(object sender, AllFramesReadyEventArgs e)
        {
         
            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
            {

                if (skeletonFrame != null)
                {
                    //キャンバスのクリア
                    canvas1.Children.Clear();

                    //Skeletonデータの取得
                    Skeleton[] skeletonData = new Skeleton[skeletonFrame.SkeletonArrayLength];
                    skeletonFrame.CopySkeletonDataTo(skeletonData);

                    //プレイヤーごとの描画
                    foreach (var skeleton in skeletonData)
                    {
                        if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
                        {
                            //骨格を描画
                            foreach (Joint joint in skeleton.Joints)
                            {
                                if (joint.TrackingState != JointTrackingState.NotTracked)
                                {
                                    //骨格の座標(頭)
                                    if (joint.JointType == JointType.Head)
                                    {
                                        skelx.Text = joint.Position.X.ToString();
                                        skely.Text = joint.Position.Y.ToString();
                                        skelz.Text = joint.Position.Z.ToString();
                                    }

                                    //座標の変換(DepthFrame 640*480)
                                    DepthImagePoint point = depthFrame.MapFromSkeletonPoint(joint.Position);

                                    //depth座標に変換後の座標(頭)
                                    if (joint.JointType == JointType.Head)
                                    {
                                        depthx.Text = point.X.ToString();
                                        depthy.Text = point.Y.ToString();
                                        depthz.Text = point.Depth.ToString();
                                    }

                                    //円の表示
                                    if (joint.TrackingState == JointTrackingState.Tracked)
                                    {
                                        canvas1.Children.Add(new Ellipse()
                                        {
                                            Margin = new Thickness(point.X, point.Y, 0, 0),
                                            Fill = new SolidColorBrush(Colors.Green),
                                            Width = 10,
                                            Height = 10
                                        });
                                    }

                                    else
                                    {
                                        canvas1.Children.Add(new Ellipse()
                                        {
                                            Margin = new Thickness(point.X, point.Y, 0, 0),
                                            Fill = new SolidColorBrush(Colors.Red),
                                            Width = 10,
                                            Height = 10
                                        });
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
------------------------------------------------------------------------------
フレームの更新イベントが変わっています。複数タイプのフレームを同期しながら処理する場合に「AllFramesReady」を利用します。今回はDepthフレームとSkeletonフレームを使っています。
骨格追跡を行うにはDepthフレーム、Skeletonフレームはどちらも必要です。

Skeletonフレームには認識できたプレイヤーの人数分のデータが構造体として格納されています。
Skeleton構造体には各関節(Joint)の座標データやトラッキングの状態などが格納されています。

プログラムでは1人ずつのSkeleton構造体を取得してから、Joint構造体の値(座標)を取得します。
Joint構造体の座標は3次元座標(KINECTを中心にした右手系座標)なので、平面に表示するために変換が必要になります。
今回はDepthフレームに合わせて2次元座標(Depthフレームのデータは左上を原点としています)に変換しています。(MapFromSkeletonPointメソッドを使っています。)
座標の違いはテキストボックスの値を確認してください。ちなみにKINECTまでの距離(Z座標)の単位はSkeletonはメートル(m)、Depthはミリメートル(mm)です。

変換した座標を元にCanvasコントロールに円を追加して各関節部分を表示しています。

各フレームの重ね合わせの方法がいくつかあるのでまたの機会にまとめてみたいと思います。


2 件のコメント:

  1. 89行目
    depthFrame.MapFromSkeletonPoint(joint.Position)
    が,古い形式であると警告が出てしまうのですが,対処方法ってありますか?

    返信削除
  2. 返答が遅くなりました。
    SDKのバージョンはいくつでしょうか?
    1.6からは記述方法が変わっています。

    DepthImagePoint point=kinect.CoordinateMapper.MapSkeletonPointToDepthPoint(joint.Position,DepthImageFormat.Resolution640x480Fps30)

    に変更すれば動くかと思います。

    返信削除