Kinect SDK 1 - The Full Skeleton
Written by Harry Fairhead   
Article Index
Kinect SDK 1 - The Full Skeleton
Drawing the Body
The Full Skeleton

So far we've covered the visual and depth inputs and started to look at skeletonization. In this part we get down to the bare bones of the skeleton and actually draw the whole thing.

 

Practical Windows Kinect in C#
Chapter List

  1. Introduction to Kinect
  2. Getting started with Microsoft Kinect SDK 1
  3. Using the Depth Sensor
  4. The Player Index
  5. Depth and Video Space
  6. Skeletons
  7. The Full Skeleton
  8. A 3D Point Cloud

 

While there is a very good example of how to draw a complete skeleton in the SDK it isn't explained in detail and, in common with most examples, it does things in clever and correct ways.

This makes it a piece of code to admire but only if you can work out what it is doing and how. In this article the objective is not only to create a full skeleton graphic but to make it perfectly clear how it is done.

Getting started with the full Skeleton

As explained in the previous chapter, the Kinect doesn't return a skeleton, just a set of joints that it has detected. It is entirely up to us what to do with this set of joints.

One of the problems with the example skeleton viewer is that at first look you might think that something clever was going on that relied on the way that the Kinect returns the data.

Let's try and draw a skeleton in a step-by-step way that shows how it all works. The instructions that follow just get you to the point where we can access the skeleton data as described in chapter 6. So while there is nothing new it at first it is included for completeness.

Start a new C# Windows Forms project.

Make sure you have loaded a reference to the Kinect DLL and add:

using Microsoft.Kinect;

and

using System.Runtime.InteropServices;
using System.Drawing.Imaging;

so that we can use the GDI and Bitmaps.

 

To the start of the program. In the Form's constructor we create a Runtime object so that we can use the Kinect:

public Form1()
{
InitializeComponent();
sensor = KinectSensor.KinectSensors[0];
}

The nui variable is global allowing us to get a the Kinect from anywhere in the program - not good design but simpler for an example.

KinectSensor sensor;

Next we have to setup the Kinect and this is more or less the same for each program, differing in only the facilities we are going to use. In this case we make use of all of  the data streams:

sensor.ColorStream.Enable(
ColorImageFormat.RgbResolution640x480Fps30);
sensor.DepthStream.Enable(
DepthImageFormat.Resolution320x240Fps30);
sensor.SkeletonStream.Enable();

The new AllFramesReady event can be used to trigger code when all of the frame types you hve requested are ready to be processed. So we can simply use a single event handler:

sensor.AllFramesReady += FramesReady;

Finally we can set the sensor running:

 sensor.Start();
}

 

The Kinect will now return video images from the video camera, depth data  and skeleton data.

Getting the Video

Now to display the image we simply need to write the code for the FramesReady event handler.

However we are going to want to modify the video returned from the camera by drawing a skeleton on it in the same position as the user. For this reason we need to convert the video to a Bitmap and use the GDI graphics object to draw on it.

void FramesReady(object sender,
AllFramesReadyEventArgs e)
{
ColorImageFrame VFrame =
e.OpenColorImageFrame();
if (VFrame == null) return;
byte[] pixelS =
new byte[VFrame.PixelDataLength];
Bitmap bmap = ImageToBitmap(VFrame);

Now we have the bitmap form of the video frame in bmap ready to have the skeleton draw. The ImageToBitmap function was described in chapter 2 but for completeness is is listed below:

Bitmap ImageToBitmap(
ColorImageFrame Image)
{
byte[] pixeldata =
new byte[Image.PixelDataLength];
Image.CopyPixelDataTo(pixeldata);
Bitmap bmap = new Bitmap(
Image.Width,
Image.Height,
PixelFormat.Format32bppRgb);
BitmapData bmapdata = bmap.LockBits(
new Rectangle(0, 0,
Image.Width, Image.Height),
  ImageLockMode.WriteOnly,
bmap.PixelFormat);
IntPtr ptr = bmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr,
 Image.PixelDataLength);
bmap.UnlockBits(bmapdata);
return bmap;
}