ブログ@kaorun55

HoloLensやKinectなどのDepthセンサーを中心に書いています。

NiTE2(Beta) 入門(1) :ユーザーを追跡する

ここからはNiTE2(Beta)を使ってみます。OpenNI v1.xとは異なり、ユーザー追跡やスケルトン追跡は完全にNiTEから制御するようになっているようです。そのほかジェスチャー認識、ポーズ認識、手の追跡を行うことができます。ただし、ジェスチャー認識はClick、Wave、HandRiseと大幅に減っています。

環境は次の通りです。OpenNI2(Beta)の環境があることが前提です。

とりあえずコンソールアプリでプロジェクトを作り、OpenNI2(Beta)の設定を行います。

そのほかにNiTE(Beta) をインストールすると、ヘッダーパス(NITE2_INCLUDE)およびライブラリパス(NITE2_LIB)が環境変数に追加されていますので、これをVCのプロパティに設定します。NiTE2(Beta)のライブラリはNITE2.libです。

で、次のコードを実行するとカラーカメラの画像が表示されます。なお、実行時には、exeの場所に、NiTE2(Beta)のRedistフォルダ(ここでは「C:\Program Files (x86)\PrimeSense\NiTE2\Redist」)にあるファイルとフォルダ一式をコピーし、VCのデバッグ時の作業パスを$(OutDir)に設定します。

コードはこんな感じです。

#include <iostream>

#include<opencv2\opencv.hpp>

#include <NiTE.h>

cv::Scalar colors[] = {

cv::Scalar( 1, 0, 0 ),

cv::Scalar( 0, 1, 0 ),

cv::Scalar( 0, 0, 1 ),

cv::Scalar( 1, 1, 0 ),

cv::Scalar( 1, 0, 1 ),

cv::Scalar( 0, 1, 1 ),

};

cv::Mat drawUser( nite::UserTrackerFrameRef& userFrame )

{

cv::Mat depthImage;

openni::VideoFrameRef depthFrame = userFrame.getDepthFrame();

if ( depthFrame.isValid() ) {

openni::VideoMode videoMode = depthFrame.getVideoMode();

depthImage = cv::Mat( videoMode.getResolutionY(),

videoMode.getResolutionX(),

CV_8UC4 );

openni::DepthPixel* depth = (openni::DepthPixel*)depthFrame.getData();

const nite::UserId* pLabels = userFrame.getUserMap().getPixels();

for (int i = 0; i < (depthFrame.getDataSize()/sizeof(openni::DepthPixel)); ++i){

// 画像インデックスを生成

int index = i * 4;

// 距離データを画像化する

UCHAR* data = &depthImage.data[index];

if ( pLabels[i] != 0 ) {

data[0] *= colors[pLabels[i]][0];

data[1] *= colors[pLabels[i]][1];

data[2] *= colors[pLabels[i]][2];

}

else {

// 0-255のグレーデータを作成する

// distance : 10000 = gray : 255

int gray = ~((depth[i] * 255) / 10000);

data[0] = gray;

data[1] = gray;

data[2] = gray;

}

}

}

return depthImage;

}

void main()

{

try {

nite::Status niteRet = nite::NiTE::initialize();

nite::UserTracker userTracker;

niteRet = userTracker.create();

if ( niteRet != nite::STATUS_OK ) {

throw std::runtime_error( "userTracker.create" );

}

cv::Mat depthImage;

while ( 1 ) {

nite::UserTrackerFrameRef userFrame;

userTracker.readFrame( &userFrame );

depthImage = drawUser( userFrame );

cv::imshow( "User", depthImage );

int key = cv::waitKey( 10 );

if ( key == 'q' ) {

break;

}

}

}

catch ( std::exception& ) {

std::cout << openni::OpenNI::getExtendedError() << std::endl;

}

}

まずはnite::NiTE::initialize()でNiTEを初期化します。

続いてUserTrakcerを作成します。この時、UserTracker::create() に認識させるデバイス(openni::Device)を指定することもできますが、何も指定しなければANY_DEVICEでよろしくやってくれるようです。

ユーザーデータの更新は UserTracker::readFrame() で行い、更新されたユーザーデータは nite::UserTrackerFrameRef に格納されます。OpenNIの待機関数 openni::OpenNI::waitForAnyStream() とは関連が無いようで、Colorカメラのデータと同期させるのはキレイなコードにならなそうです。

Depthデータについては、ユーザー認識に使用したDepthデータを取得できるので、それを drawUser() でグレー画像化しています。この時に、ピクセルごとのユーザーデータが nite::UserTrackerFrameRef::getUserMap().getPixels() で取得できます。ここで const nite::UserId* として、ピクセルごとのユーザーデータの先頭アドレスを取得できるので、ピクセルごとにユーザーがいれば、その座標に色付けします。

簡単に色付けはできましたが、前述の通りColorカメラとの同期方法がわかりません。OpenNIのColorとDepthも基本非同期なので、同じように考えればよいのかもしれません。

あとは、何人まで認識するか。は要調査ですね。Kinectの場合は、SDKの6人に引きずられると思いますが、Xtionは上限なかったはずなので。