Dec 6

Written by: Soul Solutions
Tuesday, 6 December 2011 

Since the update to Beta2 of the Kinect for Windows SDK all our Kinect samples are out of date so I thought I’d update them to the latest and greatest and post them all here.

kinectcode1

To begin with I want a nice clean framework that is both easy for the first time Kinect coder to understand but also makes use of the new abilities of the Kinect SDK. What I’ve come up with achieves the following 3 goals:

  1. Support all combinations of Kinects be attached/detached/powered off/multiple.
  2. Exposes a single Kinect Runtime as a property called Nui (It is null if there isn’t a Kinect attached).
  3. Obvious location to wire up event model (and remove events if Kinect is removed)

This is meant to be a good place to start with simple examples with all the code clear in the one file.

    public partial class MainWindow : Window
    {
        private Runtime nui;
        public Runtime Nui
        {
            get { return nui; }
            set
            {
                if (nui != null)
                {
                    //dispose all events and unintialise

                    nui.Uninitialize();
                }
                nui = value;

                if (nui != null)
                {
                    //wire up events and initialise

                }

            }
        }

        public MainWindow()
        {
            InitializeComponent();
            Closed += WindowClosed;
            Loaded += WindowLoaded;
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            Runtime.Kinects.StatusChanged += KinectsStatusChanged;
            Nui = Runtime.Kinects.Where(k => k.Status == KinectStatus.Connected).FirstOrDefault();
        }

        void KinectsStatusChanged(object sender, StatusChangedEventArgs e)
        {
            //ignore the case where a 2nd kinect is connected while we are using the 1st kinect.
            if (Nui != null && Nui.InstanceName != e.KinectRuntime.InstanceName) return;

            Nui = Runtime.Kinects.Where(k => k.Status == KinectStatus.Connected).FirstOrDefault();
        }

        void WindowClosed(object sender, EventArgs e)
        {
            Runtime.Kinects.StatusChanged -= KinectsStatusChanged;
            Nui = null;
        }

    }

Here is our classic first demo from our talks where we wire up the video, tilt slider, depth and skeleton using this new framework.

WPF XAML:

<Window x:Class="NuiExample1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:Skeleton="clr-namespace:SoulSolutions.Kinect.Controls.Skeleton;assembly=SoulSolutions.Kinect.Controls.Skeleton" 
        Title="Kinect Nui Example 1" WindowStartupLocation="CenterScreen" SizeToContent="WidthAndHeight">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Image x:Name="colourImage" />
        <Image x:Name="depthImage" Grid.Column="1" />
        <Skeleton:SkeletonControl x:Name="skeleton" Grid.Row="1" />
        <StackPanel Grid.Column="1" Grid.Row="1" Orientation="Horizontal">
            <Slider x:Name="tiltSlider" Orientation="Vertical" Margin="40" Maximum="27" Minimum="-27" SmallChange="1" TickPlacement="TopLeft" Interval="1" IsSnapToTickEnabled="True" />
            <Button x:Name="tiltButton" Content="Set Tilt" Height="30" Width="200" Click="TiltButtonClick" />
        </StackPanel>
    </Grid>
</Window>

C#:

using System;
using System.Linq;
using System.Windows;
using Coding4Fun.Kinect.Wpf;
using Microsoft.Research.Kinect.Nui;

namespace NuiExample1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private InteropBitmapHelper imageHelper;

        private Runtime nui;
        public Runtime Nui
        {
            get { return nui; }
            set
            {
                if (nui != null)
                {
                    //dispose all events and unintialise
                    nui.VideoFrameReady -= NuiVideoFrameReady;
                    nui.DepthFrameReady -= NuiDepthFrameReady;
                    nui.SkeletonFrameReady -= NuiSkeletonFrameReady;

                    nui.Uninitialize();
                }
                nui = value;

                if (nui != null)
                {
                    //wire up events and initialise

                    //step 1 events
                    nui.VideoFrameReady += NuiVideoFrameReady;
                    nui.DepthFrameReady += NuiDepthFrameReady;
                    nui.SkeletonFrameReady += NuiSkeletonFrameReady;

                    //step 2 initialise
                    nui.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseDepth | RuntimeOptions.UseSkeletalTracking);

                    //step 3 open streams / settings
                    nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution1280x1024, ImageType.Color);
                    nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution640x480, ImageType.Depth);

                    //Must set to true and set after call to Initialize
                    nui.SkeletonEngine.TransformSmooth = true;

                    //Use to transform and reduce jitter
                    var parameters = new TransformSmoothParameters
                                         {
                                             Smoothing = 0.75f,
                                             Correction = 0.0f,
                                             Prediction = 0.0f,
                                             JitterRadius = 0.05f,
                                             MaxDeviationRadius = 0.04f
                                         };

                    nui.SkeletonEngine.SmoothParameters = parameters;

                }

            }
        }

        void NuiSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            SkeletonFrame allSkeletons = e.SkeletonFrame;

            if (e.SkeletonFrame == null) return;

            //get the first tracked skeleton
            var skeletonData = (from s in allSkeletons.Skeletons
                                where s.TrackingState == SkeletonTrackingState.Tracked
                                select s).FirstOrDefault();

            if (skeletonData == null) return;

            skeleton.SkeletonData = skeletonData;

        }

        void NuiDepthFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            //Use Coding4Fun extension method on ImageFrame class for Depth
            depthImage.Source = e.ImageFrame.ToBitmapSource();
        }

        void NuiVideoFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            PlanarImage planarImage = e.ImageFrame.Image;

            //An interopBitmap is a WPF construct that enables resetting the Bits of the image.
            //This is more efficient than doing a BitmapSource.Create call every frame.
            if (imageHelper == null)
            {
                imageHelper = new InteropBitmapHelper(planarImage.Width, planarImage.Height, planarImage.Bits);
                colourImage.Source = imageHelper.InteropBitmap;
            }
            else
            {
                imageHelper.UpdateBits(planarImage.Bits);
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            Closed += WindowClosed;
            Loaded += WindowLoaded;
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            Runtime.Kinects.StatusChanged += KinectsStatusChanged;
            Nui = Runtime.Kinects.Where(k => k.Status == KinectStatus.Connected).FirstOrDefault();
        }

        void KinectsStatusChanged(object sender, StatusChangedEventArgs e)
        {
            //ignore the case where a 2nd kinect is connected while we are using the 1st kinect.
            if (Nui != null && Nui.InstanceName != e.KinectRuntime.InstanceName) return;

            Nui = Runtime.Kinects.Where(k => k.Status == KinectStatus.Connected).FirstOrDefault();
        }

        void WindowClosed(object sender, EventArgs e)
        {
            Runtime.Kinects.StatusChanged -= KinectsStatusChanged;
            Nui = null;
        }

        private void TiltButtonClick(object sender, RoutedEventArgs e)
        {
            //Set angle to slider1 value
            Nui.NuiCamera.ElevationAngle = (int) tiltSlider.Value;
        }
    }
}

Download this full sample here.

What do you think? What kinect code are you looking for? You can catch me on http://twitter.com/soulsolutions

Tags:

2 comment(s) so far...

Re: Kinect beta2 clean wpf code and getting started

Hi.
I got an error from line 122 , the defination of KinectsStatusChanged event from StatusChangedEventArgs parameter .
what its namespace ?

By Hamid on   Wednesday, 14 December 2011

setting the tilt by slider without clicking on the set button

Hi,

I was trying to set the ElevationAngle with just slider. Imagine that there is no Set Tilt button. I couldnt do it. When I do it, It gives an error and closes the application. If you can send me the solution, I will be appreciated.

By Samet on   Thursday, 16 February 2012

Your name:
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
Enter the code shown above in the box below
Add Comment   Cancel 
Copyright © 2002-2009 Soul Solutions Pty Ltd. | Login