Kinect开发学习笔记之(六)带游戏者ID的深度数据的提取

zouxy09@qq.com

http://blog.csdn.net/zouxy09

我的Kinect开发平台是:

Win7x86 + VS2010 + Kinect for Windows SDK v1.6 + OpenCV2.3.0

开发环境的搭建见上一文:

http://blog.csdn.net/zouxy09/article/details/8146055

本学习笔记以下面的方式组织:编程前期分析、代码与注释和重要代码解析三部分。

要实现目标:通过微软的SDK提取带游戏者ID的深度数据并用OpenCV显示,不同用户,显示的颜色不同

一、编程前期分析

我们在上一文中提到的是不带游戏者ID的深度数据的提取,具体见下面:

Kinect开发学习笔记之(五)不带游戏者ID的深度数据的提取

http://blog.csdn.net/zouxy09/article/details/8146719

首先,Kinect传感器核心是发射红外结构光,并探测红外光反射,从而可以计算出视场范围内每一个像素的深度值。从深度数据中最先提取出来的是物体主体和形状,以及每一个像素点的游戏者索引信息。然后用这些形状信息来匹配人体的各个部分,最后计算匹配出来的各个关节在人体中的位置。而Kinect具有一次识别多达6个游戏者的能力,并能跟踪最多两个人的骨骼(对于XBOX360来说,就是可以同时两个人玩游戏了)。

可能有点奇怪哦,这一个带游戏者ID,一个不带,还得那么严肃地给它单独开一文来学习。究竟啥来头啊。呵呵,实际上,既然微软提供了这种差别,那么它的存在肯定是有意义的,所谓存在即合理嘛。多个选择嘛。需要用到游戏者ID的时候就用,不需要的时候就不用费那么大劲。也不能说费劲,就是使用游戏者ID的时候,我们需要再做一些工作,去把不同游戏者的轮廓找出来,然后为了区别,标上不同的颜色,这就是本文想实现的。有点啰嗦了。

上一文中,我们讲到,Kinect的深度图像数据有两种格式,一种是带游戏者ID的,一种是不带的。两种格式都是用两个字节来保存一个像素的深度值,而两方式的差别在于:

(1)唯一表示深度值:那么像素的低12位表示一个深度值,高4位未使用;

(2)既表示深度值又含有游戏者ID:Kinect为每一个追踪到的游戏者编号作为索引。而这个方式中,像素值的高13位保存了深度值,低三位保存用户序号,7 (0000 0111)这个位掩码能够帮助我们从深度数据中获取到游戏者索引值。

要注意的是,不要对特定的游戏者索引位进行编码,因为他们是会变化的。实际的游戏者索引位并不总是和Kinect前面的游戏者编号一致。啥意思呢?例如,Kinect视野中只有一个游戏者,但是返回的游戏者索引位值可能是3或者4。也就是说有时候第一个游戏者的游戏者索引位可能不是1。还有,如果走进Kinect视野再走出去,然后再走进来,虽然你还是你,但是Kinect给你的索引ID可能就和原来的不一样了,例如之前返回的索引位是1,走出去后再次走进,可能索引位变为其他值了。所以开发Kinect应用程序的时候应该注意到这一点。

说得有点乱哦,咱们还是看代码吧。

二、代码与注释

#include <windows.h>
#include <iostream>
#include <NuiApi.h>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;//处理深度数据的每一个像素,如果属于同一个用户的ID,那么像素就标为同种颜色,不同的用户,
//其ID不一样,颜色的标示也不一样,如果不属于某个用户的像素,那么就采用原来的深度值
RGBQUAD shortDepth2RGBquad( USHORT depthID )
{ //每像素共16bit的信息,其中最低3位是ID(所捕捉到的人的ID),剩下的13位才是信息USHORT realDepth = (depthID & 0xfff8) >> 3; //提取距离信息,高13位 USHORT player =  depthID & 0x07 ;  //提取ID信息 ,低3位//因为提取的信息是距离信息,为了便于显示,这里归一化为0-255BYTE depth = 255 - (BYTE)(256*realDepth/0x0fff); RGBQUAD q; q.rgbRed = q.rgbBlue = q.rgbGreen = 0; //RGB三个通道的值都是相等的话,就是灰度的//Kinect系统能够处理辨识传感器前多至6个人物的信息,但同一时刻最多只有2个玩家可被追踪(即骨骼跟踪)switch( player ) { case 0:  q.rgbRed = depth / 2; q.rgbBlue = depth / 2; q.rgbGreen = depth / 2; break; case 1: q.rgbRed = depth; break; case 2: q.rgbGreen = depth;  break; case 3: q.rgbRed = depth / 4; q.rgbGreen = depth; q.rgbBlue = depth; break; case 4: q.rgbRed = depth; q.rgbGreen = depth; q.rgbBlue = depth / 4; break; case 5: q.rgbRed = depth; q.rgbGreen = depth / 4; q.rgbBlue = depth; break; case 6: q.rgbRed = depth / 2; q.rgbGreen = depth / 2; q.rgbBlue = depth; break; case 7: q.rgbRed = 255 - ( depth / 2 ); q.rgbGreen = 255 - ( depth / 2 ); q.rgbBlue = 255 - ( depth / 2 ); } return q;
}int main(int argc, char *argv[])
{Mat image;image.create(240, 320, CV_8UC3); //1、初始化NUI,注意这里是DEPTH_AND_PLAYER_INDEXHRESULT hr = NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX); if (FAILED(hr)) { cout<<"NuiInitialize failed"<<endl; return hr; } //2、定义事件句柄 //创建读取下一帧的信号事件句柄,控制KINECT是否可以开始读取下一帧数据HANDLE nextColorFrameEvent = CreateEvent( NULL, TRUE, FALSE, NULL );HANDLE depthStreamHandle = NULL; //保存图像数据流的句柄,用以提取数据 //3、打开KINECT设备的彩色图信息通道,并用depthStreamHandle保存该流的句柄,以便于以后读取hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX, NUI_IMAGE_RESOLUTION_320x240, 0, 2, nextColorFrameEvent, &depthStreamHandle); if( FAILED( hr ) )//判断是否提取正确 { cout<<"Could not open color image stream video"<<endl; NuiShutdown(); return hr; }namedWindow("depthImage", CV_WINDOW_AUTOSIZE);//4、开始读取深度数据 while(1) { const NUI_IMAGE_FRAME * pImageFrame = NULL; //4.1、无限等待新的数据,等到后返回if (WaitForSingleObject(nextColorFrameEvent, INFINITE)==0) { //4.2、从刚才打开数据流的流句柄中得到该帧数据,读取到的数据地址存于pImageFramehr = NuiImageStreamGetNextFrame(depthStreamHandle, 0, &pImageFrame); if (FAILED(hr)){cout<<"Could not get depth image"<<endl; NuiShutdown();return -1;}INuiFrameTexture * pTexture = pImageFrame->pFrameTexture;NUI_LOCKED_RECT LockedRect;//4.3、提取数据帧到LockedRect,它包括两个数据对象:pitch每行字节数,pBits第一个字节地址//并锁定数据,这样当我们读数据的时候,kinect就不会去修改它pTexture->LockRect(0, &LockedRect, NULL, 0); //4.4、确认获得的数据是否有效if( LockedRect.Pitch != 0 ) { //4.5、将数据转换为OpenCV的Mat格式for (int i=0; i<image.rows; i++) {uchar *ptr = image.ptr<uchar>(i);  //第i行的指针//其二是既表示深度值又含有人物序号,则像素值的高13位保存了深度值,低三位保存用户序号,//注意这里需要转换,因为每个数据是2个字节,存储的同上面的颜色信息不一样,uchar *pBufferRun = (uchar*)(LockedRect.pBits) + i * LockedRect.Pitch;USHORT * pBuffer = (USHORT*) pBufferRun;for (int j=0; j<image.cols; j++) {//对于每一个像素,我们通过它的深度数据去修改它的RGB值;RGBQUAD rgb = shortDepth2RGBquad(pBuffer[j]);ptr[3*j] = rgb.rgbBlue; ptr[3*j+1] = rgb.rgbGreen; ptr[3*j+2] = rgb.rgbRed; } } imshow("depthImage", image); //显示图像 } else { cout<<"Buffer length of received texture is bogus\r\n"<<endl; }//5、这帧已经处理完了,所以将其解锁pTexture->UnlockRect(0);//6、释放本帧数据,准备迎接下一帧 NuiImageStreamReleaseFrame(depthStreamHandle, pImageFrame ); } if (cvWaitKey(20) == 27) break; } //7、关闭NUI链接 NuiShutdown(); return 0;
}

三、代码解析

首先,这里基本上和上一文说的深度数据的获取的流程和API都是一样的,具体的话,参考上一文。只是有几个点需要说明下:

(1)初始化和打开深度数据流的时候传入的参数是不同的,这个需要注意下,我们需要的是DEPTH_AND_PLAYER_INDEX数据;

(2)每个像素的深度数据由两个字节来保存,高13位保存了深度值,低三位保存用户序号。

(3)具体显示的时候我们是这样处理的:

对于每一个像素,我们通过它的深度数据去修改它的RGB值,如果属于同一个用户的ID,那么像素就标为同种颜色,不同的用户,其ID不一样,颜色的标示也不一样,如果不属于某个用户的像素,那么就采用原来的深度值。

首先,这里涉及到了:

USHORTrealDepth = (depthID & 0xfff8) >> 3; //提取距离信息,高13位

USHORTplayer =  depthID & 0x07 ;  //提取ID信息,低3位

然后RGBQUAD是一个结构体,其保存一个像素点的RGB值,定义如下:

typedef struct tagRGBQUAD {

BYTE    rgbBlue;

BYTE    rgbGreen;

BYTE    rgbRed;

BYTE    rgbReserved;

} RGBQUAD;

至此,目标达成。

下面是结果,感觉似乎如果两个人靠得太近的话,也会被识别为同一个用户,标示同样的颜色,这点感觉有点不太稳定,这种情况应该挺容易避免的啊,是我高估了Kinect,还是我高估了我。

Kinect开发学习笔记之(六)带游戏者ID的深度数据的提取相关推荐

  1. Kinect开发学习笔记之(五)不带游戏者ID的深度数据的提取

    Kinect开发学习笔记之(五)不带游戏者ID的深度数据的提取 zouxy09@qq.com http://blog.csdn.net/zouxy09 我的Kinect开发平台是: Win7 x86 ...

  2. Kinect开发学习笔记之(八)彩色、深度、骨骼和用户抠图结合

    Kinect开发学习笔记之(八)彩色.深度.骨骼和用户抠图结合 zouxy09@qq.com http://blog.csdn.net/zouxy09 我的Kinect开发平台是: Win7 x86 ...

  3. Kinect开发学习笔记之(四)提取颜色数据并用OpenCV显示

    Kinect开发学习笔记之(四)提取颜色数据并用OpenCV显示 zouxy09@qq.com http://blog.csdn.net/zouxy09 我的Kinect开发平台是: Win7 x86 ...

  4. Kinect开发学习笔记之(七)骨骼数据的提取

    Kinect开发学习笔记之(七)骨骼数据的提取 zouxy09@qq.com http://blog.csdn.net/zouxy09 我的Kinect开发平台是: Win7x86 + VS2010 ...

  5. Polyworks脚本开发学习笔记(十六)-用C#进行Polyworks二次开发

    Polyworks脚本开发学习笔记(十六)-用C#进行Polyworks二次开发 Polyworks支持C#二次开发,用对应的SDK文档试着做一下开发样例. 新建一个C#项目,在解决方案中右键添加引用 ...

  6. Windows驱动开发学习笔记(六)—— Inline HOOK

    Windows驱动开发学习笔记(六)-- Inline HOOK SSDT HOOK Inline Hook 挂钩 执行流程 脱钩 实验一:3环 Inline Hook 实验二:0环 Inline H ...

  7. Kinect开发学习笔记之(三)Kinect开发环境配置

    Kinect开发学习笔记之(三)Kinect开发环境配置 zouxy09@qq.com http://blog.csdn.net/zouxy09 我的Kinect开发平台是: Win7 x86 + V ...

  8. Kinect开发学习笔记之(二)Kinect开发学习资源整理

    Kinect开发学习笔记之(二)Kinect开发学习资源整理 zouxy09@qq.com http://blog.csdn.net/zouxy09 刚刚接触Kinect,在网上狂搜资料,获得了很多有 ...

  9. Kinect学习(五):提取带用户ID的深度数据

    前言 在前面的一篇文章中讨论了如何从Kinect获取深度图:Kinect学习(四):提取深度数据. 这里要对其进行拓展,Kinect可以获取两种格式的深度图: 不带用户ID的深度数据,也是存储在16位 ...

最新文章

  1. python 迭代器 生成器 解析
  2. Android中Activity的四种启动方式
  3. angularjs 中 Factory,Service,Provider 之间的区别
  4. 小米6 twrp_小米6刷上统信 UOS 国产系统,操作流畅但安装需谨慎!
  5. Dell PowerEdge R740xd可以做什么?
  6. angular--Observable总结
  7. C#中List的排序(Sort)
  8. Qt安装教程(Qt 6.4)
  9. 野芭蕉V1.1.1-15.0429.1338
  10. cobalt strik启动
  11. 转载:50有用的JavaScript和jQuery技术和插件
  12. 2023全新纯净版本知识付费微信小程序源码_附搭建教程_亲测可用
  13. mysql在手游中的作用_数据库虚拟化技术_手游业务MySQL数据库虚拟化漫谈 | By 肖力-云栖社区...
  14. 通过对虚拟磁盘进行碎片整理来提高VMware VM性能
  15. yoyo-rebecca
  16. GPM降水数据下载及使用简要说明
  17. 72_text_generation\unimo-text 理解
  18. 使用MakeMKV将DVD和ISO文件转换为MKV
  19. DynamicPDF HTML TO PDF 转换器
  20. 江西师范大学计算机信息工程学院研究生,2021年江西师范大学计算机信息工程学院考研专业目录_研究生考试范围 - 学途吧...

热门文章

  1. Elasticsearch教程-从入门到精通-ES索引迁移
  2. qt与js html进行数据传递,QT与javascript交互数据的实现
  3. 搞定Server 2008蓝牙问题
  4. 更改centos epel源
  5. 2.12linux csf 防火墙 防止少量的ddos cc攻击
  6. javascript中字符串的比较规则
  7. hdu5486 Difference of Clustering 暴力
  8. 软件工程第五章3(1)
  9. Winrunner与QTP
  10. 微软2013年笔试题详解及深入