Kinect raw depth to distance in meters

I am trying to convert Kinect depth map to distance in meters. The problem is that for depthmap value '1080' and around it, distance is too large because the term in denominator becomes very close to '0'. and for values above '1090', distance is negative.

if (depthValue < 2047) 
{
  depthM = 1.0 / (depthValue*-0.0030711016 + 3.3309495161);
}

The correct answer is actually a comment on your question. The number given is actually a distance in millimetres. To get this number, you either need to use a skeleton joint and call DepthImageFrame's MapFromSkeletonPoint or shift the raw short value right by DepthImageFrame.PlayerIndexBitmaskWidth.

E.g. Skeleton

using (var skeletonFrame= e.OpenSkeletonFrame())
using (var depthFrame = e.OpenDepthImageFrame())
{
    skeletonFrame.CopySkeletonDataTo(skeletons);
    var skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];

    foreach (var skeleton in skeletons)
    {
            if (skeleton.TrackingState != SkeletonTrackingState.Tracked) continue;

            var head = skeleton.Joints[JointType.Head];
            if (head.TrackingState == JointTrackingState.NotTracked) continue;

            var depthImagePoint = depthFrame.MapFromSkeletonPoint(head.Position);

            int depthInMillimeters = depthImagePoint.Depth; // TADA!
    }
}

E.g. shift

using (var depthFrame = e.OpenDepthImageFrame())
{
    var depthArray = new short[depthFrame.PixelDataLength];
    depthFrame.CopyPixelDataTo(depthArray);

    for (int i = 0; i < depthArray.Length; i++) {
        int depthInMillimeters = 
            depthArray[i] >> DepthImageFrame.PlayerIndexBitmaskWidth;
        // TADAx2!
}

The original solution below is no longer correct:

Based on the contents of this article - http://openkinect.org/wiki/Imaging_Information:

if (depthValue <= 2047) {
   depthM = 0.1236 * Math.Tan(depthValue / 2842.5 + 1.1863);
}

old inactive question but anyway here is what worked for me I have an old model of kinect (1414 for XBOX360 + PC reduction) and recently had time to make some code and measurements:

  1. my raw data is 16 bit not 10/11/12/13 bit !!!

    so probably your data is very similar. Min distance measured is ~0.95m max distance unknown (have not big enough area for testing) and min valid value is 6408 ... ~0.95m from sensor . Another valid value is 15800 ... ~2.5m from sensor for interpolation

  2. naive (linear) transformation to meters

    float z,m=(2.5-0.9)/(15800.0-6408.0);
    WORD raw;
    
    raw=... raw depth form camera/image;
    z=0.0;
    if ((raw>=6408)&&(raw< 0xFFF0)) 
     z=0.9+(float(raw-6408)*m);
    // here z is range in [m] or 0 for invalid depth
    
  3. when I will have more time then I will check the linearity and make it more precise

    to do that I need to make some programing for better measurement and setup working area for more precise distance measurements. More precise constants maybe add cosine correction if its needed. I do not know if device/driver make it itself or not have to measure it. When I get to it will update my answer but I am very lazy so it can be a while ...

[Edit 1] made some geometry measurement setup and code changes
so here are new more precise conclusions:

if (raw==0x0000) z=0.0;
else if (p.raw>=0x8000) p.z=4.0;
else p.z=0.8+(float(p.raw-6576)*0.00012115165336374002280501710376283);

raw data is already cosine corrected by driver or kinect itself so raw represent perpendicular distance from sensor. raw data is linear to perpendicular distance (at least on 0.8 - 2.0 m range) and after more precise measurement the depth range is <0.8 - 4.0> [m]. Measurements before was inaccurate.


raw = 0 is too close value
raw >= 32768 is too far / unknown value or even something else (there are more possibilities)