NtKinect: Kinect V2 C++ Programming with OpenCV on Windows10

Kinect V2 で骨格を認識する


2016.07.16: created by
Japanese English
目次へ

前提として理解しておくべき知識


骨格認識

「骨格を認識する」とはすなわち「ある人間の関節の位置情報を取得する」ということです。 関節を表す型は Joint で、Kinect for Windows SDK 2.0 では次のように定義されています。

Kinet V2における関節の定義

「関節」に関するデータは Joint という型で表されます。 Joint 型は

というメンバ変数で構成された構造体です。 Jointの集まりが人間ひとりの骨格情報になります。

Kinect for Windows SDK 2.0 の Kinect.h(抜粋)
enum _JointType {
    JointType_SpineBase= 0,
    JointType_SpineMid= 1,
    JointType_Neck= 2,
    JointType_Head= 3,
    JointType_ShoulderLeft= 4,
    JointType_ElbowLeft= 5,
    JointType_WristLeft= 6,
    JointType_HandLeft= 7,
    JointType_ShoulderRight= 8,
    JointType_ElbowRight= 9,
    JointType_WristRight= 10,
    JointType_HandRight= 11,
    JointType_HipLeft= 12,
    JointType_KneeLeft= 13,
    JointType_AnkleLeft= 14,
    JointType_FootLeft= 15,
    JointType_HipRight= 16,
    JointType_KneeRight= 17,
    JointType_AnkleRight= 18,
    JointType_FootRight= 19,
    JointType_SpineShoulder= 20,
    JointType_HandTipLeft= 21,
    JointType_ThumbLeft= 22,
    JointType_HandTipRight= 23,
    JointType_ThumbRight= 24,
    JointType_Count= ( JointType_ThumbRight + 1 ) 
};

enum _TrackingState {
    TrackingState_NotTracked= 0,
    TrackingState_Inferred= 1,
    TrackingState_Tracked= 2
};

typedef struct _Joint {
    JointType JointType;
    CameraSpacePoint Position;
    TrackingState TrackingState;
} Joint;

NtKinect の骨格情報に関するメソッド

返り値の型 メソッド名 説明
void setSkeleton() 骨格認識を行い、次のメンバ変数に値を設定する。
変数名説明
vector<vector<Joint>> skeleton骨格情報
vector<int> skeletonId骨格の bodyIndex
vector<UINT64> skeletonTrackingId骨格のtrackingId

NtKinect の骨格情報に関するメンバ変数

変数名 説明
vector<vector<Joint>> skeleton 骨格情報のベクタ。 一人の人間の関節の集合が vector<Joint> であり、これが骨格情報です。 複数の人間を扱うため骨格情報のベクタ、すなわち vector<vector<Joint>> となります。
関節の座標は CameraSpace 座標系における位置です。
  • 認識した人数 --- sekeleton.size()
  • index 番目の人間の骨格情報 --- sekeleton[index ]
  • index 番目の人間の関節情報の数 --- sekeleton[index ].size()
  • index 番目の人間のjointType の関節の情報 --- sekeleton[index ][jointType ]
  • index 番目の人間のjointType の関節の座標 --- sekeleton[index ][jointType ].Position
  • index 番目の人間のjointType の関節の追跡状況 --- sekeleton[index ][jointType ].TrackingState
skelton[index ][jointType ].JointType == jointType が成り立ちますので、特定の骨格の特定の関節の情報に直接アクセスすることができます。
vector<int> skeletonId 骨格に対応するbodyIndexのベクタ。
skeleton[index ] の bodyIndex は skeletonId[index ] に保持されます。
vector<UINT64> skeletonTrackingId 骨格に対応するtrackingIdのベクタ。
skeleton[index ] の trackingId は skeletonTrackingId[index ] に保持されます。

Kinect V2 の3種類の座標系

データの種類によって、それを計測するセンサーの位置や解像度が異なります。 そのため、実世界での同じ位置の状態が、センサーによってそれぞれの座標系で表現された値として得られます。 異なるセンサーから得られたデータを同時に利用する場合は、 座標系の変換を行なってどちらかの座標系に合わせる必要があります。

Kinect V2 では、ColorSpace, DepthSpace, CameraSpace という3つの座標系があって、 それぞれの座標を表すデータ型 ColorSpacePoint, DepthSpacePoint, CameraSpacePoint が存在します。

Kinect for Windows SDK 2.0 の Kinect.h(抜粋)
typedef struct _ColorSpacePoint {
    float X;
    float Y;
} ColorSpacePoint;

typedef struct _DepthSpacePoint {
    float X;
    float Y;
} DepthSpacePoint;

typedef struct _CameraSpacePoint {
    float X;
    float Y;
    float Z;
} CameraSpacePoint;

Kinect V2 の座標系とデータ型

RGB画像, Depth画像, 関節情報ではそれぞれ使っている座標系が異なります。 RGB画像の座標系は ColorSpace で、Depth画像の座標系は DepthSpace, 関節情報の座標系は CameraSpace です。

座標系位置を表す型データの種類
ColorSpaceColorSpacePointRGB画像
DepthSpaceDepthSpacePointdepth画像, bodyIndex画像, 赤外線画像
CameraSpaceCameraSpacePointskeleton情報



関節の位置を表すCameraSpace 座標系

CameraSpace 座標系は、

  • 原点にKinect V2 があり、
  • カメラのレンズの向きがz軸の正方向で、
  • 垂直上方向がy軸の正方向である、
  • 右手系
3次元座標系です。 すなわち、CameraSpace, ColorSpace, DepthSpace という3種類の座標系のすべてにおいて、 「Kinect V2 に対面しているユーザから見て左から右への水平方向が x 軸の正方向」になります。 「Kinect V2 に対面しているユーザからみてあたかも鏡に写っている画像のように」 データが取得されて表示されると考えれば分かりやすいと思います。
(2016/11/12 図を変更し、説明を追記しました)。

座標系の変換に関するKinect V2のメソッド

Kinect V2 の ICoordinateMapper クラス が保持する「座標系の変換用メソッド」は次の通りです。

返り値の型 メソッド名 説明
HRESULT MapCameraPointToColorSpace(
    CameraSpacePoint sp ,
    ColorSpacePoint *cp )
CameraSpace 座標系での座標 sp を ColorSpace 座標系での座標に変換してcp にセットする。 返り値はS_OKかエラーコード。
HRESULT MapCameraPointToDepthSpace(
  CameraSpacePoint sp ,
  DelpthSpacePoint *dp )
CameraSpace 座標系での座標 sp を DepthSpace 座標系での座標に変換してdp にセットする。 返り値はS_OKかエラーコード。
HRESULT MapDepthPointToColorSpace(
  DepthSpacePoint dp ,
  UINT16 depth ,
  ColorSpacePoint *cp )
DepthSpace 座標系での座標 dp と距離depth から ColorSpace 座標系での座標に変換してcp にセットする。 返り値はS_OKかエラーコード。
HRESULT MapDepthPointToCameraSpace(
  DepthSpacePoint dp ,
  UINT16 depth ,
  CameraSpacePoint *sp )
DepthSpace 座標系での座標 dp と距離depth から CameraSpace 座標系での座標に変換してsp にセットする。 返り値はS_OKかエラーコード。

座標系の変換に関する NtKinect のメンバ変数

Kinect V2 で座標系の変換に使う ICoordinateMapper クラス のインスタンスは、 NtKinect のメンバ変数 coordinateMapper に保持されています。

変数名 説明
CComPtr<ICoordinateMapper> coordinateMapper 座標変換を行う ICoordinateMapperのインスタンス。

プログラム作成の手順

  1. NtKinect: Kinect V2 でRGBカメラ画像を取得する(基本設定)」 の Visual Studio のプロジェクト KinectV2.zipを用いて作成します。
  2. main.cppの内容を以下のように変更します。
  3. kinect.setSkeleton()メソッドを呼び出して骨格情報を kinect.skeleton に取得します。 関節の位置は CameraSpace 座標系で表現されているので、 ColorSpace 座標系の座標に変換してから RGB画像上に四角形を描画しています(緑字部分)。

    Joint型のデータが意味を持つのは、その TrackingState メンバ変数の値が TrackingState_Tracked か TrackingState_Inferred である場合だけなので、 プログラム中では TrackingState_NotTracked である場合はその関節の表示処理をスキップしています。

    main.cpp
    #include <iostream>
    #include <sstream>
    
    #include "NtKinect.h"
    
    using namespace std;
    
    void doJob() {
      NtKinect kinect;
      while (1) {
        kinect.setRGB();
        kinect.setSkeleton();
        for (auto person : kinect.skeleton) {
          for (auto joint : person) {
            if (joint.TrackingState == TrackingState_NotTracked) continue;
            ColorSpacePoint cp;
            kinect.coordinateMapper->MapCameraPointToColorSpace(joint.Position,&cp);
            cv::rectangle(kinect.rgbImage, cv::Rect((int)cp.X-5, (int)cp.Y-5,10,10), cv::Scalar(0,0,255),2);
          }
        }
        cv::imshow("rgb", kinect.rgbImage);
        auto key = cv::waitKey(1);
        if (key == 'q') break;
      }
      cv::destroyAllWindows();
    }
    
    int main(int argc, char** argv) {
      try {
        doJob();
      } catch (exception &ex) {
        cout << ex.what() << endl;
        string s;
        cin >> s;
      }
      return 0;
    }
    
    
  4. プログラムを実行するとRGB画像が表示されます。'q' キーで終了します。
  5. RGB画像の上に、認識された関節が赤い四角形で示されます。OpenCVでは色をBGRの順番で表現するのが 普通なので cv::Scalar(0,0,255)は (青, 緑, 赤)=(0,0,255)、すなわち赤を表していることに注意しましょう。




  6. サンプルのプロジェクトはこちら KinectV2_skeleton.zip
  7. 上記のzipファイルには必ずしも最新の NtKinect.h が含まれていない場合があるので、 こちらから最新版をダウンロードして 差し替えてお使い下さい。



http://nw.tsuda.ac.jp/