首页 > 代码库 > Kinect 2.0 + OpenCV 显示深度数据、骨架信息、手势状态和人物二值图

Kinect 2.0 + OpenCV 显示深度数据、骨架信息、手势状态和人物二值图

1.前言

Kinect 2.0实测比第一代性能提升非常多!

本来想简单地找个教程复制黏贴一下,居然还没有人写过C++版的Kinect 2.0教程,自己摸索了一下,现在把结果拿出来和大家分享。

实现的功能是:深度数据(Depth Data),骨架信息(Body Data),手势状态(Hand State)和人物二值图(就是图1的那个东西,微软官方称法是Body Index Data)的提取和显示。

效果如下:

  

图1 骨架信息,人物二值图和手势状态



图2 深度信息


2.安装

Kinect 2.0的安装要求非常苛刻,可以在官网看。具体如何安装非常简单,就不多说了,有问题可以留言。

提一句:我是Mac,本来 一直用Parallels Desktop跑虚拟机杠杠的,结果Kinect 2.0由于对GPU有要求,而虚拟机下显示的GPU不是真实的GPU,导致我的Kinect 2.0无法在虚拟机下安装。如果各位有解决这一问题的,请务必赐教!!感激不尽。


3.代码剖析

如果要自己研究的话,目前基本就两个研究来源。一个是Kinect 2.0 SDK提供的范例代码,另一个是微软Kinect官网(尤其建议阅读其中的“read technical docs”链接)。我就是结合这两个资料,摸索出来的。

具体的代码我会附在最后,但首先我们要了解Kinect 2.0的工作机制,了解了这个以后代码理解起来超级容易!Kinect 2.0真心比1.0的函数接口简单太多。

3.1 运行机理

在Kinect 2.0中,每个类型的数据都有三个类与之对应:Source,Reader和Frame。比如如果要读取骨架,就有IBodyFrameSource,IBodyFrameReader,IBodyFrame这三个类,而要读取深度数据,就有IDepthFrameSource,IDepthFrameReader,IDepthFrame这三个类,以此类推其他的如Body Index,Infrared,Color数据也是这样命名的。

那么这三个接口是什么意思呢?

3.1.1 Source

在我们初始化并打开了Kinect后,我们需要请求Kinect打开一个源,我们将从这个源不断获得信息。其代码为:

m_pKinectSensor->get_BodyFrameSource(&pBodyFrameSource);

其中m_pKinectSensor是我们的Kinect总端口,pBodyFrameSource是一个IBodyFrameSource类。

3.1.2 Reader

由于Source是Kinect端拥有的,不是我们电脑拥有的,所以我们需要创建一个读口,这个读口和上述的源绑定,之后我们读取信息都通过调用这个Reader来获得。其代码为:

pBodyFrameSource->OpenReader(&m_pBodyFrameReader);

其中m_pBodyFrameReader是一个IBodyFrameReader类。

3.1.3 Frame

Frame是真正存储数据的类,每一次都让Reader把数据读到Frame中,然后我们再从Frame中提取各种各样最后使用的数据。代码为:

m_pBodyFrameReader->AcquireLatestFrame(&pBodyFrame);

其中pBodyFrame是一个IBodyFrame类。

3.2 如何从Frame中获得数据

请求Source和创建Reader对于每一个数据类型都是一模一样的,但是从Frame中提取信息则各有不同。下面讲讲深度信息、骨架信息、手势状态和人物二值图信息的提取方法。

3.2.1 深度信息:

在Kinect 2.0中,深度坐标空间的范围是(高*宽 = 424*512)(官网有说明)。从深度信息Frame中提取数据,主要就是把Frame中的数据转存到一个数组中(官网链接)。代码为:

pBodyIndexFrame->CopyFrameDataToArray(cDepthHeight * cDepthWidth, bodyIndexArray);
这里cDepthHeight是424,cDepthWidth是512,bodyIndexArray就是一个424*512大小的16位unsigned int数组,用来存储深度数据。

3.2.2 骨架信息:

kinect 2.0可以同时追踪六个人的骨架,因此每次我们需要先调用函数,获得六个骨架信息(如果没有人,那么那个骨架类就是空指针)。代码为:

pBodyFrame->GetAndRefreshBodyData(_countof(ppBodies), ppBodies);
这里ppBodies是一个长度为6的IBody数组,IBody是用来存储追踪到的骨架信息的类。

在获得了这个类后,我们需要进一步从类中提取骨架位置,对于ppBodies中的每一个元素pBody,代码为:

pBody->GetJoints(_countof(joints), joints);

这里的joints是一个长度为25的数组,每一个元素就是骨架的位置信息。然而, 这个骨架位置信息是照相机坐标系(camera view)下的位置,x和y的范围都是-1到1。因此我们需要将它转化到深度坐标系中。这里要用到一个coordinateMapper类,具体代码为:

m_pCoordinateMapper->MapCameraPointToDepthSpace(joints[j].Position, &depthSpacePosition[j]);
coordinateMapper类的创建非常简单,具体可以参考代码。depthSpacePosition是一个长度也为25的数组,每一个元素是DepthSpacePoint,这个元素包含了在深度坐标系下的x和y坐标。

3.2.3 手势状态:

在上面的骨架信息的pBody中,同时也包含了追踪到的人的手势状态信息,具体代码为:

pBody->get_HandLeftState(&leftHandState);
pBody->get_HandRightState(&rightHandState);
其中leftHandState和rightHandState都是HandState类,这个类有五种状态:open,closed,lasso,not tracked,unknown(官网链接)。其中前三种是特殊手势,后两种大同小异,反正就是无法识别了。比较特殊的是lasso,这个手势在官网有说明,具体形状是:伸出剪刀手,然后把食指和中指合并在一起,就是lasso状态了。事实上只用食指伸出来也可以被判断为lasso,但是食指和中指一起用,追踪效果更佳稳定。

3.2.4 人物二值图:

人物二值图数据的获取和深度数据获取非常类似,具体代码为:

pBodyIndexFrame->CopyFrameDataToArray(cDepthHeight * cDepthWidth, bodyIndexArray);
只不过,bodyIndexArray是一个424*512的8位unsigned char数组。如果某个点被判断为属于人的一部分,就是黑色,否则(背景部分)就是白色。

4.源代码

源代码比较长,我做了一定程度的注释。请移步我的github下载查看。地址:otnt‘s github

注意:请手动配置工程,需要包含kinect和opencv的库。

如有任何问题和建议,非常欢迎在下面留言,我会随时关注的~ 


2014.11.04


Kinect 2.0 + OpenCV 显示深度数据、骨架信息、手势状态和人物二值图