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:
-
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 is6408 ... ~0.95m
from sensor . Another valid value is15800 ... ~2.5m
from sensor for interpolation -
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
-
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)