ブログ@kaorun55

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

NiTE2(Beta) 入門(3) :ポーズを検出する

NiTEの特長の一つである、ポーズの検出をやってみます。検出できるポーズは nite::PoseType に定義されており、現在のところPOSE_PSIとPOSE_CROSSED_HANDSの二つです。Psiは、OpenNI初期のスケルトン認識にも利用されていた、上の画像のようなポーズです。Crossed Handsは腕をクロスしたポーズで、一年くらい前に出ていたOpenNIのアプリケーションガイドラインにも載っていたポーズで、サンプルではアプリケーション終了のポーズに利用されています(下図)。

ここでは、ユーザー追跡の色付けが、Psiで黒で、Crossed Handsでそのままの色で表示するようにしてみます。

#include <iostream>

#include<opencv2\opencv.hpp>

#include <NiTE.h>

cv::Scalar defaultColors[6] = {

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::Scalar colors[6];

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 drawSkeleton( cv::Mat& depthImage, nite::UserTracker& userTracker,

nite::UserTrackerFrameRef& userFrame )

{

const nite::Array<nite::UserData>& users = userFrame.getUsers();

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

const nite::UserData& user = users[i];

if ( user.isNew() ) {

userTracker.startSkeletonTracking( user.getId() );

userTracker.startPoseDetection( user.getId(), nite::POSE_PSI );

userTracker.startPoseDetection( user.getId(), nite::POSE_CROSSED_HANDS );

}

else if ( !user.isLost() ) {

// スケルト

const nite::Skeleton& skeelton = user.getSkeleton();

if ( skeelton.getState() == nite::SkeletonState::SKELETON_TRACKED ) {

for ( int j = 0; j <= 14; ++j ) {

const nite::SkeletonJoint& joint =

skeelton.getJoint( (nite::JointType)j );

if ( joint.getPositionConfidence() >= 0.7f ) {

const nite::Point3f& position = joint.getPosition();

float x = 0, y = 0;

userTracker.convertJointCoordinatesToDepth(

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

cv::circle( depthImage, cvPoint( (int)x, (int)y ),

5, cv::Scalar( 0, 0, 255 ), 5 );

}

}

}

for ( int p = 0; p < 2; ++p ) {

// ポーズ

const nite::PoseData& pose = user.getPose( (nite::PoseType)p );

// ポーズ認識開始

if ( pose.isEntered() ) {

}

// ポーズ認識中

else if ( pose.isHeld() ) {

colors[user.getId()][0] = p;

colors[user.getId()][1] = p;

colors[user.getId()][2] = p;

}

// ポーズ認識終了

else if ( pose.isExited() ) {

colors[user.getId()][0] = defaultColors[user.getId()][0];

colors[user.getId()][1] = defaultColors[user.getId()][1];

colors[user.getId()][2] = defaultColors[user.getId()][2];

}

}

}

}

}

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;

for ( int i = 0; i < 6; ++i ) {

colors[i] = defaultColors[i];

}

while ( 1 ) {

nite::UserTrackerFrameRef userFrame;

userTracker.readFrame( &userFrame );

depthImage = drawUser( userFrame );

drawSkeleton( depthImage, userTracker, userFrame );

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

int key = cv::waitKey( 10 );

if ( key == 'q' ) {

break;

}

}

}

catch ( std::exception& ) {

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

}

}

前回のスケルトン追跡のコードをベースにしています。

追加した部分として、一つ目にユーザーの追跡を開始した段階で、スケルトンの追跡のほかにポーズの検出をnite::UserTracker::startPoseDetection()開始しています。引数にユーザーIDと検出するポーズを登録することで、そのユーザーのポーズの検出を開始します。

ポーズの検出状態はnite::UserData::getPose() で指定したポーズに対して取得します。ポーズの検出状態はnite::PoseDataに格納され、nite::PoseData::isEntered()でポーズの検出を開始、nite::PoseData::isHeld()でポーズの検出中、nite::PoseData::isExited()でポーズが検出できなくなった状態を取得できます。

今回はnite::PoseData::isHeld()でポーズの種類によって描画色を変え、nite::PoseData::isExited()で元に戻しています。