ブログ@kaorun55

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

NiTE2(Beta) 入門(5) :手を追跡する

NiTE(Beta)入門の最後として、手の追跡を行います。コードは前回の続きです。ジェスチャーを検出したときに、その時の手の位置を合わせて取得することができます。その位置を起点に手を追跡させます。今回は、その位置をいくつか保存しておき、手の軌跡を表示してみます。

#include <iostream>

#include <list>

#include<opencv2\opencv.hpp>

#include <NiTE.h>

cv::Mat convertDepthToColor( openni::VideoFrameRef& depthFrame )

{

cv::Mat depthImage = cv::Mat( depthFrame.getVideoMode().getResolutionY(),

depthFrame.getVideoMode().getResolutionX(),

CV_8UC4 );

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

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

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

int index = i * 4;

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

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

// 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::HandTracker handTracker;

niteRet = handTracker.create();

if (niteRet != nite::STATUS_OK) {

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

}

handTracker.startGestureDetection( nite::GestureType::GESTURE_WAVE );

cv::Mat depthImage;

std::list<nite::Point3f> handPoints;

while ( 1 ) {

// 手の追跡フレームを取得する

nite::HandTrackerFrameRef handTrackerFrame;

handTracker.readFrame( &handTrackerFrame );

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

if ( depthFrame.isValid() ) {

depthImage = convertDepthToColor( depthFrame );

}

// ジェスチャーを認識したら、その点から手を追跡する

const nite::Array<nite::GestureData>& gestures =

handTrackerFrame.getGestures();

for ( int i = 0; i < gestures.getSize(); ++i ) {

if ( gestures[i].isComplete() ) {

nite::HandId newId;

handTracker.startHandTracking( gestures[i].getCurrentPosition(), &newId );

}

}

// 手を追跡していたら、その点を記録する

const nite::Array<nite::HandData>& hands = handTrackerFrame.getHands();

for (int i = 0; i < hands.getSize(); ++i) {

if ( hands[i].isTracking() ) {

handPoints.push_back( hands[i].getPosition() );

if ( handPoints.size() > 30 ) {

handPoints.pop_front();

}

}

}

// 手の軌跡を表示する

for ( auto it = handPoints.begin(); it != handPoints.end(); ) {

auto convertHandCoordinatesToDepth =

[&] ( nite::Point3f& position ) -> cv::Point {

float x = 0, y = 0;

handTracker.convertHandCoordinatesToDepth(

position.x, position.y, position.z, &x, &y );

return cv::Point( x, y );

};

cv::Point start = convertHandCoordinatesToDepth( *it );

if ( ++it == handPoints.end() ) {

break;

}

cv::Point end = convertHandCoordinatesToDepth( *it );

cv::line( depthImage, start, end, cv::Scalar( 0, 255, 0 ), 3 );

}

cv::imshow( "Hnad Tracker", depthImage );

int key = cv::waitKey( 10 );

if ( key == 'q' ) {

break;

}

}

}

catch ( std::exception& ) {

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

}

}

ジェスチャーの検出ができたときに取得できる位置(nite::GestureData::getCurrentPosition() )をnite::HandTracker::startHandTracking()に渡して手の追跡を開始します。

手の追跡情報はnite::HandTrackerFrameRef::getHands()でnite::HandDataの配列として取得できます。手が追跡状態(nite::HandData::isTracking() )であれば、nite::HandData::getPosition()で取得できる3次元の位置座標を保存します。

続いて描画ですが、3次元座標をDepthカメラの2次元座標に変換するnite::HandTracker::convertHandCoordinatesToDepth() で座標変換し、グレー画像化したDepthデータ上に描画します。