您的位置:首页 > 编程语言 > C语言/C++

Kinect v2程序设计(C++) Body 篇 背景移除 骨骼跟踪 手势识别

2016-04-13 17:09 806 查看


【翻译】Kinect v2程序设计(C++) Body 篇

http://www.cnblogs.com/TracePlus/p/4138615.html
http://www.cnblogs.com/TracePlus/p/4136368.html
图3 BodyIndex数据

 Kinect SDK v1Kinect SDK v2预览版
名称PlayerBodyIndex
检测支持人数6人6人
人体領域的値1~60~5
非人体領域的値0255(0xff)
表1 Kinect SDK v1和Kinect SDK v2预览版的人体区域(Player,BodyIndex)的比较

示例程序是,把BodyIndex的值,在人体区域用color
table的颜色(=「cv::Vec3b(B,G,R
)」),在非人体区域用黑色(=「cv::Vec3b(0,0,0)」)进行着色来实现可视化。

Kinect SDK v2预览版的主要功能的使用介绍,基本上完成了。这次,是关于取得Body(人体姿势)方法的说明。
  上一节,是使用Kinect SDK v2预览版从Kinect v2预览版取得BodyIndex(人体区域)的方法。

 这一节,介绍从Kinect取得Body(人体姿势)的方法。

Body
  到目前为止,Kinect能取得Depth(通过传感器的距离信息)和BodyIndex(人体区域)。并且,基于这些数据可以取得人体姿势。
 
  Kinect的人体姿势,是向学习了基于庞大数量的姿势信息的识别器里,输入人体区域的信息来推定的(注:因为男女老少高矮胖瘦体形各不相同,所以必须基于神经网络的数据库才能准确识别人体)。详细还请参考Microsoft
Research发表的论文。

  这个论文在IEEE CVPR 2011(计算机视觉及模式认识领域的首位会议)发表,获奖Best Paper。

Microsoft Research“Real-Time Human Pose Recognition in Parts from a Single Depth Image”

  背景技术说不定很复杂,不过开发者通过Kinect SDK可以简单地取得和使用人体姿势。
 

  人体的姿势数据,可以得到头,手,脚等3维的位置,基于这些可以实现姿势的识别。

  这个人体区域,在Kinect SDK v1被称为「Skeleton」,不过,在Kinect SDK v2预览版里更名为「Body」。
 
  这一节,介绍取得Body的方法。

 
示例程序
使用Kinect SDK v2预览版取得Body和Color图像叠加显示为「●(圆点)」的示例程序展示。还有,基于Body数据,Hand
State(手的状态)也做了显示。第2节有介绍取得数据的阶段摘录解说,这个示例程序的全部内容,在下面的github里公开。
 
https://github.com/UnaNancyOwen/Kinect2Sample
 



图1 Kinect SDK v2预览版的数据取得流程(重发)

「Sensor」
取得「Sensor」

// Sensor
IKinectSensor* pSensor;   ……1
HRESULT hResult = S_OK;
hResult = GetDefaultKinectSensor( &pSensor );  ……2
if( FAILED( hResult ) ){
std::cerr << "Error : GetDefaultKinectSensor" << std::endl;
return -1;
}
hResult = pSensor->Open();  ……3
if( FAILED( hResult ) ){
std::cerr << "Error : IKinectSensor::Open()" << std::endl;
return -1;
}


列表1.1 相当于图1「Source」的部分
1 Kinect v2预览版的Sensor接口。
2 取得默认的Sensor。
3 打开Sensor。

 

「Source」
从「Sensor」取得「Source」。

// Source
IBodyFrameSource* pBodySource;  ……1
hResult = pSensor->get_BodyFrameSource( &pBodySource );  ……2
if( FAILED( hResult ) ){
std::cerr << "Error : IKinectSensor::get_BodyFrameSource()" << std::endl;
return -1;
}


列表1.2 相当于图1「Source」的部分
1 Body Frame的Source接口。
2 从Sensor取得Source。
 

  这里只是关于取得Body的源代码解说,不过,为了案例程序的显示,也同时取得了Color。

「Reader」
「Source」从打开「Reader」。

// Reader
IBodyFrameReader* pBodyReader;  ……1
hResult = pBodySource->OpenReader( &pBodyReader );  ……2
if( FAILED( hResult ) ){
std::cerr << "Error : IBodyFrameSource::OpenReader()" << std::endl;
return -1;
}


列表1.3 相当于图1「Reader」的部分
1 Body  Frame的Reader接口。
2 从Source打开Reader。

 

「Frame」~「Data」
从「Reader」取得最新的「Frame」(列表1.5)。

 
在这之前,为了可以和从传感器取得的坐标匹配,取得ICoordinateMapper的接口(列表1.4),由于Color照相机和Depth传感器的位置是分开的,因此需要把body数据和Color图像的位置进行匹配。

// Coordinate Mapper
ICoordinateMapper* pCoordinateMapper;  ……1
hResult = pSensor->get_CoordinateMapper( &pCoordinateMapper );  ……2
if( FAILED( hResult ) ){
std::cerr << "Error : IKinectSensor::get_CoordinateMapper()" << std::endl;
return -1;
}


列表1.4,坐标匹配接口的取得
1 Frame之间的坐标匹配的接口。
2 从Sensor获取坐标匹配的接口。
 
接下来的列表1.5,是和连载第2节一样的方法,表示的是Color图形的取得。

int width = 1920;
int height = 1080;
unsigned int bufferSize = width * height * 4 * sizeof( unsigned char );
cv::Mat bufferMat( height, width, CV_8UC4 );
cv::Mat bodyMat( height / 2, width / 2, CV_8UC4 );
cv::namedWindow( "Body" );
// Color Table
cv::Vec3b color[6];
color[0] = cv::Vec3b( 255,   0,   0 );
color[1] = cv::Vec3b(   0, 255,   0 );
color[2] = cv::Vec3b(   0,   0, 255 );
color[3] = cv::Vec3b( 255, 255,   0 );
color[4] = cv::Vec3b( 255,   0, 255 );
color[5] = cv::Vec3b(   0, 255, 255 );
while( 1 ){
// Color Frame  ……1
IColorFrame* pColorFrame = nullptr;
hResult = pColorReader->AcquireLatestFrame( &pColorFrame );
if( SUCCEEDED( hResult ) ){
hResult = pColorFrame->CopyConvertedFrameDataToArray( bufferSize, reinterpret_cast<BYTE*>( bufferMat.data ), ColorImageFormat_Bgra );
if( SUCCEEDED( hResult ) ){
cv::resize( bufferMat, bodyMat, cv::Size(), 0.5, 0.5 );
}
}
SafeRelease( pColorFrame );
/* Body部分在列表1.6 */
// Show Window
cv::imshow( "Body", bodyMat );
if( cv::waitKey( 10 ) == VK_ESCAPE ){
break;
}
}


列表1.5,相当于图1「Frame」,「Data」的部分(第1部分)

1 为了显示Body数据取得Color图像,详细见第2节。
  

列表1.5中的Body部分的代码,在下面的列表1.6里显示。

// Body Frame
IBodyFrame* pBodyFrame = nullptr;  ……1
hResult = pBodyReader->AcquireLatestFrame( &pBodyFrame );  ……2
if( SUCCEEDED( hResult ) ){
IBody* pBody[BODY_COUNT] = { 0 };  ……3
hResult = pBodyFrame->GetAndRefreshBodyData( BODY_COUNT, pBody );  ……3
if( SUCCEEDED( hResult ) ){
for( int count = 0; count < BODY_COUNT; count++ ){
BOOLEAN bTracked = false;  ……4
hResult = pBody[count]->get_IsTracked( &bTracked );  ……4
if( SUCCEEDED( hResult ) && bTracked ){
Joint joint[JointType::JointType_Count];  ……5
hResult = pBody[count]->GetJoints( JointType::JointType_Count, joint );  ……5
if( SUCCEEDED( hResult ) ){
// Left Hand State
HandState leftHandState = HandState::HandState_Unknown;  ……6
hResult = pBody[count]->get_HandLeftState( &leftHandState );  ……6
if( SUCCEEDED( hResult ) ){
ColorSpacePoint colorSpacePoint = { 0 };  ……7
hResult = pCoordinateMapper->MapCameraPointToColorSpace( joint[JointType::JointType_HandLeft].Position, &colorSpacePoint );  ……7
if( SUCCEEDED( hResult ) ){
int x = static_cast<int>( colorSpacePoint.X );
int y = static_cast<int>( colorSpacePoint.Y );
if( ( x >= 0 ) && ( x < width ) && ( y >= 0 ) && ( y < height ) ){
if( leftHandState == HandState::HandState_Open ){  ……8
cv::circle( bufferMat, cv::Point( x, y ), 75, cv::Scalar( 0, 128, 0 ), 5, CV_AA );
}
else if( leftHandState == HandState::HandState_Closed ){  ……8
cv::circle( bufferMat, cv::Point( x, y ), 75, cv::Scalar( 0, 0, 128 ), 5, CV_AA );
}
else if( leftHandState == HandState::HandState_Lasso ){  ……8
cv::circle( bufferMat, cv::Point( x, y ), 75, cv::Scalar( 128, 128, 0 ), 5, CV_AA );
}
}
}
}
// Right Hand State
/* 和左手一样,获取右手Hand State绘制状态。 */
// Joint  ……9
for( int type = 0; type < JointType::JointType_Count; type++ ){
ColorSpacePoint colorSpacePoint = { 0 };
pCoordinateMapper->MapCameraPointToColorSpace( joint[type].Position, &colorSpacePoint );
int x = static_cast< int >( colorSpacePoint.X );
int y = static_cast< int >( colorSpacePoint.Y );
if( ( x >= 0 ) && ( x < width ) && ( y >= 0 ) && ( y < height ) ){
cv::circle( bufferMat, cv::Point( x, y ), 5, static_cast<cv::Scalar>( color[count] ), -1, CV_AA );
}
}
}
}
}
cv::resize( bufferMat, bodyMat, cv::Size(), 0.5, 0.5 );
}
}
SafeRelease( pBodyFrame );


列表1.6,相当于图1「Frame」,「Data」的部分(第2部分)
1 Body的Frame接口。
2 从Reader里取得最新的Frame。
3 从Frame取得Body。  
   后面,是从人体取得数据。
4 确认能着追踪到人体。
5 取得人体Joint(关节)。
6 取得Hand State。
7 为了绘制,把Body座標向Color座標的坐标匹配。
   匹配的坐标是否超出绘制范围(这里Color图像的尺寸是1920×1080)的检查。
   (注:因为两个Camera位置、FOV和分辨率的不同,在匹配时不可能完全一一对应,所以必须检查坐标的有效性)
8 对应状态绘制相应颜色的○(圆型)做Hand
State的可视化。
   用各自对应Open(打开:布),Closed(关闭:拳),Lasso(套索:剪)的颜色绘制。如果检查不出状态就不绘制。
9 对应人体的Joint参照color table的颜色做绘制。
   和Hand State一样,Body坐标向Color坐标坐标匹配来绘制●(圆点)。

 

  使用Kinect SDK v1从人体区域中检测获取的详细人体姿势最多支持2个人。Kinect
SDK v2预览版可以检测获取全部人体区域(6人)的详细人体姿势。
  另外,Kinect SDK v1能取得的Joint是全身20个,Kinect SDK v2预览版是追加了「脖子(=NECK)」,「指尖(=HAND_TIP_LEFT,HAND_TIP_RIGHT)」,「大拇指(=THUMB_LEFT,THUMB_RIGHT)」5个,一共25个Joint。

 
  示例程序里,根据Joint位置参考color table的颜色绘制成「●(圆点)」来可视化。
 
  Kinect SDK v1(Kinect Developer Toolkit/Kinect Interaction)可以取得的Hand State,有「Open(打开)」和「Closed(关闭)」的2种类型。
 
  Kinect SDK v2预览版,在「Open」「Closed」基础上又增加了「Lasso(=套索)」这个状态的取得。「Lasso」能检查出竖起两个手指的状态。想象为「猜拳的(拳头,剪刀,布)」就好了。这里还有一个链接扩展阅读,我之后会翻译。
 
  现在,6个人中可以同时获取其中2个人的Hand State。
 
  示例程序里,可以通过手的Joint位置的状态来着色「○(圆环)」的绘制做可视化。「Open」绿色(=「cv::Scalar(0,128,0)」),「Closed」是红色(=「cv::Scalar(0,0,128)」),「Lasso」用淡蓝色(=「cv::Scalar(128,128,0)」)表现。
 

 Kinect SDK v1Kinect SDK v2预览版
名称SkeletonBody
人体姿勢可以取得的人数2人6人
Joint(关节)20处25处
Hand State(手的状態)2種類3種類
Hand State可以取得的人数2人2人
表1 Kinect SDK v1和Kinect SDK v2预览版的人体姿势(Skeleton,Body)的比较



图2 Kinect v1和Kinect v2预览版的可以取得的Joint
运行结果
运行这个示例程序,就像图3一样,从v2预览版取得的人体姿势和手的状态被可视化了。



图3 运行结果
Joint用●(圆点)来显示,Hand State用来○(圆环)来显示。



图4 Hand State的识别结果

「Open」是绿色,「Closed」是红色,「Lasso」浅蓝色的○(圆环)来显示。 手的状态可以清晰的识别。

 
总结
  这一节是使用Kinect SDK v2预览版取得Body的示例程序的介绍。现在,Kinect
SDK v2预览版实现的主要功能基本上都被介绍了。
 
  不过,Kinect SDK v2预览版,在RTM版的发布之前预计会有2~3次的更新。近日,第1次更新被预定公开给早期提供程序的参与者。一旦SDK v2预览版有公开更新,本连载就会追加新的功能介绍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: