Click here to Skip to main content
Click here to Skip to main content

Smoothing Kinect Depth Frames in Real-Time

By , 24 Jan 2012
 

Introduction

I've been working with the Microsoft Kinect for Xbox 360 on my PC for a few months now, and overall I find it fantastic! However, one thing that has continued to bug me is the seemingly poor quality of rendered Depth Frame images. There is a lot of noise in a Depth Frame, with missing bits and a pretty serious flickering issue. The frame rate isn't bad from the Kinect, with a maximum of around 30 fps; however, due to the random noise present in the data, it draws your perception to the refresh. In this article, I am going to show you my solution to this problem. I will be smoothing Depth Frames in real-time as they come from the Kinect, and are rendered to the screen. This is accomplished through two combined methods: pixel filtering, and weighted moving average.

Background

Some Information on the Kinect

By now, I would assume that everyone has at least heard of the Kinect and understands the basic premise. It's a specialized sensor built by Microsoft that is capable of recognizing and tracking humans in 3D space. How is it able to do that? While it's true that the Kinect has two cameras in it, it does not accomplish 3D sensing through stereo optics. A technology called Light Coding makes the 3D sensing possible.

kinect.JPG

On the Kinect, there is an Infrared (IR) Projector, a Color (RGB) Camera, and an Infrared (IR) Sensor. For purposes of 3D sensing, the IR Projector emits a grid of IR light in front of it. This light then reflects off objects in its path and is reflected back to the IR Sensor. The pattern received by the IR Sensor is then decoded in the Kinect to determine the depth information and is sent via USB to another device for further processing. This depth information is incredibly useful in computer vision applications. As a part of the Kinect Beta SDK, this depth information is used to determine joint locations on the human body, thereby allowing developers like us to come up with all sorts of useful applications and functionality.

Important Setup Information

Before you download the links for either the demo application source or demo application executable, you need to prepare your development environment. To use this application, you need to have the Kinect Beta 2 SDK installed on your machine: http://www.microsoft.com/en-us/kinectforwindows/download/.

At the time of this posting, the commercial SDK has not been released. Please be sure to only use the Kinect Beta 2 SDK for this article’s downloads. Also, the SDK installs with a couple demo applications; please be sure that these run on your machine before you download the files for this article.

The Problem of Depth Data

Before I dive into the solution, let me better express the problem. Below is a screenshot of raw depth data rendered to an image for reference. Objects that are closer to the Kinect are lighter in color and objects that are further away are darker.

noisyintro.JPG

What you're looking at is an image of me sitting at my desk. I'm sitting in the middle; there is a bookcase to the left and a fake Christmas tree to the right. As you can already tell, even without the flickering of a video feed, the quality is pretty low. The maximum resolution that you can get for depth data from the Kinect is 320x240, but even for this resolution the quality looks poor indeed. The noise in the data manifests itself as white spots continuously popping in and out of the picture. Some of the noise in the data comes from the IR light being scattered by the object it’s hitting, some comes from shadows of objects closer to the Kinect. I wear glasses and often have noise where my glasses should be due to the IR light scattering.

Another limitation to the depth data is that it has a limit to how far it can see. The current limit is about 8 meters. Do you see that giant white square behind me in the picture? That's not an object close to the Kinect; the room I'm in actually extends beyond that white square about another meter. This is how the Kinect handles objects that it can't see with depth sensing, returning a depth of Zero.

The Solution

As I had mentioned briefly, the solution I have developed uses two different methods of smoothing the depth data: pixel filtering, and weighted moving average. The two methods can either be used separately or in series to produce a smoothed output. While the solution doesn't completely remove all noise, it does make an appreciable difference. The solutions I have used do not degrade the frame rate and are capable of producing real-time results for output to a screen or recording.

Pixel Filtering

The first step in the pixel filtering process is to transform the depth data from the Kinect into something that is a bit easier to process.

private short[] CreateDepthArray(ImageFrame image)
{
    short[] returnArray = new short[image.Image.Width * image.Image.Height];
    byte[] depthFrame = image.Image.Bits;

    // Porcess each row in parallel
    Parallel.For(0, 240, depthImageRowIndex =>
    {
        // Process each pixel in the row
        for (int depthImageColumnIndex = 0; depthImageColumnIndex < 640; depthImageColumnIndex += 2)
        {
            var depthIndex = depthImageColumnIndex + (depthImageRowIndex * 640);
            var index = depthIndex / 2;

            returnArray[index] = 
                CalculateDistanceFromDepth(depthFrame[depthIndex], depthFrame[depthIndex + 1]);
        }
    });

    return returnArray;
}

This method creates a simple short[] into which a depth value for each pixel is placed. The depth value is calculated from the byte[] of an ImageFrame that is sent every time the Kinect pushes a new frame. For each pixel, the byte[] of the ImageFrame has two values.

private short CalculateDistanceFromDepth(byte first, byte second)
{
    // Please note that this would be different if you 
    // use Depth and User tracking rather than just depth
    return (short)(first | second << 8);
}

Now that we have an array that is a bit easier to process, we can begin applying the actual filter to it. We scan through the entire array, pixel by pixel, looking for Zero values. These are the values that the Kinect couldn't process properly. We want to remove as many of these as realistically possible without degrading performance or reducing other features of the data (more on that later).

When we find a Zero value in the array, it is considered a candidate for filtering, and we must take a closer look. In particular, we want to look at the neighboring pixels. The filter effectively has two "bands" around the candidate pixel, and is used to search for non-Zero values in other pixels. The filter creates a frequency distribution of these values, and takes note of how many were found in each band. It will then compare these values to an arbitrary threshold value for each band to determine if the candidate should be filtered. If the threshold for either band is broken, then the statistical mode of all the non-Zero values will be applied to the candidate, otherwise it is left alone.

filter.JPG

The biggest considerations for this method are ensuring that the bands for the filter actually surround the pixel as they would be displayed in the rendered image, and not just values next to each other in the depth array. The code to apply this filter is as follows:

short[] smoothDepthArray = new short[depthArray.Length];

// We will be using these numbers for constraints on indexes
int widthBound = width - 1;
int heightBound = height - 1;

// We process each row in parallel
Parallel.For(0, 240, depthArrayRowIndex =>
{
  // Process each pixel in the row
  for (int depthArrayColumnIndex = 0; depthArrayColumnIndex < 320; depthArrayColumnIndex++)
  {
    var depthIndex = depthArrayColumnIndex + (depthArrayRowIndex * 320);

    // We are only concerned with eliminating 'white' noise from the data.
    // We consider any pixel with a depth of 0 as a possible candidate for filtering.
    if (depthArray[depthIndex] == 0)
    {
      // From the depth index, we can determine the X and Y coordinates that the index
      // will appear in the image. We use this to help us define our filter matrix.
      int x = depthIndex % 320;
      int y = (depthIndex - x) / 320;

      // The filter collection is used to count the frequency of each
      // depth value in the filter array. This is used later to determine
      // the statistical mode for possible assignment to the candidate.
      short[,] filterCollection = new short[24,2];

      // The inner and outer band counts are used later to compare against the threshold 
      // values set in the UI to identify a positive filter result.
      int innerBandCount = 0;
      int outerBandCount = 0;

      // The following loops will loop through a 5 X 5 matrix of pixels surrounding the 
      // candidate pixel. This defines 2 distinct 'bands' around the candidate pixel.
      // If any of the pixels in this matrix are non-0, we will accumulate them and count
      // how many non-0 pixels are in each band. If the number of non-0 pixels breaks the
      // threshold in either band, then the average of all non-0 pixels in the matrix is applied
      // to the candidate pixel.
      for (int yi = -2; yi < 3; yi++)
      {
        for (int xi = -2; xi < 3; xi++)
        {
          // yi and xi are modifiers that will be subtracted from and added to the
          // candidate pixel's x and y coordinates that we calculated earlier. From the
          // resulting coordinates, we can calculate the index to be addressed for processing.

          // We do not want to consider the candidate
          // pixel (xi = 0, yi = 0) in our process at this point.
          // We already know that it's 0
          if (xi != 0 || yi != 0)
          {
            // We then create our modified coordinates for each pass
            var xSearch = x + xi;
            var ySearch = y + yi;

            // While the modified coordinates may in fact calculate out to an actual index, it 
            // might not be the one we want. Be sure to check
            // to make sure that the modified coordinates
            // match up with our image bounds.
            if (xSearch >= 0 && xSearch <= widthBound && 
                         ySearch >= 0 && ySearch <= heightBound)
            {
              var index = xSearch + (ySearch * width);
              // We only want to look for non-0 values
              if (depthArray[index] != 0)
              {
                // We want to find count the frequency of each depth
                for (int i = 0; i < 24; i++)
                {
                  if (filterCollection[i, 0] == depthArray[index])
                  {
                    // When the depth is already in the filter collection
                    // we will just increment the frequency.
                    filterCollection[i, 1]++;
                    break;
                  }
                  else if (filterCollection[i, 0] == 0)
                  {
                    // When we encounter a 0 depth in the filter collection
                    // this means we have reached the end of values already counted.
                    // We will then add the new depth and start it's frequency at 1.
                    filterCollection[i, 0] = depthArray[index];
                    filterCollection[i, 1]++;
                    break;
                  }
                }

                // We will then determine which band the non-0 pixel
                // was found in, and increment the band counters.
                if (yi != 2 && yi != -2 && xi != 2 && xi != -2)
                  innerBandCount++;
                else
                  outerBandCount++;
              }
            }
          }
        }
      }

      // Once we have determined our inner and outer band non-zero counts, and 
      // accumulated all of those values, we can compare it against the threshold
      // to determine if our candidate pixel will be changed to the
      // statistical mode of the non-zero surrounding pixels.
      if (innerBandCount >= innerBandThreshold || outerBandCount >= outerBandThreshold)
      {
        short frequency = 0;
        short depth = 0;
        // This loop will determine the statistical mode
        // of the surrounding pixels for assignment to
        // the candidate.
        for (int i = 0; i < 24; i++)
        {
          // This means we have reached the end of our
          // frequency distribution and can break out of the
          // loop to save time.
          if (filterCollection[i,0] == 0)
            break;
          if (filterCollection[i, 1] > frequency)
          {
            depth = filterCollection[i, 0];
            frequency = filterCollection[i, 1];
          }
        }
 
        smoothDepthArray[depthIndex] = depth;
      }
    }
    else
    {
      // If the pixel is not zero, we will keep the original depth.
      smoothDepthArray[depthIndex] = depthArray[depthIndex];
    }
  }
});

A Note About Changes Since the Original Post

I have recently updated this filter to be more accurate compared to my original post. In my original post, if any of the band thresholds were broken, the statistical mean of all non-Zero pixels in the filter matrix was assigned to the candidate pixel; I have changed this to use the statistical mode. Why does this matter?

filter.JPG

Consider the previous picture representing a theoretical filter matrix of depth values. From looking at these values, we can visually identify that there is probably an edge of some object in our filter matrix. If we were to apply the average of all these values to the candidate pixel, it would remove the noise from the X,Y perspective but it would introduce noise along the Z,Y perspective; placing the candidate pixel's depth half way between the two individual features. By using the statistical mode, we are mostly assured of assigning a depth to the candidate pixel that matches the most dominant feature in the filter matrix.

I say 'mostly' because there is still a chance of identifying a submissive feature as being dominant due to small variances in the depth readings; this has had negligible effect on the results though. A solution to this issue involves data discretization and deserves a separate article of its own.

Weighted Moving Average

Now that we have a filtered depth array on our hands, we can move on to the process of calculating a weighted moving average of an arbitrary number of previous depth arrays. The reason we do this is to reduce the flickering effect produced by the random noise still left in the depth array. At 30 fps, you're really going to notice the flicker. I had previously tried an interlacing technique to reduce the flicker, but it never really looked as smooth as I would like. After experimenting with a couple other methods, I settled on the weighted moving average.

What we do is set up a Queue<short[]> to store our most recent N number of depth arrays. Since Queue's are a FIFO (First In, First Out) collection object, they have excellent methods to handle discrete sets of time series data. We then weight the importance of the most recent depth arrays to the highest, and the importance of the oldest the lowest. A new depth array is created from the weighted average of the depth frames in the Queue.

This weighting method was chosen due to the blurring effect that averaging motion data can have on the final rendering. If you were to stand still, a straight average would work fine with a small number of items in your Queue. However, once you start moving around, you will have a noticeable trail behind you anywhere you go. You can still get this with a weighted moving average, but the effects are less noticeable. The code for this is as follows:

averageQueue.Enqueue(depthArray);

CheckForDequeue();

int[] sumDepthArray = new int[depthArray.Length];
short[] averagedDepthArray = new short[depthArray.Length];

int Denominator = 0;
int Count = 1;

// REMEMBER!!! Queue's are FIFO (first in, first out). 
// This means that when you iterate over them, you will
// encounter the oldest frame first.

// We first create a single array, summing all of the pixels 
// of each frame on a weighted basis and determining the denominator
// that we will be using later.
foreach (var item in averageQueue)
{
    // Process each row in parallel
    Parallel.For(0,240, depthArrayRowIndex =>
    {
      // Process each pixel in the row
      for (int depthArrayColumnIndex = 0; depthArrayColumnIndex < 320; depthArrayColumnIndex++)
      {
        var index = depthArrayColumnIndex + (depthArrayRowIndex * 320);
        sumDepthArray[index] += item[index] * Count;
      }
    });
    Denominator += Count;
    Count++;
}

// Once we have summed all of the information on a weighted basis,
// we can divide each pixel by our denominator to get a weighted average.
Parallel.For(0, depthArray.Length, i =>
{
    averagedDepthArray[i] = (short)(sumDepthArray[i] / Denominator);
});

Render the Image

Now that we have applied both of our smoothing techniques to the depth data, we can render the image to a Bitmap:

// We multiply the product of width and height by 4 because each byte
// will represent a different color channel per pixel in the final iamge.
byte[] colorFrame = new byte[width * height * 4];

// Process each row in parallel
Parallel.For(0, 240, depthArrayRowIndex =>
{
  // Process each pixel in the row
  for (int depthArrayColumnIndex = 0; depthArrayColumnIndex < 320; depthArrayColumnIndex++)
  {
    var distanceIndex = depthArrayColumnIndex + (depthArrayRowIndex * 320);
    // Because the colorFrame we are creating has four times as many bytes representing
    // a pixel in the final image, we set the index to be the depth index * 4.
    var index = distanceIndex * 4;

    // Map the distance to an intesity that can be represented in RGB
    var intensity = CalculateIntensityFromDistance(depthArray[distanceIndex]);

    // Apply the intensity to the color channels
    colorFrame[index + BlueIndex] = intensity;
    colorFrame[index + GreenIndex] = intensity;
    colorFrame[index + RedIndex] = intensity;
  }
});

All Together Now

Now that I have shown you some of the code and theory behind the smoothing process, let’s look at it in terms of using the demo application provided in the links above.

app.JPG

As you can see, the demo application will do a side by side comparison of the Raw Depth Image and the Smoothed Depth Image. You can experiment with the smoothing settings in the application as well. The settings that you will find when you first run the application are what I recommend for general purpose use. It provides a good mix of smoothing for stationary objects, moving objects, and doesn't try to "fill in" too much from the filtering method.

For example: you can turn both band filters down to 1, and turn the weighted moving average up to 10, and you'll have the lowest flicker and noise for stationary blunt objects. However, once you move, you will have a very noticeable trail, and your fingers will all look like they are webbed if you don't have a wall close behind you.

Points of Interest

I have really enjoyed playing around with these smoothing techniques and learning that there is probably no 'one-size-fits-all' solution for it. Even with the same hardware, your physical environment and intentions will drive your choice for smoothing more than anything. I would like to encourage you to open the code and take a look for yourself, and share your ideas for improvement! At the very least, go borrow your neighbor kid’s Kinect for a day and give the demo application a whirl.

Another point of interest has been in seeing how reducing noise for rendering purposes can actually introduce noise for depth calculation purposes. The pixel filtering method of reducing noise works well on its own to reduce noise in depth information prior to further calculations, removing 'white' noise and providing a best guess as to what that information should be. The weighted moving average method works well to reduce noise prior to rendering, but will actually introduce noise along the Z,Y perspective due to the effects of averaging depth information. I hope to continue learning about these effects and how to use them properly for different types of applications.

I'll leave you with a brief video demonstration of the demo application. In it, I pretty much just sit and wave my arms around, but it gives you a good idea of what these techniques are capable of doing. I run through all the combinations of features in 70 seconds, and no audio.

Please keep in mind, that it is almost impossible to see a change in the flicker when I turn off the weighted moving average due to the low frame rate of YouTube. You'll just have to trust me, or download the code; it's like night and day.

Here is a direct link to the video on YouTube: http://youtu.be/YZ64kJ--aeg.

Further Reading

If this topic interests you, I would highly recommend reading the Microsoft Research paper on KinectFusion: Real-Time Dense Surface Mapping and Tracking. They have done some amazing work in this particular area. However, I don’t think you would ever be able to achieve these results with .NET: http://research.microsoft.com/pubs/155378/ismar2011.pdf.

History

  • January 21 , 2012 - First version.
  • January 22, 2012 - Updated the article and downloads to include suggestion from jpmik in the comments.
  • January 24, 2012 - Updated the pixel filtering method to be more accurate.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Karl Sanford
Software Developer Open Systems Technologies
United States United States
Member
First learned to program in 1997 on my TI-83 and have been doing it ever since, with a foray into networking and infrastructure.
 
Mostly a C# junky (Win\Web Forms, WP7.5/8, WPF and MVC), though I have experience with many other technologies and products.
 
I have also been trying to learn and apply more in the area of AI, specifically focusing on computer vision, natural language processing, and classification.
 
In my spare time, I love to tinker with electronics and various useless DIY projects.
 
My brain is a shark... if it stops moving, it will die. I'm always looking to learn more.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionSmoothing for VBmembercrodriguez081112 May '13 - 11:38 
Hi i have very questions for sdk 1.7
What is the equivalent for vb.net
How to use in my source code
 
Paralles.FOR ? in vb.net
 
thanks
QuestionNew source can't downloadmemberLouis Edison26 Mar '13 - 22:00 
Hi , i can't download your source because the file is empty
AnswerRe: New source can't downloadmemberKarl Sanford27 Mar '13 - 3:22 
All fixed, thanks for the heads-up!
Be The Noise

GeneralRe: New source can't downloadmemberLouis Edison5 Apr '13 - 22:54 
Thank you support the great source code to us Smile | :)
NewsUpdated Source Code AvailablememberKarl Sanford24 Mar '13 - 14:17 
Sorry it's taken so long, but I've finally had some time to revisit the source code on this article. I have made a first pass with the new v1.7 Kinect SDK, and have posted it to https://kinectdepthsmoothing.codeplex.com/[^].
 
I still have a little tweaking I'd like to do before I update the article, but this will get the code working on the new SDK's. A few notes about changes in the source code you will find:
-The smoothing methods will now allow for PlayerIndex preservation of DepthImagePixel's. This allows you to use the filters for green screen scenarios now.
-The smoothing methods will now handle multiple depth frame resolutions.
-The WPF example is built upon the DepthBasics-WPF example code from the Kinect for Windows Toolkit.
Be The Noise

QuestionNew Version Requiredmembershayaanmasood21 Mar '13 - 13:48 
Hi buddy,
 
Great Code but no help for Kinect SDK 1.6 version
 
Showing Half blank screen as always..
 

Please Help us ASAP!
AnswerRe: New Version RequiredmemberKarl Sanford24 Mar '13 - 14:19 
Sorry the update took so long, here is some code to get you started[^]
Be The Noise

QuestionDid you finally updated?memberson1cman12 Feb '13 - 23:30 
Hi, your code is very helpfull, i was trying to run both methods you propouse in the new kinect version(1.6), i just figure out how to make work the filter in this new version, but i having problems with the averge method; im doing a code that only get the players silouttes with out the jittering on the edges... can you please help me how to implement your code to the new kinect version or at least tell me how to remove the jittering from the players silouettes? Thank you BTW.
AnswerRe: Did you finally updated?memberlazydog710 Mar '13 - 12:57 
can you upload the code ( 1.6 sdk ) please (: ?
GeneralRe: Did you finally updated?memberKarl Sanford24 Mar '13 - 14:20 
Sorry the update took so long, here is some code to get you started[^]
Be The Noise

AnswerRe: Did you finally updated?memberKarl Sanford24 Mar '13 - 14:20 
Sorry the update took so long, here is some code to get you started[^]
Be The Noise

QuestionCode aint workingmemberBilal.ahmed24120 Nov '12 - 12:08 
I just downloaded and compiled the code. but I have certain issues.
 
using Microsoft.Research.Kinect.Nui;
 
this component cant be found.
 
also the namespace imageframe isnt found.
 
am i having issues with the sdk??
AnswerRe: Code aint workingmemberKarl Sanford24 Mar '13 - 14:20 
Sorry the update took so long, here is some code to get you started[^]
Be The Noise

QuestionWhy use Inner & Outer bound instead of just a 5x5 grid?memberMember 947620013 Oct '12 - 21:33 
Hi Karl, May i know why do you wanna use inner and outer bounds instead of just a 5x5 grid to slide through? Is it any advantage for using inner & outer bounds?? Thanks!
GeneralAwesome!memberMember 947620011 Oct '12 - 17:23 
Hi Karl, just wanted to thank you for sharing such a detailed post and taking the time to educate people. Thank you!
Questionproblem in convert it to v 1.5membercombo_ci6 Oct '12 - 8:12 
hi dear
i was converted this code to ver 1.5
but when i run the project the output of depthframe have some problem and half of output image is Black!!
 
i was upload the code in http://virga.persiangig.com/Programming/KinectDepthSmoothingSrc3.rar
 
can anyone help me?
AnswerRe: problem in convert it to v 1.5memberKarl Sanford24 Mar '13 - 14:21 
Sorry the update took so long, here is some code to get you started[^]
Be The Noise

QuestionOpenCV Equivalentmembermr.axe18 Sep '12 - 18:40 
Great Job !
 
Do you know anything like you developed in OpenCV ???
 
M.
QuestionRegarding new SDk 1.5memberimjayakumar5 Sep '12 - 7:34 
Hi
Your code looks good Big Grin | :-D . BTW I was trying to implement your filter in the green screen kinect app. But unable to finish it.
Have you tried with new SDK 1.5 ??
 
If so kindly share your new code .
 
Regards,
Jayakumar Natarajan

Regards
Jayakumar Natarajan
--------------------------------------------------------
Ever Tried,Ever Faild No Matter.Fail Again. Fail Better.

GeneralMy vote of 5memberSanjay K. Gupta10 Aug '12 - 1:07 
Well written and well coded article(proper commented the code).
Thanks for sharing.
QuestionCode updatememberlily.maz.yuto19 Jul '12 - 8:19 
Hi karl,
 
Thanks very much for sharing your knowledge, it's outstanding. We are working on a kinect project, which basically is a chromakey effect and your work would help us to enhance the image results, but we are developing it in the last SDK version. Do you think you could give us any clue about how to update this code to the last version. We need to solve this immediately, and we will really appreciate any advice you could give us.  
 
Thanks again, you are very kind   Smile | :)
 
Best regards
 
Lily- ByYuto team
AnswerRe: Code updatememberKarl Sanford19 Jul '12 - 8:31 
Hi, I'm glad this will help you out. I'm currently working on updating it for the latest SDK... I was hoping to have it done already, but I'm having a hard time getting the time to myself to do it. Hopefully it'll be done soon.
 
As far as what I'm having to change:
-Basics of the new SDK: the new SDK versus the Beta are very different in how you actually initialize and obtain the depth information.
-Changing how the depth array is created from the ImageFrame: the current CreateDepthArray method needs to be changed to use the newer image frames and depth bits. All subsequent methods use the results of the CreateDepthArray method, so not much else should need to be changed as long as the results stay the same.
-Allowing tracked user depths: currently, my code only supports basic depth frames. there were other requests to accept and preserve player indexes in depth frames, and I am working on adding this functionality. For this, we essentially have to just keep the original frame information, extract our depth data and perform modifications, and reinsert it into the original frame info. this is proving to be a little challenging because it adds the extra steps of smoothing over potential tracked players and expanding it beyond the intended area.
Be The Noise

AnswerRe: Code updatememberKarl Sanford24 Mar '13 - 14:22 
Sorry the update took so long, here is some code to get you started[^]
Be The Noise

QuestionUpdate to 1.0 or 1.5 SDK?membercodepix12 Jul '12 - 9:21 
I speak for many when I ask when we can have an updated version of this code, to run it in the sdk 1.5? the conversion is tortuous ...
 
This solution could refine the silhouette of the player in a cutout mask (see greenscreen on the SDK Toolkit)?
 
Thank you very much for your time and help Cool | :cool:
AnswerRe: Update to 1.0 or 1.5 SDK?memberKarl Sanford13 Jul '12 - 3:02 
I know, I've been slacking on this front. I'm thinking I should have time this weekend to work on it.
 
Right now, I really only have time to update it to the new SDK, and not work out a solution for the silhouette.
 
Thanks for your interest Thumbs Up | :thumbsup:
Be The Noise

GeneralRe: Update to 1.0 or 1.5 SDK?membercodepix13 Jul '12 - 8:04 
Thank you for your quick response, but if you can work on this a little bit of your time on that, this make huge impact in our development process.
 
I hope can you make our wishes a reality
 
Have a nice day!
GeneralRe: Update to 1.0 or 1.5 SDK?memberKarl Sanford24 Mar '13 - 14:21 
Sorry the update took so long, here is some code to get you started[^]
Be The Noise

QuestionDid you have any update for the average ?memberleezhm12 Apr '12 - 3:28 
I have done it like you said, and i had problem when i moved.the image had a very noticeable trail,
Did anyone can give me some suggestion? Thanks!
AnswerRe: Did you have any update for the average ?memberKarl Sanford12 Apr '12 - 3:46 
The only advice I would give is to make sure that you have implemented a weighted moving average, and not just a moving average. You could also try reducing the number of frames you are including in the average, or try moving to an exponential weighted moving average.
Be The Noise

GeneralRe: Did you have any update for the average ?memberleezhm12 Apr '12 - 5:06 
First,thanks for your reply.
 
yes, now i had set the averageFrameCount as 5 and also used an exponential as count 5th power.
 
Btw,did you have any good exponential weighted moving average.Thanks!
QuestionLatest kinect SDK.memberatulks.in13 Mar '12 - 21:03 
I am having trouble using this algorythm with the latest kinect sdk.There are few changes i have as per the new sdk but with no luck, would appreciate any help.
" There are 10 types of people one who understands binary and one who don't................."

GeneralMy vote of 5memberMihai MOGA8 Feb '12 - 6:39 
Great article. Please keep it up!
GeneralRe: My vote of 5memberKarl Sanford8 Feb '12 - 13:17 
Thank you very much!
Be The Noise

QuestionReferred PapermemberMember 39310185 Feb '12 - 17:58 
May I know from which publications I can get the details about the two approaches you have discussed here.
AnswerRe: Referred PapermemberKarl Sanford5 Feb '12 - 19:52 
Sorry, I didn't use any research publications to develop these two approaches. I'm sure there are probably some papers out there that cover similar techniques, but I don't know of them. Is there something specific you had questions about that I could help you with?
Be The Noise

GeneralRe: Referred PapermemberMember 39310186 Feb '12 - 18:54 
Your Article is informative. Cheer Up! I want to implement and evaluate some more filters. Can you suggest the idea to implement Multi-lateral Filter for Real-Time Depth Enhancement.
QuestionDoes the algorithm works if used for getting a smooth DepthAndPlayer Frame?memberLeoYuen2 Feb '12 - 7:16 
Hi Karl,
 
I am new to Kinect programming, and was frustrated by the blinking edges of the player extracted from the DepthAndPlayer frame.
 
Your algorithm seems to be a solution for my problem, but in your case you don't handle the player index.
 
Would you mind giving advice to me what if this algorithm can be useful if I also want the player information? Or what direction should I go if I to solve that?
 
Thank you very much!
 
Leo
AnswerRe: Does the algorithm works if used for getting a smooth DepthAndPlayer Frame?memberKarl Sanford2 Feb '12 - 13:23 
Hi Leo,
To be able to handle the depth with a player index, there are a couple things to look at. First, you want to be able to calculate the depth correctly. We will change the CalculateDistanceFromDepth method to look like this:
private short CalculateDistanceFromDepth(byte first, byte second)
{
    return (short)((first >> 3) | (second << 5));
}
 
This change will handle the smoothing for output of depth data when using the player index.
 
However, I'm guessing that what you really want is to be able to change the color of the player? To do this, you will need to preserve the player index from the original depth data in some way. There are any number of ways to do this.
 
One possible way to do this is to preserve the data in a new array. You could do this by creating a new method that is exactly like the CreateDepthArray except that rather than performing the CalculateDistanceFromDepth method, you would calculate the Player Index like this:
private short GetPlayerIndexFromDepth(byte first)
{
    return (short)(first & 0x07);
}
 
Then you could use this resulting array during the actual rendering to identify pixels that represent a Player.
 
That would be the rough and dirty approach, and should get you started down the right path.
 
To make the approach more elegant, something to consider is that smoothing could possibly extend the area that represents a player. You may need to tweak the actual smoothing algorithm to take player indexes into account during the process if you want the player coloring to extend over the smoothed noise pixels.
 
If I was entirely off, and you actually want to be able to use the Player Indexes of the depth data as a smoothed mask for clipping of color frames, then I would recommend an entirely different approach all together...
Be The Noise

NewsKinect SDK v1memberKarl Sanford1 Feb '12 - 4:47 
Version 1 of the Kinect SDK was released today. I am still reviewing the changes, but I plan to update this article for the new release when I get some free time (maybe this weekend?).
Be The Noise

GeneralRe: Kinect SDK v1memberMember 86962123 Mar '12 - 7:56 
Hi Karl,
 
Did you manage to take a look at this for the v1 SDK, as yet?
 
I've tried integrating the CreateAverageDepthArray() and CreateFilteredDepthArray() functions into my own project that generates a point cloud from Kinect data, but have so far found the smoothing (at leastthe averaging - the filtering doesn't appear to be working at all for me, as yet) to give worse results that the base Kinect.
 
I'll keep plugging away, in case it's an issue from my side.
 
Kean
GeneralGood articlemvpthatraja29 Jan '12 - 7:07 
Awesome, My 5!
thatraja

FREE Code Conversion VB6 ASP VB.NET C# ASP.NET C++ JAVA PHP DELPHI | Nobody remains a virgin, Life screws everyone Sigh | :sigh:

GeneralRe: Good articlememberKarl Sanford29 Jan '12 - 7:22 
Thanks, glad you enjoyed it!
Be The Noise

GeneralMy vote of 5memberSteve Maier26 Jan '12 - 6:52 
I am just getting into Kinect work and think this is a great article.
GeneralRe: My vote of 5memberKarl Sanford26 Jan '12 - 8:43 
Thank you very much!
Be The Noise

QuestionVB Equivelent?membermorden24 Jan '12 - 14:43 
HI, and thanks so much for a well written article. Ive been working on somehting similar, and have enjoyed your article immensely. Unfortunatly, Im a VB guy, and while I have gotten the code converted for the most part, Im really stuck with the Parallel.For routines... I cant seem to understand whats happening in them well enough to translate it to something I can use. Im not sure if oyu can or not, but could you either explain the following snippet, or, if possible, provide a vb translation? I think I understand whats going on, but clearly Im missing something...thanks in advance!
 
private short[] CreateDepthArray(ImageFrame image)
{
      short[] returnArray = new short[image.Image.Width * image.Image.Height];
      byte[] depthFrame = image.Image.Bits;
 
      // Porcess each row in parallel
      Parallel.For(0, 240, depthImageRowIndex =&gt;
      {
            // Process each pixel in the row
            for (int depthImageColumnIndex = 0; depthImageColumnIndex &lt; 640; depthImageColumnIndex += 2)
            {
                  var depthIndex = depthImageColumnIndex + (depthImageRowIndex * 640);
                  var index = depthIndex / 2;
 
                  returnArray[index] =
                        CalculateDistanceFromDepth(depthFrame[depthIndex], depthFrame[depthIndex + 1]);
            }
      });
 
      return returnArray;
}
AnswerRe: VB Equivelent?memberKarl Sanford24 Jan '12 - 19:04 
Thanks, I'm glad you've enjoyed the article. I've used VB so rarely that you don't want a translation from me. I will try to explain what's happening a bit better, and point you in the right direction.
 
0   private short[] CreateDepthArray(ImageFrame image)
1   {
2     short[] returnArray = new short[image.Image.Width * image.Image.Height];
3     byte[] depthFrame = image.Image.Bits;
4 
5     // Porcess each row in parallel
6     Parallel.For(0, 240, depthImageRowIndex =>
7     {
8       // Process each pixel in the row
9       for (int depthImageColumnIndex = 0; depthImageColumnIndex < 640; depthImageColumnIndex += 2)
10      {
11        var depthIndex = depthImageColumnIndex + (depthImageRowIndex * 640);
12        var index = depthIndex / 2;
13 
14        returnArray[index] =
                CalculateDistanceFromDepth(depthFrame[depthIndex], depthFrame[depthIndex + 1]);
15      }
16    });
17  
18    return returnArray;
19  }
A Parallel For loop acts much like any normal for loop; you have a From(inclusive) value, a To(exclusive), and some variable to know what loop number you're on. On line 6, the values being passed to the For method are (From 0 (inclusive), To 240 (exclusive), variable to use in the loop). The Parallel.For method will then (possibly) run each of the loops in parallel.
 
We loop from 0 to 239 because we want to process each row of the array as it would appear in the final render. Since the resolution is 320x240, we can then calculate the start of each row by multiplying the row index (0 - 239) by the number of columns in each row. We can address each pixel in the row by adding this number to the column index as you see in line 11.
 
You may be wondering why I have 640 in for the number of columns if the resolution is 320x240. This is because of the way we get the depth frame from the Kinect. The byte array from the Kinect that holds the depth information has two bytes per pixel; so there are actually 320 (resolution width) x 2 = 640 columns in the initial data. We use these two bytes on line 14 to calculate an individual depth value. We can then determine the index of the individual value in the final render by dividing our depthIndex by 2 (since our depth frame is twice as big as our output) on line 12. Then notice that we increment our column index by 2 at the end of row 9 to progress to the next pixel.
 
As you'll see in the code, any other Parallel.For loop after the data has been processed in this method will have a column limit of 320.
 
At the end of line 6, you'll notice the '=>' symbols, then some curly brackets and some code. This is a lambda expression in C#. You can read more about the VB equivalent at MSDN - Lambda Expressions (Visual Basic)[^].
 
Also, to get some help on the VB syntax for the Parallel.For loop, again I would have you look at MSDN - How to: Write a Simple Parallel.For Loop[^]
 
Hope this helps!
Be The Noise

GeneralMy Vote of 5memberClark Kent12323 Jan '12 - 2:15 
A well written article. I also enjoyed the structure of your article. Stating what the problem is and showing the solution and highlighting any code that achieves the solution. I wish all articles written used this same structure.
 
Good job. Keep up the good work. Smile | :)
GeneralRe: My Vote of 5memberKarl Sanford23 Jan '12 - 3:27 
Thank you very much!
Be The Noise

GeneralMy vote of 5memberJF201522 Jan '12 - 18:58 
Nice article. Thanks for sharing!
GeneralRe: My vote of 5memberKarl Sanford22 Jan '12 - 19:15 
Thanks!
Be The Noise

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 24 Jan 2012
Article Copyright 2012 by Karl Sanford
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid