【翻譯】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 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 預覽版有公開更新本連載就會追加新的功能介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM