Kinect for Windows v2入門 ― C++プログラマー向け連載(4)
Kinect v2プログラミング(C++) - BodyIndex編
Kinect SDK v2で、BodyIndex(人物領域)を取得する方法を、サンプルコードを示しながら説明する(正式版に合わせて改訂)。
前回は、Kinect for Windows v2(以下、Kinect v2)からKinect for Windows SDK v2 (以下、Kinect SDK v2)を用いてDepthデータを取得する方法を紹介した。
今回は、KinectからBodyIndex(=人物領域)を取得する方法を紹介する。
BodyIndex
Kinectは取得したDepthデータ(=センサー面からの距離情報)を基にした人物領域を取得できる。
人物領域は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)は異なる。
この人物領域は、Kinect SDK v1では「Player」と呼ばれていたが、Kinect SDK v2 では「BodyIndex」と名称が変更されている。
今回は、BodyIndexを取得する方法を紹介する。
サンプルプログラム
Kinect SDK v2でBodyIndexを取得し、人物ごとに色分けして表示するサンプルプログラムを示す。連載 第2回で紹介したデータを取得するステップごとに抜粋して解説する。このサンプルプログラムの全容は、以下のページで公開している。
また、Kinect SDK v1で同様にDepthデータを取得、可視化して表示するサンプルプログラムは、筆者の近著『Kinect for Windows SDK実践プログラミング』を参照していただきたい。
「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;
}
|
- 1Kinect v2を扱うためのSensorインターフェース。
- 2デフォルトのSensorを取得する。
- 3Sensorを開く。
「Source」
Sensor
からSource
を取得する。
// Source
IBodyIndexFrameSource* pBodyIndexSource; //……1
hResult = pSensor->get_BodyIndexFrameSource( &pBodyIndexSource ); //……2
if( FAILED( hResult ) ){
std::cerr << "Error : IKinectSensor::get_BodyIndexFrameSource()" << std::endl;
return -1;
}
|
- 1BodyIndexフレームのためのSourceインターフェース。
- 2SensorからSourceを取得する。
前回紹介したように、Kinect SDK v1ではPlayerはDepthと一緒に取得する必要があったが、Kinect SDK v2ではBodyIndexのみで取得できる。
「Reader」
Source
からReader
を開く。
// Reader
IBodyIndexFrameReader* pBodyIndexReader; //……1
hResult = pBodyIndexSource->OpenReader( &pBodyIndexReader ); //……2
if( FAILED( hResult ) ){
std::cerr << "Error : IBodyIndexFrameSource::OpenReader()" << std::endl;
return -1;
}
|
- 1BodyIndexフレームのためのReaderインターフェース。
- 2SourceからReaderを開く。
「Frame」~「Data」
Reader
から最新のFrame
を取得する。
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;
}
}
|
- 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 )
)に着色して可視化する。
Kinect SDK v1 | Kinect 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) |
実行結果
このサンプルプログラムを実行すると図3のようにKinect v2から取得した人物領域が色付けされて表示される。
手指の細かい形状まで取得できていることが分かる。
まとめ
今回は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画像を取得するサンプルプログラムを紹介する。正式版に合わせて改訂。
4. 【現在、表示中】≫ Kinect v2プログラミング(C++) - BodyIndex編
Kinect SDK v2で、BodyIndex(人物領域)を取得する方法を、サンプルコードを示しながら説明する(正式版に合わせて改訂)。
5. Kinect v2プログラミング(C++) - Body編
Kinect SDK v2に実装されている主要な機能の紹介は、一通り完了。今回は、Body(人物姿勢)を取得する方法を説明する(正式版に合わせて改訂)。