2011/08/15

Kinect SDK(4) ~スケルトントラッキング利用編~

皆さんお久しぶりです!ゆゆです。
合宿やら実験準備やらでアワアワしてましたが、少し時間ができたのでKinectについての記事を更新したいと思います。


今回は、きっとみんなお待ちかね(?)のスケルトントラッキング・骨格推定をしたいと思います!Kinectの真骨頂ですね。

まずはサンプルプログラムを…

/*
Kinect の 深度センサを表示するプログラム
*/

#include<Windows.h>
#include<MSR_NuiApi.h>//KinectSDK利用時にinclude
#include<opencv2\opencv.hpp>//今回はOpenCVを利用します。


//各種libファイルの設定
(略)
//ハンドル
HANDLE m_pDepthStreamHandle;
HANDLE m_hNextEvent[2];


//画像データ
cv::Ptr<IplImage> DepthImage;
cv::Ptr<IplImage> SkeletonImage;

//RGBデータを取得する関数
int GetDepthImage();
RGBQUAD Nui_ShortToQuad_Depth( USHORT s );

void GetSkeleton();

CvScalar green;

int main(){
    //Kinectの初期化関数
    NuiInitialize( NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON );

    //各ハンドルの設定
    m_hNextEvent[0] = CreateEvent( NULL, TRUE, FALSE, NULL );
    m_hNextEvent[1] = CreateEvent( NULL, TRUE, FALSE, NULL );
    m_pDepthStreamHandle   = NULL;

    //深度センサストリームの設定
    HRESULT hr = NuiImageStreamOpen(NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX , NUI_IMAGE_RESOLUTION_320x240 , 0 , 2 , m_hNextEvent[0] , &m_pDepthStreamHandle );
    if( FAILED( hr ) ) return -1;//取得失敗
    //スケルトントラッキングの設定
    hr = NuiSkeletonTrackingEnable( m_hNextEvent[1], 0 );
    if( FAILED( hr ) ) return -1;//取得失敗
    
    //OpenCVの設定
    cvNamedWindow("Kinect Depth and Player Image",CV_WINDOW_AUTOSIZE);
    DepthImage = cvCreateImage( cvSize (320, 240) , IPL_DEPTH_8U , 3);
    SkeletonImage = cvCreateImage( cvSize (320, 240) , IPL_DEPTH_8U , 3);

    green.val[0] = 0;green.val[1] = 255;green.val[2] = 0;green.val[3] = 255;

    while(cvWaitKey(10)!='q'){
        //次のRGBフレームが来るまで待機
        WaitForMultipleObjects(2,m_hNextEvent,TRUE,INFINITE);
        
        //取得をする
        if(GetDepthImage()==-1)return -2;

        //表示
        cvShowImage ("Kinect Depth and Player Image",DepthImage);
        GetSkeleton();
        cvShowImage ("Skeleton",SkeletonImage);
    }

    cvDestroyAllWindows();
    NuiShutdown();

    return 0;
}


int GetDepthImage()
(略)
RGBQUAD Nui_ShortToQuad_Depth( USHORT s )
(略)
void GetSkeleton(){
    //スケルトンフレームクラス
    NUI_SKELETON_FRAME SkeletonFrame;
    //スケルトンフレームの取得
    HRESULT hr = NuiSkeletonGetNextFrame(0,&SkeletonFrame);
    if(FAILED(hr))return;

    //表示クリア
    cvZero(SkeletonImage);
    
    for(int i=0;i<NUI_SKELETON_COUNT;i++){//ユーザーID分回す
        if(SkeletonFrame.SkeletonData[i].eTrackingState == NUI_SKELETON_TRACKED){
            NUI_SKELETON_DATA data = SkeletonFrame.SkeletonData[i];//i番目のユーザーのスケルトンデータの格納
            for(int j=0;j<NUI_SKELETON_POSITION_COUNT;j++){//取得できる骨格分forで回す。
                float x,y;
                //3次元データをデプスマップ上の二次元座標に対応付ける。
                NuiTransformSkeletonToDepthImageF(data.SkeletonPositions[j], &x, &y );
                CvPoint point;
                point.x = (int)(x * 320 + 0.5f);
                point.y = (int)(y * 240 + 0.5f);
                //描画
                cvCircle(SkeletonImage,point,3,green,3);
            }
        }
    }
}
実行結果はこんな感じです。
スケルトントラッキング
ユーザの骨格位置情報が取得できているのがわかりますね!

では、プログラムを見ていきましょう!

・初期設定
//スケルトントラッキングの初期設定
hr = NuiSkeletonTrackingEnable( m_hNextEvent[1], 0 );
まずは初期設定です。NuiSkeletonTrackingEnable関数を使うことで、スケルトントラッキングの初期化を行います。
第一引数にはハンドルを、第二引数は未実装なので気にしなくて大丈夫です。

・スケルトンフレームの取得
//スケルトンフレームクラス
NUI_SKELETON_FRAME SkeletonFrame;
//スケルトンフレームの取得
HRESULT hr = NuiSkeletonGetNextFrame(0,&SkeletonFrame);
これでスケルトンフレームを取得できます。スケルトンフレームには全てのユーザのスケルトントラッキング情報が入っております。

・各ユーザーの骨格情報の取得
//スケルトンデータの取得
SkeletonFrame.SkeletonData[i]
スケルトンフレームのメンバ変数:SkeletonData(型はNUI_SKELETON_DATA)配列にそれぞれのユーザのスケルトンデータが入っています。ユーザデータは最高6人まで取得できます。

また、NUI_SKELETON_DATAのメンバ変数.eTrackingStateがNUI_SKELETON_TRACKEDの場合、そのユーザはそのフレームにおいてトラッキングされているということになります。他にeTrackingStateは以下のような数値をとります。
  • NUI_SKELETON_NOT_TRACKED
  • NUI_SKELETON_POSITION_ONLY
  • NUI_SKELETON_TRACKED


各骨格の位置情報はNUI_SKELETON_DATAのメンバ変数SkeletonPositions[]配列に入っています。骨格は列挙変数によって指定することができ、例えば頭の位置が欲しいときは

SkeletonPositions[NUI_SKELETON_POSITION_HEAD]

のようにします。他に指定できる数値は
  • NUI_SKELETON_POSITION_HIP_CENTER
  • NUI_SKELETON_POSITION_SPINE
  • NUI_SKELETON_POSITION_SHOULDER_CENTER
  • NUI_SKELETON_POSITION_HEAD
  • NUI_SKELETON_POSITION_SHOULDER_LEFT
  • NUI_SKELETON_POSITION_ELBOW_LEFT
  • NUI_SKELETON_POSITION_WRIST_LEFT
  • NUI_SKELETON_POSITION_HAND_LEFT
  • NUI_SKELETON_POSITION_SHOULDER_RIGHT
  • NUI_SKELETON_POSITION_ELBOW_RIGHT
  • NUI_SKELETON_POSITION_WRIST_RIGHT
  • NUI_SKELETON_POSITION_HAND_RIGHT
  • NUI_SKELETON_POSITION_HIP_LEFT
  • NUI_SKELETON_POSITION_KNEE_LEFT
  • NUI_SKELETON_POSITION_ANKLE_LEFT
  • NUI_SKELETON_POSITION_FOOT_LEFT
  • NUI_SKELETON_POSITION_HIP_RIGHT
  • NUI_SKELETON_POSITION_KNEE_RIGHT
  • NUI_SKELETON_POSITION_ANKLE_RIGHT
  • NUI_SKELETON_POSITION_FOOT_RIGHT
  • NUI_SKELETON_POSITION_COUNT
となります。


・3次元座標データ→二次元データへの変換
//二次元データへの変換
NuiTransformSkeletonToDepthImageF(data.SkeletonPositions[j], &x, &y );
SkeletonPositionsに入っているデータは3次元上のデータなので、OpenCV等で表示させるためには
二次元上のデータにする必要があります。その時にNuiTransformSkeletonToDepthImageF関数を入れます。
第一引数に変換したい三次元位置情報を、第二引数第三引数に格納先をfloatのポインタで指定します。



以上でスケルトントラッキングが利用できるようになります。骨格情報を取得できれば様々なインタラクションに応用できると思うのでぜひマスターしましょう!

0 件のコメント:

コメントを投稿