Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Working with Depth Data (Beta 2 SDK)

Download

Right click “Save as…”

Embed code for this video

Copy the code above to embed our video on your website/blog.

Close

Video format

Note: These selections will fall back to the next best format depending upon browser capability.

Close

Update: Kinect for Window SDK v1 Quickstart Series now Available (Feb 1st)

Please use the newly updated Kinect for Windows SDK Quickstart series. The content below will only work with the Beta 2 version of the Kinect for Windows SDK.

 

 

This video covers the basics of using depth data from Kinect. You may find it easier to follow along by downloading the Kinect for Windows SDK Quickstarts samples and slides that have been updated for Beta 2 (Nov, 2011).

  • [00:43] Depth data overview
  • [04:56] Initializing the Kinect Runtime
  • [05:46] Using the depth data to create an image
  • [12:13] Using the PlayerIndex

Setup

The steps below assume you have setup your development environment as explained in the "Setting Up Your Development Environment" video.

 

Updates for Kinect for Windows SDK Beta 2 (Nov, 2011)

    • Creating the Kinect runtime uses the SetupKinect method used in the Setting Up Your Development Environment example.
    • Beta 2 includes a sample (Microsoft.Samples.Kinect.WpfViewers) set of user controls for visualizing data from the Color/RGB and Depth cameras that you can reuse in your application. The samples can be found at: "\Program Files\Microsoft SDKs\Kinect\v1.0 Beta2\Samples\KinectSDKSamples.zip" in the “Managed\KinectWpfViewers” solution.

 

Task Setup the Depth Camera event

Designing the UI

Add an image with Width=320 and Height=240 as shown below:

XAML

    <Image Height="240" HorizontalAlignment="Left" Margin="62,41,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="320" />

Setup the Depth Camera Event

Create an instance of the Kinect Runtime outside of the Window_Loaded event. Then, initialize the Runtime to use DepthAndPlayerIndex and UseSkeletalTracking. Finally register for DepthFrameReady event and open the Depth stream as shown below.

C#

//Kinect Runtime
Runtime nui = new Runtime(); 

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    //UseDepthAndPlayerIndex and UseSkeletalTracking
    nui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking);

    //register for event
    nui.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_DepthFrameReady);

    //DepthAndPlayerIndex ImageType
    nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, 
        ImageType.DepthAndPlayerIndex); 
}

void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
{
}

Visual Basic

Private nui As New Runtime

Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)

    'UseDepthAndPlayerIndex and UseSkeletalTracking
    nui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex Or RuntimeOptions.UseSkeletalTracking)

    'register for event
    AddHandler nui.DepthFrameReady, AddressOf nui_DepthFrameReady

    'DepthAndPlayerIndex ImageType
    nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex)

End Sub

Private Sub nui_DepthFrameReady(ByVal sender As Object, ByVal e As ImageFrameReadyEventArgs)

End Sub

Understanding the PlanarImage byte[] Array

The DepthFrameReady event returns an ImageFrame class that contains a PlanarImage.  That PlanarImage contains a byte[] array which contains the distance of each pixel. The array:

  • Starts at top left of image
  • Moves left to right, then top to bottom
  • Represents distance for pixel

Calculating Distance

Because there are 2 bytes per pixel (16 bits) representing the distance, you will need to use bitshift operators to get the distance for a particular pixel. The exact bitshifting depends on the ImageType you use

Depth Image Type
Bitshift second byte left by 8
Distance (0,0) = (int)(Bits[0] | Bits[1] << 8 );

DepthAndPlayerIndex Image Type
Bitshift first byte right by 3 (to remove player index), and second byte left by 5
Distance (0,0) =(int)(Bits[0] >> 3 | Bits[1] << 5);

The method below shows returning the distance in millimeters using the 2 bytes for a particular pixel.

C#

private int GetDistanceWithPlayerIndex(byte firstFrame, byte secondFrame)
{
  //offset by 3 in first byte to get value after player index 
  int distance = (int)(firstFrame >> 3 | secondFrame << 5);
  return distance;
}

Visual Basic

Private Function GetDistanceWithPlayerIndex(ByVal firstFrame As Byte, ByVal secondFrame As Byte) As Integer

    'offset by 3 in first byte to get value after player index 
    Dim distance As Integer = (CInt(firstFrame) >> 3 Or CInt(secondFrame) << 5)
    Return distance

End Function

The DepthAndPlayerIndex image type is offset by 3 since the first three bits represent the Player Index. The Player Index has up to six possible values.

  • 0 – No player
  • 1 – Skeleton 0
  • 2 – Skeleton 1
  • ...

Note: While depth data is available for multiple players, you can only have skeletal/joint data for a maximum of two players.

To get a player index, we'll use the following formula. Since the player index is only in the first byte, we do not need the pixels second byte.

C#

private static int GetPlayerIndex(byte firstFrame)
{
    //returns 0 = no player, 1 = 1st player, 2 = 2nd player...
    return (int)firstFrame & 7; 
}

Visual Basic

Private Shared Function GetPlayerIndex(ByVal firstFrame As Byte) As Integer

    'returns 0 = no player, 1 = 1st player, 2 = 2nd player...
    'bitwise & on firstFrame
    Return CInt(firstFrame) And 7

End Function

Using distance to color values

Now that we know the distance for a particular pixel using the formula above, we can loop through all of the bytes in the byte[] array and dynamically assign colors to pixels based on their distance.

To begin with, we'll build a colorFrame byte[] array using the Bgr (Blue, Green, Red) pixel format. What this means is that each pixel (ex: the pixel at 0,0) is represented by four bytes, one each for Blue, Green, Red, and an empty one (or transparency byte if you choose the Bgra format).

For example, the snippet below would color the pixel blue if the current distance for that pixel was < 900 millimeters from the Kinect. To set the color to blue, we set the first byte (BlueIndex) to the maximum value, 255.

C#

if (distance <= 900)
{
    //we are very close
    colorFrame[index + BlueIndex] = 255;
    colorFrame[index + GreenIndex] = 0;
    colorFrame[index + RedIndex] = 0;
}

Visual Basic

If distance <= 900 Then

    'we are very close
    colorFrame(index + BlueIndex) = 255
    colorFrame(index + GreenIndex) = 0
    colorFrame(index + RedIndex) = 0

End If

Similarly, we can use the code snippet below to check if the current pixel represents a Player Index. If it does, we'll set the maximum value for both green and red which produces a yellow/gold-like color.

C#

if (GetPlayerIndex(depthData[depthIndex]) > 0)
{
    //we are the farthest
    colorFrame[index + BlueIndex] = 0;
    colorFrame[index + GreenIndex] = 255;
    colorFrame[index + RedIndex] = 255;
}

Visual Basic

If GetPlayerIndex(depthData(depthIndex)) > 0 Then

    'we are the farthest
    colorFrame(index + BlueIndex) = 0
    colorFrame(index + GreenIndex) = 255
    colorFrame(index + RedIndex) = 255

End If

Setting a monochrome intensity

While the previous examples we hard-coded colors, you can also build a monochrome histogram that scales the color range (0-255) proportionally based on the distance range (850-4000).

To get a monochrome appearance, the intensity is applied equally to all three colors (blue, green, red).

C#

//equal coloring for monochromatic histogram
var intensity = CalculateIntensityFromDepth(distance);

colorFrame[index + BlueIndex] = intensity;
colorFrame[index + GreenIndex] = intensity;
colorFrame[index + RedIndex] = intensity;

const float MaxDepthDistance = 4000; // max value returned
const float MinDepthDistance = 850; // min value returned
const float MaxDepthDistanceOffset = MaxDepthDistance - MinDepthDistance;

public static byte CalculateIntensityFromDepth(int distance)
{
   //formula for calculating monochrome intensity for histogram
   return (byte)(255 - (255 * Math.Max(distance - MinDepthDistance, 0)  / (MaxDepthDistanceOffset)));
}

Visual Basic

'equal coloring for monochromatic histogram
Dim intensity = CalculateIntensityFromDepth(distance)

colorFrame(index + BlueIndex) = intensity
colorFrame(index + GreenIndex) = intensity
colorFrame(index + RedIndex) = intensity

Private Const MaxDepthDistance As Single = 4000 ' max value returned
Private Const MinDepthDistance As Single = 850 ' min value returned
Private Const MaxDepthDistanceOffset As Single = MaxDepthDistance - MinDepthDistance

Public Shared Function CalculateIntensityFromDepth(ByVal distance As Integer) As Byte

    'formula for calculating monochrome intensity for histogram
    Return CByte(255 - (255 * Math.Max(distance - MinDepthDistance, 0) / (MaxDepthDistanceOffset)))

End Function

Putting it all together

Using the formulas above, we'll now return a byte[] array that has a color value for each pixel based on if the distance for that pixel:

  • > 900 = Blue
  • > 900 && < 2000 = Green
  • > 2000 = Red

If the pixel represents a player, the color will be yellow/gold.

C#

private byte[] GenerateColoredBytes(ImageFrame imageFrame)
{
    int height = imageFrame.Image.Height;
    int width = imageFrame.Image.Width;

    //Depth data for each pixel
    Byte[] depthData = imageFrame.Image.Bits; 

    //colorFrame contains color information for all pixels in image
    //Height x Width x 4 (Red, Green, Blue, empty byte)
    Byte[] colorFrame = new byte[imageFrame.Image.Height * imageFrame.Image.Width * 4];

    //Bgr32  - Blue, Green, Red, empty byte
    //Bgra32 - Blue, Green, Red, transparency 
    //You must set transparency for Bgra as .NET defaults a byte to 0 = fully transparent

    //hardcoded locations to Blue, Green, Red (BGR) index positions       
    const int BlueIndex = 0;
    const int GreenIndex = 1;
    const int RedIndex = 2;

    var depthIndex = 0;
    for (var y = 0; y < height; y++)
    {        
        var heightOffset = y * width;

        for (var x = 0; x < width; x++)
        {
            var index = ((width - x - 1) + heightOffset) * 4;

            //var distance = GetDistance(depthData[depthIndex], depthData[depthIndex + 1]);
            var distance = GetDistanceWithPlayerIndex(depthData[depthIndex], depthData[depthIndex + 1]);

            if (distance <= 900)
            {
                //we are very close
                colorFrame[index + BlueIndex] = 255;
                colorFrame[index + GreenIndex] = 0;
                colorFrame[index + RedIndex] = 0;       
            }
            else if (distance > 900 && distance < 2000)
            {
                //we are a bit further away
                colorFrame[index + BlueIndex] = 0;
                colorFrame[index + GreenIndex] = 255;
                colorFrame[index + RedIndex] = 0;
            }
            else if (distance > 2000)
            {
                //we are the farthest
                colorFrame[index + BlueIndex] = 0;
                colorFrame[index + GreenIndex] = 0;
                colorFrame[index + RedIndex] = 255;
            }

            //Color a player
            if (GetPlayerIndex(depthData[depthIndex]) > 0)
            {
                //we are the farthest
                colorFrame[index + BlueIndex] = 0;
                colorFrame[index + GreenIndex] = 255;
                colorFrame[index + RedIndex] = 255;
            }

            //jump two bytes at a time
            depthIndex += 2;
        }
    }

    return colorFrame;
}

Visual Basic

Private Function GenerateColoredBytes(ByVal imageFrame As ImageFrame) As Byte()

    Dim height As Integer = imageFrame.Image.Height
    Dim width As Integer = imageFrame.Image.Width

    'Depth data for each pixel
    Dim depthData() As Byte = imageFrame.Image.Bits

    'colorFrame contains color information for all pixels in image
    'Height x Width x 4 (Red, Green, Blue, empty byte)
    Dim colorFrame(imageFrame.Image.Height * imageFrame.Image.Width * 4 - 1) As Byte

    'Bgr32  - Blue, Green, Red, empty byte
    'Bgra32 - Blue, Green, Red, transparency 
    'You must set transparency for Bgra as .NET defaults a byte to 0 = fully transparent

    'hardcoded locations to Blue, Green, Red (BGR) index positions       
    Const BlueIndex As Integer = 0
    Const GreenIndex As Integer = 1
    Const RedIndex As Integer = 2

    Dim depthIndex = 0
    For y = 0 To height - 1

        Dim heightOffset = y * width

        For x = 0 To width - 1

            Dim index = ((width - x - 1) + heightOffset) * 4

            Dim distance = GetDistanceWithPlayerIndex(depthData(depthIndex), depthData(depthIndex + 1))

            If distance <= 900 Then

                'we are very close
                colorFrame(index + BlueIndex) = 255
                colorFrame(index + GreenIndex) = 0
                colorFrame(index + RedIndex) = 0

            ElseIf distance > 900 AndAlso distance < 2000 Then

                'we are a bit further away
                colorFrame(index + BlueIndex) = 0
                colorFrame(index + GreenIndex) = 255
                colorFrame(index + RedIndex) = 0

            ElseIf distance > 2000 Then

                'we are the farthest
                colorFrame(index + BlueIndex) = 0
                colorFrame(index + GreenIndex) = 0
                colorFrame(index + RedIndex) = 255

            End If

            '//Color a player
            If GetPlayerIndex(depthData(depthIndex)) > 0 Then

                'we are the farthest
                colorFrame(index + BlueIndex) = 0
                colorFrame(index + GreenIndex) = 255
                colorFrame(index + RedIndex) = 255

            End If

            'jump two bytes at a time
            depthIndex += 2

        Next x

    Next y

    Return colorFrame

End Function

Comment on the Post

Already have a Channel 9 account? Please sign in