Kinectの深さ画像データによる3 D点群の計算


この文書はhttp://www.cnblogs.com/JohnShao/archive/2011/05/22/2053496.html
OpenNIを通してKinectの深さ、色の情報を読み取ることができるようになった後、実際にこれらの情報を使って、3 Dの環境を再構築して表示してみました~しかし、実際には、前の例で読んだ深さの情報は、すべて原始資料であり、座標軸もセンサーの2次元映像の座標システムであり、3 Dシーンを再構築するには、これらの情報は換算する必要があります.幸い、OpenNIはDepth Generatorですでに提供されています. ConvertProjectiveToRealWorld() およびConvertRealWorldToProjective() この2つの関数は、プログラム開発者が座標変換を迅速に行うのに役立ちます.
これら3 Dのポイントを直接色付けしてOpenGLで描けば、次のような映画になるでしょう.
もちろん、point cloudは最良の表示方式とは限らず、必要があれば多角形を再構築して描くこともできますが、多角形の再構築はもう別のテーマなので、Heresyもここで議論するつもりはありません.また、Heresyはこの記事でもOpenGL表示の部分については言及せず、これらのpoint cloudをどのように構築するかを示す簡単な例を提供するだけです.
 
これらの点の位置と色情報を格納するために、ここでは簡単な構造、SColorPoint 3 Dを定義します.
struct SColorPoint3D
{
  float  X;
  float  Y;
  float  Z;
  float  R;
  float  G;
  float  B;

  SColorPoint3D( XnPoint3D pos, XnRGB24Pixel color )
  {
    X = pos.X;
    Y = pos.Y;
    Z = pos.Z;
    R = (float)color.nRed / 255;
    G = (float)color.nGreen / 255;
    B = (float)color.nBlue / 255;
  }
};

この構造は単純な6つの福点数にすぎず、この点の位置と色をそれぞれ記録している.コンストラクションサブの部分はOpenNI定義の構造に入る変数である:位置を表す XnPoint3D  およびRGB色を表す XnRGB24Pixel.
便宜上、Heresyは座標を変換した部分を手紙式に書いた. GeneratePointCloud()の内容は次のとおりです.
void GeneratePointCloud( xn::DepthGenerator& rDepthGen,
                         const XnDepthPixel* pDepth,
                         const XnRGB24Pixel* pImage,
                         vector<SColorPoint3D>& vPointCloud )
{
  // 1. number of point is the number of 2D image pixel
  xn::DepthMetaData mDepthMD;
  rDepthGen.GetMetaData( mDepthMD );
  unsigned int uPointNum = mDepthMD.FullXRes() * mDepthMD.FullYRes();

  // 2. build the data structure for convert
  XnPoint3D* pDepthPointSet = new XnPoint3D[ uPointNum ];
  unsigned int i, j, idxShift, idx;
  for( j = 0; j < mDepthMD.FullYRes(); ++j )
  {
    idxShift = j * mDepthMD.FullXRes();
    for( i = 0; i < mDepthMD.FullXRes(); ++i )
    {
      idx = idxShift + i;
      pDepthPointSet[idx].X = i;
      pDepthPointSet[idx].Y = j;
      pDepthPointSet[idx].Z = pDepth[idx];
    }
  }

  // 3. un-project points to real world
  XnPoint3D* p3DPointSet = new XnPoint3D[ uPointNum ];
  rDepthGen.ConvertProjectiveToRealWorld( uPointNum, pDepthPointSet, p3DPointSet );
  delete[] pDepthPointSet;

  // 4. build point cloud
  for( i = 0; i < uPointNum; ++ i )
  {
    // skip the depth 0 points
    if( p3DPointSet[i].Z == 0 )
      continue;

    vPointCloud.push_back( SColorPoint3D( p3DPointSet[i], pImage[i] ) );
  }
  delete[] p3DPointSet;
}

この手紙は xn::DepthGenerator そして、読んだ深さの映像とカラーの映像が入ってきて、資料の出所として使われています.変換完了後の3 Dポイントデータを格納するvectorも転送される.
その中でも、深さ映像のフォーマットは同じです XnDepthPixel のconst指標ですが、カラー映像の部分では、HeresyはRGBを封入したものに変更されています XnRGB 24 Pixelは、インデックス値の計算を減らすことができます.このように修正するため、カラー映像を読み取るプログラムも
const
 XnUInt8* pImageMap = mImageGenerator.GetImageMap();
次のように変更
const XnRGB24Pixel* pImageMap = mImageGenerator.GetRGB24ImageMap();
一方,書簡コンテンツの部分では,第1セグメントの部分は主にdepth generatorのmeta-data:xn::DepthMetaDataを取得することによって 簡単なサイズ、インデックス計算をします.このように使いたくなければ、640 x 480という固定値で直接計算することもできますが、以前と SetMapOutputMode() 設定した解析度が一致すればよい.
第2部「build the data structure for convert」は、奥行き映像の640 x 480ポイントを、いずれも XnPoint3D 形式の1つはアレイで、後の座標変換の準備ができています.
第3部「un-project points to real world」は実際に変換する部分です.こちらは、座標を映像の座標系から3 D座標系に変換し、主にDepth Generatorの ConvertProjectiveToRealWorld() この書簡変換するポイントの数(uPointNum)、変換するポイントをアレイ形式で伝えるだけの使い方も簡単です XnPoint 3 D*)入って、彼にもうallocateをあげました. XnPoint3D アレイ(p 3 DPointSet)は、自動的に変換できます~
第4部Heresyは、1つのループですべての点をスキャンし、深さ0の点を削除し(Kinectが深さを判定できない部分を表すため)、色の情報とともに SColorPoint3D の形になる vPointCloud に保存されています.
△この動作は、実は2歩目のときに先にやってもいいのですが、あちらで色を作る部分は面倒です.
メインプログラムに戻ると、本来データを読み取るプログラムは次のようになります.
// 8. read data
eResult = mContext.WaitNoneUpdateAll(); if( eResult == XN_STATUS_OK ) { // 9a. get the depth map
 const XnDepthPixel* pDepthMap = mDepthGenerator.GetDepthMap(); // 9b. get the image map
 const XnUInt8*    pImageMap = mImageGenerator.GetImageMap();
}

前述したように、Heresy側はOpenGLで表示されている部分について言及するつもりはないので、こちらは絶えず資料を更新するため、無限ループの形式に変更して絶えず資料を更新し、座標変換を行う.変換後の結果も、簡単にその点の数だけ出力されます.
// 8. read data
vector<SColorPoint3D> vPointCloud;
while( true )
{
  eResult = mContext.WaitNoneUpdateAll();
  // 9a. get the depth map
  const XnDepthPixel*  pDepthMap = mDepthGenerator.GetDepthMap();

  // 9b. get the image map
  const XnRGB24Pixel*  pImageMap = mImageGenerator.GetRGB24ImageMap();

  // 10 generate point cloud
  vPointCloud.clear();
  GeneratePointCloud( mDepthGenerator, pDepthMap, pImageMap, vPointCloud );
  cout << "Point number: " << vPointCloud.size() << endl;
}

OpenGLで描くなら、基本的には無限ループではなく、描くたびにKinectの資料を読み、透過する GeneratePointCloud() 変換をして多角形を再構築するつもりはなく、Heresyのように少しずつ描けば、結果は上の映画のようになるかもしれませんね~