本文转载于http://blog.csdn.net/zouxy09/article/details/8146266,在此感谢zouxy09童鞋。
一、编程前期分析
深度数据的获取和彩色图像数据的获取基本上是一样的,所以关于采集过程就不赘述了,具体见:
Kinect开发学习笔记之(四)提取颜色数据并用OpenCV显示
http://blog.csdn.net/zouxy09/article/details/8146266
这里需要了解下深度数据:
深度数据流所提供的图像帧中,每一个像素点代表的是在深度感应器的视野中,该特定的(x, y)坐标处物体到离摄像头平面最近的物体到该平面的距离(以毫米为单位)。
Kinect中深度值最大为4096mm,0值通常表示深度值不能确定,一般应该将0值过滤掉。微软建议在开发中使用1220mm~3810mm范围内的值。在进行其他深度图像处理之前,应该使用阈值方法过滤深度数据至1220mm-3810mm这一范围内。
下图显示了Kinect Sensor的感知范围,其中的default range对Xbox 360和Kinect for Windows都适用,而near range仅对后者适用:
深度数据的存储:
Kinect的深度图像数据含有两种格式,两种格式都是用两个字节来保存一个像素的深度值,而两方式的差别在于:
(1)唯一表示深度值:那么像素的低12位表示一个深度值,高4位未使用;
(2)既表示深度值又含有游戏者ID:Kinect SDK具有分析深度数据和探测人体或者游戏者轮廓的功能,它一次能够识别多达6个游戏者。SDK为每一个追踪到的游戏者编号作为索引。而这个方式中,像素值的高13位保存了深度值,低三位保存用户序号,7 (0000 0111)这个位掩码能够帮助我们从深度数据中获取到游戏者索引值(这个编程将在下一节)。
应用程序可以使用深度数据流中的深度数据来支持各种各样的用户特性,如追踪用户的运动并在程序中识别和忽略背景物体的信息等。
二、代码与注释
三、代码解析
首先,这里基本上和彩色图像数据的获取的流程和API都是一样的,只有有几个不同点:
(1) 因为我们需要的是深度数据,所以在NuiInitialize(DWORD dwFlags);初始化时,应告知Kinect我要的是深度数据:NUI_INITIALIZE_FLAG_USES_DEPTH;
(2)我们需要打开的是Kinect设备的深度数据流,所以调用NuiImageStreamOpen传入了类型是NUI_IMAGE_TYPE_DEPTH, 另外,深度图像的分辨率一般用NUI_IMAGE_RESOLUTION_320x240。
(3)另外,Kinect的深度图像数据含有两种格式,其一是唯一表示深度值:那么像素的低12位表示一个深度值,高4位未使用;其二是既表示深度值又含有游戏者ID,则像素值的高13位保存了深度值,低三位保存用户序号,其中序号为0则表示无用户,1和2分别表示两个不同的用户(这个会在下一文中编程)。
所以这里深度数据转换为OpenCV的Mat数据类型就和彩色的不一样了。在显示中,我们通过一个单通道的灰度图像来描述深度图像,像素点越白表示场景中目标里摄像头越近。
转换时候有一个地方需要特别注意的,代码里面用的是:
uchar *pBufferRun = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;
USHORT * pBuffer = (USHORT*) pBufferRun;
那为什么不直接下面这样呢:
USHORT * pBuffer = (USHORT*) (LockedRect.pBits) + i * LockedRect.Pitch;
因为每个深度数据是2个字节,存储的同上面的颜色信息不一样,而pitch是以字节为单位的,然后地址的偏移是按LockedRect.pBits的地址类型来偏移的。也就是说假如按第二种方式来编写,那么当i等于图像的中间那一行的时候,pBuffer指针已经指向图像的最后一行了(因为ushort是两个字节,每偏移一个i,地址就偏移两个字节),这时候i再增加,就会导致数组访问越界了。导致的结果是,我们的深度图像显示都一半的时候,程序就死掉了。
至此,目标达成。