Kinect v2程序設計(C++) Body 篇 背景移除 骨骼跟蹤 手勢識別

【翻譯】Kinect v2程序設計(C++) Body 篇

http://www.cnblogs.com/TracePlus/p/4138615.html
http://www.cnblogs.com/TracePlus/p/4136368.html
圖3 BodyIndex數據
  Kinect SDK v1 Kinect SDK v2預覽版
名稱 Player BodyIndex
檢測支持人數 6人 6人
人體領域的値 1~6 0~5
非人體領域的値 0 255(0xff)
表1 Kinect SDK v1和Kinect SDK v2預覽版的人體區域(Player,BodyIndex)的比較
示例程序是,把BodyIndex的值人體區域color table的顏色(=「cv::Vec3b(BGR )」)在非人體區域用黑色(=「cv::Vec3b(000)」)進行着色來實現可視化。



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預覽版取得BodyColor圖像疊加顯示爲「●(圓點)」的示例程序展示。還有基於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傳感器的位置是分開的因此需要把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( 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 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預覽版有公開更新本連載就會追加新的功能介紹。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章