【翻译】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里公开。
 
 
图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传感器的位置是分开的因此需要把b ody数据和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( 25500 );
color[1] = cv::Vec3b(   02550 );
color[2] = cv::Vec3b(   00255 );
color[3] = cv::Vec3b( 2552550 );
color[4] = cv::Vec3b( 2550255 );
color[5] = cv::Vec3b(   0255255 );
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.50.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( 01280 ), 5, CV_AA );
                  }
                  else if( leftHandState == HandState::HandState_Closed ){  ……8
                    cv::circle( bufferMat, cv::Point( x, y ), 75, cv::Scalar( 00128 ), 5, CV_AA );
                  }
                  else if( leftHandState == HandState::HandState_Lasso ){  ……8
                    cv::circle( bufferMat, cv::Point( x, y ), 75, cv::Scalar( 1281280 ), 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.50.5 );
    }
  }
  SafeRelease( pBodyFrame );
列表1.6, 相当于图1「Frame」,「Data」的部分(第2部分)
1 Body的Frame接口。
2 从Reader里取得最新的Frame。
3 从Frame取得Body。  
   后面,是从人体取得数据。
4 确认能着追踪到人体。
取得人体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_LEFTHAND_TIP_RIGHT)」「大拇指(=THUMB_LEFTTHUMB_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(01280)」)「Closed」是红色(=「cv::Scalar(00128)」)「Lasso」用淡蓝色(=「cv::Scalar(1281280)」)表现。
 
 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 预览版有公开更新本连载就会追加新的功能介绍。

转载于:https://www.cnblogs.com/TracePlus/p/4138615.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值