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

Kinect V2 で RGB画像とDepth 画像を対応させる


2016.07.19: created by
Japanese English
目次へ

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


Kinect V2 でRGBカメラ画像を取得する

Kinect V2 では 1920x1080 の解像度の RGB カメラ画像を取得できます。 OpenCVが GBR フォーマットを基本として用いていることから、 NtKinect でもGBRAフォーマットを採用しています。

NtKinect の RGB 画像に関するメソッド

返り値の型 メソッド名 説明
void setRGB() RGB画像を取得し、publicなメンバ変数 rgbImage に設定する。

NtKinect の RGB 画像に関するメンバ変数

変数名 説明
cv::Mat rgbImage RGBカメラの画像。解像度は1920x1080でBGRAフォーマット。
画像の座標は ColorSpace 座標系における位置です。
  • rgbImage.cols --- 画像の横方向の解像度 (1920)
  • rgbImage.rows --- 画像の縦方向の解像度 (1080)
  • rgbImage.at<cv::Vec4b>(y , x ) --- 画像の (x , y ) 座標の画素にアクセスする
  •         cv::Vec4b pixel = rgbImage.at<cv::Vec4b>(y,x);
                piexel[0] // Blue
                piexel[1] // Green
                piexel[2] // Red
                piexel[3] // Alpha
    

Depth画像

512x424の解像度でDepth(距離、深度)画像を取得することができます。 測定可能な距離の範囲は 500mm 〜 8000mm ですが、 人間を認識できる範囲は 500mm 〜 4500mm です。

Kinect20.lib において IDepthFrameSource は get_DepthMinReliableDistance()メソッドと get_DepthMaxReliableDistance()メソッドを持っていますが、 それぞれが返す値は 500 と 4500 です。

得られたDepth画像はピクセル毎に UINT16 (16bit 符号なし整数) で表現されます。

NtKinect の Depth 画像に関するメソッド

返り値の型 メソッド名 説明
void setDepth(bool raw = true) メンバ変数 depthImage に Depth 画像をセットする。
引数が「なし」か「true」で呼び出された場合、各画素には距離がmm単位で設定される。
引数が「false」で呼び出された場合、各画素には距離を 65535/4500 倍した値が設定される。 すなわち 0〜4500 (mm) という距離を0 (黒) 〜 65535 (白) という白黒画像の輝度にマップした画像になる。

NtKinect の Depth 画像に関するメンバ変数

変数名 説明
cv::Mat depthImage Depth 画像。 512x424の大きさで、各ピクセルは UINT16 で表現される。
画像の座標は DepthSpace 座標系における位置となる。
  • depthImage.cols --- 画像の横方向の解像度 (512)
  • depthImage.rows --- 画像の縦方向の解像度 (424)
  • depthImage.at<UINT16>(y , x ) --- 画像の (x , y ) 座標の画素にアクセスする
  •         UINT16 depth = rgbImage.at<UINT16>(y , x );
    

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 2015 のプロジェクト KinectV2.zipを用いて作成します。
  2. main.cppの内容を以下のように変更します。
  3. kinect.setDepth()メソッドを呼び出して kinect.depthImage に距離データを設定します。 引数を指定していないので、距離データそのものを、すなわち、 対象までの距離をmm単位で取得できます。

    2000mm よりも遠い画素を青で塗ることで、近くのものの映像だけを表示します。 500mmよりも小さい値は測定できていないだけなので、その画素は 8000mm 以遠として扱っています。

    RGB画像とDepth画像は解像度が異なるので、Depth画像の1ピクセルを RGB画像の4ピクセルに対応させて 矩形領域を処理しています。

    main.cpp
    #include <iostream>
    #include <sstream>
    
    #include "NtKinect.h"
    
    using namespace std;
    
    void doJob() {
      NtKinect kinect;
      while (1) {
        kinect.setRGB();
        kinect.setDepth();
        for (int y=0; y<kinect.depthImage.rows; y++) {
          for (int x=0; x<kinect.depthImage.cols; x++) {
            UINT16 d = kinect.depthImage.at<UINT16>(y,x);
            DepthSpacePoint dp; dp.X=x; dp.Y=y;
            ColorSpacePoint cp;
            HRESULT hr = kinect.coordinateMapper->MapDepthPointToColorSpace(dp,d,&cp);
            if (hr != S_OK) continue;
            if (d > 2000 || d < 500) {
              int ax = (int) cp.X;
              int ay = (int) cp.Y;
              cv::rectangle(kinect.rgbImage,cv::Rect(ax-2,ay-2,4,4),cv::Scalar(255,0,0),2);
            }
          }
        }
        cv::imshow("rbg near", 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. サンプルのプロジェクトはこちら KinectV2_depth2.zip
  6. 上記のzipファイルには必ずしも最新の NtKinect.h が含まれていない場合があるので、 こちらから最新版をダウンロードして 差し替えてお使い下さい。



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