首页 > 代码库 > Kinect v2程序设计(C++) Body 篇
Kinect v2程序设计(C++) Body 篇
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 v1 | Kinect SDK v2预览版 | |
---|---|---|
名称 | Skeleton | Body |
人体姿勢可以取得的人数 | 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预览版有公开更新,本连载就会追加新的功能介绍。
Kinect v2程序设计(C++) Body 篇
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。