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データ上に描画します。