Getting started with Microsoft Kinect SDK - The Full Skeleton
Written by Mike James   
Monday, 23 January 2012
Article Index
Getting started with Microsoft Kinect SDK - The Full Skeleton
Drawing the body
Joining the Bones

 

Now to display the image we simply need to write the code for the FrameReady event handler. However we are going to want to modify the video returned from the camera by adding a small cross at the position of the players head. To do this we will store the video frame in a global variable and allow the SkeletonFrameReady event handler to actually do the displaying of the video frame.

So the FrameReady method is:

void FrameReady(object sender, 
ImageFrameReadyEventArgs e)
{
videoimage= e.ImageFrame.Image;
}

The videoimage variable is just a global PlanarImage:

PlanarImage videoimage;

Now we need to define the SkeletonFrameReady event handler to simply show the video.  It has to check first to make sure that an image has been stored in the videoimage:

void SkeletonFrameReady(object sender,
 SkeletonFrameReadyEventArgs e)
{
if (videoimage.Bits == null) return;

We can't display a PlanarImage in, say, a PictureBox unless we first convert it to an Image object. How to do this was covered in Part 1 of this series, so the method that does the job PImageToBitmap is simply quoted:

Bitmap PImageToBitmap(PlanarImage PImage)
{
Bitmap bmap = new Bitmap(
PImage.Width,
PImage.Height,
PixelFormat.Format32bppRgb);
BitmapData bmapdata = bmap.LockBits(
new Rectangle(0, 0, PImage.Width,
PImage.Height),
ImageLockMode.WriteOnly,
bmap.PixelFormat);
IntPtr ptr = bmapdata.Scan0;
Marshal.Copy(PImage.Bits,
0,
ptr,
PImage.Width *
PImage.BytesPerPixel *
PImage.Height);
bmap.UnlockBits(bmapdata);
return bmap;
}

For an explanation of how this works see Part 1 - but essentially what it does it take a PlanarImage and returns an equivalent Bitmap object.

Drawing the Body

Now that we have the video frame as a bitmap we can start to draw the skeleton on it. First we need a Graphics object:

Graphics g = Graphics.FromImage(bmap);

Our next problem is to find the positions of each of the joints that makeup the "body" of the skeleton.  If you look at the diagram that gives the names of the joints you can see that we need the positions of the Head, Shoulder Center, Spine and Hip Center.

 

bodyparts

 

The positions of these four joints gives us the line of the torso.

First we need the head:

Vector location = 
data.Joints[JointID.Head].Position;

and we need to convert its position to pixel co-ordinates as described in the previous article:

float xd, yd;
nui.SkeletonEngine.SkeletonToDepthImage(
location, out xd, out yd);
xd = Math.Max(0, Math.Min(xd * 320, 320));
yd = Math.Max(0, Math.Min(yd * 240, 240));
int x, y;
ImageViewArea iv = new ImageViewArea();
nui.NuiCamera.
GetColorPixelCoordinatesFromDepthPixel(
ImageResolution.Resolution640x480,
iv,
(int)xd, (int)yd,
(short)0, out x, out y);

So far so good but now we need the position of the ShoulderCenter joint and this means repeating everything we have just done.

We clearly need to package this code into a helper method:

Point GetJoint(JointID j,
SkeletonData data)
{
Vector location=data.Joints[j].Position;
float xd, yd;
nui.SkeletonEngine.SkeletonToDepthImage(
location, out xd, out yd);
xd = Math.Max(0, Math.Min(xd * 320, 320));
yd = Math.Max(0, Math.Min(yd * 240, 240));
int x, y;
ImageViewArea iv = new ImageViewArea();
nui.NuiCamera.
GetColorPixelCoordinatesFromDepthPixel(
ImageResolution.Resolution640x480,
iv,
(int)xd, (int)yd,
(short)0, out x, out y);
return new Point(x, y);
}

The only difference is that we are now returning the co-ordinates as a Point object.

Now we have the helper we can plot the line between the head and the ShoulerCenter joints:

Point p1=GetJoint(JointID.Head,data);
Point p2=GetJoint(JointID.ShoulderCenter,
 data);
g.DrawLine(Pens.Red, p1, p2);

Now if you run the program you would see a single red line drawn from the head to the shoulder. This is the first part of our skeleton drawing.



Last Updated ( Monday, 06 February 2012 )