ブログ@kaorun55

HoloLensやKinectなどのDepthセンサーを中心に書いています。

Kinect for Windows SDK でスケルトンを表示する(C# + WPF)


画像系の最後として、スケルトンを表示してみましょう。
ケルトンの表示方法もBeta2に比べて簡単になっています。

コード

それではコードを見てみましょう。全体のコードはこちらにあります。

XAML
<Window x:Class="SkeletonSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Skeleton" Height="521" Width="663">
    <Grid>
        <Image Name="imageRgbCamera" Stretch="Uniform" />
        <Image Name="imageDepthCamera" Stretch="Uniform" Opacity="0.7" />
        <Canvas Name="canvas1" />
    </Grid>
</Window>

ケルトンのjointを配置するためのCanvasを一つ追加します。そのほかは、前回までと同じです。


CS
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using Coding4Fun.Kinect.Wpf;
using Microsoft.Kinect;

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

        public MainWindow()
        {
            InitializeComponent();

            try {
                if ( KinectSensor.KinectSensors.Count == 0 ) {
                    throw new Exception( "Kinectが接続されていません" );
                }

                // Kinectインスタンスを取得する
                kinect = KinectSensor.KinectSensors[0];

                // すべてのフレーム更新通知をもらう
                kinect.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>( kinect_AllFramesReady );

                // Color,Depth,Skeletonを有効にする
                kinect.ColorStream.Enable();
                kinect.DepthStream.Enable();
                kinect.SkeletonStream.Enable();

                // Kinectの動作を開始する
                kinect.Start();
            }
            catch ( Exception ex ) {
                MessageBox.Show( ex.Message );
                Close();
            }
        }

        // すべてのデータの更新通知を受け取る
        void kinect_AllFramesReady( object sender, AllFramesReadyEventArgs e )
        {
            imageRgbCamera.Source = e.OpenColorImageFrame().ToBitmapSource();
            imageDepthCamera.Source = e.OpenDepthImageFrame().ToBitmapSource();

            // 骨格位置の表示
            ShowSkeleton( e );
        }

        private void ShowSkeleton( AllFramesReadyEventArgs e )
        {
            // キャンバスをクリアする
            canvas1.Children.Clear();

            // スケルトンフレームを取得する
            SkeletonFrame skeletonFrame = e.OpenSkeletonFrame();
            if ( skeletonFrame != null ) {
                // スケルトンデータを取得する
                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 ) {
                            // 骨格の座標をカラー座標に変換する
                            ColorImagePoint point = kinect.MapSkeletonPointToColor( joint.Position, kinect.ColorStream.Format );

                            // 円を書く
                            canvas1.Children.Add( new Ellipse()
                            {
                                Margin = new Thickness( point.X, point.Y, 0, 0 ),
                                Fill = new SolidColorBrush( Colors.Black ),
                                Width = 20,
                                Height = 20,
                            } );
                        }
                    }
                }
            }
        }
    }
}

すこし変えてみました。
フレーム更新イベントをAllFramesReadyを使ってひとまとめにしました。AllFramesReadyはRGB,Depth,Skeletonをまとめて通知してくれるイベントです。

// すべてのフレーム更新通知をもらう
kinect.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>( kinect_AllFramesReady );


kinect_AllFramesReadyでは、RGB,Depth,Skeletonをそれぞれ描画します。Skeletonは少し手間なので、メソッドに切り出しました。

// すべてのデータの更新通知を受け取る
void kinect_AllFramesReady( object sender, AllFramesReadyEventArgs e )
{
    imageRgbCamera.Source = e.OpenColorImageFrame().ToBitmapSource();
    imageDepthCamera.Source = e.OpenDepthImageFrame().ToBitmapSource();

    // 骨格位置の表示
    ShowSkeleton( e );
}


ケルトンの描画です。

  1. 最初にOpenSkeletonFrameを取得します。
  2. 続いてSkeletonを取得します。Skeletonの数はSkeletonFrame.SkeletonArrayLengthで取得できるので、容量分の配列を確保しSkeletonFrame.CopySkeletonDataToで取得します。
  3. あとはSkeletonおよびJointで回して描画します。Skeletonの座標をRGBの座標に変換するために、KinectSensor.MapSkeletonPointToColorを使っています。
  4. KinectSensor.MapSkeletonPointToColorで取得された、SkeletonのRGB座標を利用して、Canvasに円を書いています
private void ShowSkeleton( AllFramesReadyEventArgs e )
{
    // キャンバスをクリアする
    canvas1.Children.Clear();

    // スケルトンフレームを取得する
    SkeletonFrame skeletonFrame = e.OpenSkeletonFrame();
    if ( skeletonFrame != null ) {
        // スケルトンデータを取得する
        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 ) {
                    // 骨格の座標をカラー座標に変換する
                    ColorImagePoint point = kinect.MapSkeletonPointToColor( joint.Position, kinect.ColorStream.Format );

                    // 円を書く
                    canvas1.Children.Add( new Ellipse()
                    {
                        Margin = new Thickness( point.X, point.Y, 0, 0 ),
                        Fill = new SolidColorBrush( Colors.Black ),
                        Width = 20,
                        Height = 20,
                    } );
                }
            }
        }
    }
}


このようにBeta2とそれほど変わらずに、すこし簡単になっているスケルトンでした。