Kinect for Windows v2入門 ― C++プログラマー向け連載(4)

Kinect for Windows v2入門 ― C++プログラマー向け連載(4)

Kinect v2プログラミング(C++) - BodyIndex編

2014年12月25日 改訂 (初版:2014/03/10)

Kinect SDK v2で、BodyIndex(人物領域)を取得する方法を、サンプルコードを示しながら説明する(正式版に合わせて改訂)。

杉浦 司(Microsoft MVP for Kinect for Windows)
  • このエントリーをはてなブックマークに追加

 前回は、Kinect for Windows v2(以下、Kinect v2)からKinect for Windows SDK v2 (以下、Kinect SDK v2)を用いてDepthデータを取得する方法を紹介した。

 今回は、KinectからBodyIndex(=人物領域)を取得する方法を紹介する。

BodyIndex

 Kinectは取得したDepthデータ(=センサー面からの距離情報)を基にした人物領域を取得できる。

図1 Kinect SDK v2のサンプルプログラム(BodyBasics)

BodyIndexをColorの座標ヘ位置合わせして切り取るだけだが、手指の細かい部分まできれいに人物を切り抜けている。

 人物領域はDepthデータを基にしているため、Depthセンサーの解像度と同じになる。前回紹介したように、Kinect v2 ではDepthセンサーの解像度や分解能が大幅に向上しているため、Kinect v1に比べ、手指などの細かい部分も人物領域として崩れることなく取得できるようになった。

 また、人物が検出可能な範囲がKinect v1は0.8~4.0mであったが、Kinect v2は0.5~4.5mと広くなっている(図2)*1

 ただし、検出できる人物領域はどちらも6人までと変わらない。

  • *1 Depthデータが取得可能な範囲(0.5~8.0m)と人物が検出できる範囲(0.5~4.5m)は異なる。
図2 Kinect v2のDepthの取得範囲と人物の検出範囲(再掲)

 この人物領域は、Kinect SDK v1では「Player」と呼ばれていたが、Kinect SDK v2 では「BodyIndex」と名称が変更されている。

 今回は、BodyIndexを取得する方法を紹介する。

サンプルプログラム

 Kinect SDK v2でBodyIndexを取得し、人物ごとに色分けして表示するサンプルプログラムを示す。連載 第2回で紹介したデータを取得するステップごとに抜粋して解説する。このサンプルプログラムの全容は、以下のページで公開している。

図2 Kinect SDK v2のデータ取得の流れ(再掲)

 また、Kinect SDK v1で同様にDepthデータを取得、可視化して表示するサンプルプログラムは、筆者の近著『Kinect for Windows SDK実践プログラミング』を参照していただきたい。

「Sensor」

 Sensorを取得する。

C++
// 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 図2の「Sensor」に該当する部分(再掲)
  • 1Kinect v2を扱うためのSensorインターフェース。
  • 2デフォルトのSensorを取得する。
  • 3Sensorを開く。

「Source」

 SensorからSourceを取得する。

C++
// Source
IBodyIndexFrameSource* pBodyIndexSource;  //……1
hResult = pSensor->get_BodyIndexFrameSource( &pBodyIndexSource );  //……2
if( FAILED( hResult ) ){
  std::cerr << "Error : IKinectSensor::get_BodyIndexFrameSource()" << std::endl;
  return -1;
}
リスト1.2 図2の「Source」に該当する部分
  • 1BodyIndexフレームのためのSourceインターフェース。
  • 2SensorからSourceを取得する。

 前回紹介したように、Kinect SDK v1ではPlayerはDepthと一緒に取得する必要があったが、Kinect SDK v2ではBodyIndexのみで取得できる。

「Reader」

 SourceからReaderを開く。

C++
// Reader
IBodyIndexFrameReader* pBodyIndexReader;  //……1
hResult = pBodyIndexSource->OpenReader( &pBodyIndexReader );  //……2
if( FAILED( hResult ) ){
  std::cerr << "Error : IBodyIndexFrameSource::OpenReader()" << std::endl;
  return -1;
}
リスト1.3 図2の「Reader」に該当する部分
  • 1BodyIndexフレームのためのReaderインターフェース。
  • 2SourceからReaderを開く。

「Frame」~「Data」

 Readerから最新のFrameを取得する。

C++
int width = 512;  //……1
int height = 424;  //……1
cv::Mat bodyIndexMat( height, width, CV_8UC3 );  //……2
cv::namedWindow( "BodyIndex" );

// Color Table
cv::Vec3b color[BODY_COUNT];  //……3
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 ){
  // Frame
  IBodyIndexFrame* pBodyIndexFrame = nullptr;  //……4
  hResult = pBodyIndexReader->AcquireLatestFrame( &pBodyIndexFrame );  //……5
  if( SUCCEEDED( hResult ) ){
    unsigned int bufferSize = 0;
    unsigned char* buffer = nullptr;
    hResult = pBodyIndexFrame->AccessUnderlyingBuffer( &bufferSize, &buffer );  ……6
    if( SUCCEEDED( hResult ) ){
      for( int y = 0; y < height; y++ ){
        for( int x = 0; x < width; x++ ){
          unsigned int index = y * width + x;
          if( buffer[index] != 0xff ){
            bodyIndexMat.at<cv::Vec3b>( y, x ) = color[buffer[index]];  //……7
          }
          else{
            bodyIndexMat.at<cv::Vec3b>( y, x ) = cv::Vec3b( 0, 0, 0 );  //……7
          }
        }
      }
    }
  }
  SafeRelease( pBodyIndexFrame );  //……8

  // Show Window
  cv::imshow( "BodyIndex", bodyIndexMat );
  if( cv::waitKey( 30 ) == VK_ESCAPE ){
    break;
  }
}
リスト1.4 図2の「Frame」~「Data」に該当する部分
  • 1BodyIndexのサイズ(512×424)。
    ここでは説明の簡素化のため画像サイズを決め打ちしているが、サンプルプログラムではSourceからフレーム情報を取得している。
  • 2BodyIndexから人物領域を描画するためのOpenCVのcv::Mat型を準備する。
  • 3人物領域を色付けするためのカラーテーブル。
  • 4BodyIndexを取得するためのFrameインターフェース。
  • 5Readerから最新のFrameを取得する。
  • 6FrameからBodyIndexを取得する。
    BodyIndexが格納された配列のポインターを取得する。
  • 7BodyIndexから人物領域を描画する。
    BodyIndexごとにカラーテーブルを参照して色付けする。
  • 8Frameを解放する。
    内部バッファが解放されて次のデータを取得できる状態になる。

 Frameが取得できたらBodyIndexのデータを取り出す。

 取り出したBodyIndexのデータは、図3のように人物領域と非人物領域にそれぞれ値が入っている。

 Kinect SDK v1の「Player」では、人物領域に「1」~「6」、非人物領域に「0」が入っていたが、Kinect SDK v2の「BodyIndex」では、人物領域に「0」~「5」、非人物領域に「255(0xff)」が入っている(表1)。

 サンプルプログラムでは、BodyIndexの値を見て、人物領域には対応するカラーテーブルの色(=cv::Vec3b( B,G,R ))に、非人物領域には黒色(=cv::Vec3b( 0, 0, 0 ))に着色して可視化する。

図3 BodyIndexデータ
Kinect SDK v1Kinect SDK v2
名称 Player BodyIndex
検出可能範囲 0.8~4.0m
(Near Mode 0.4~3.0m)
0.5~4.5m
検出可能人数 6人 6人
人物領域の値 1~6 0~5
非人物領域の値 0 255(0xff)
表1 Kinect SDK v1とKinect SDK v2の人物領域(Player、BodyIndex)の比較

実行結果

 このサンプルプログラムを実行すると図3のようにKinect v2から取得した人物領域が色付けされて表示される。

図4 実行結果
図4 実行結果

手指の細かい形状まで取得できていることが分かる。

まとめ

 今回はKinect SDK v2でBodyIndexを取得するサンプルプログラムを紹介した。次回はBody(=人物姿勢)を取得するサンプルプログラムを紹介する。

※以下では、本稿の前後を合わせて5回分(第1回~第5回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

1. Kinect v1とKinect v2の徹底比較

Kinect for Windowsの旧版と、次世代型の新版を比較しながら、進化したハードウェア&ソフトウェアをC++開発者向けに紹介する(正式版に対応させた改訂連載スタート)。今回はセンサー仕様や動作要件を徹底的に比較する。

2. Kinect v2プログラミング(C++) - Color編

Kinect SDK v2で、データを取得する基本的な流れを説明。Color画像を取得するサンプルプログラムを紹介する。正式版に合わせて改訂。

3. Kinect v2プログラミング(C++) - Depth編

Kinect SDK v2プレビュー版で、Depthデータを取得する方法を説明する(改訂版)。

4. 【現在、表示中】≫ Kinect v2プログラミング(C++) - BodyIndex編

Kinect SDK v2で、BodyIndex(人物領域)を取得する方法を、サンプルコードを示しながら説明する(正式版に合わせて改訂)。

5. Kinect v2プログラミング(C++) - Body編

Kinect SDK v2に実装されている主要な機能の紹介は、一通り完了。今回は、Body(人物姿勢)を取得する方法を説明する(正式版に合わせて改訂)。

イベント情報(メディアスポンサーです)

Azure Central の記事内容の紹介

GrapeCity Garage 記事内容の紹介

Twitterでつぶやこう!


Build Insider賛同企業・団体

Build Insiderは、以下の企業・団体の支援を受けて活動しています(募集概要)。

ゴールドレベル

  • グレープシティ株式会社
  • 日本マイクロソフト株式会社