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を定義します.
この構造は単純な6つの福点数にすぎず、この点の位置と色をそれぞれ記録している.コンストラクションサブの部分はOpenNI定義の構造に入る変数である:位置を表す XnPoint3D およびRGB色を表す XnRGB24Pixel.
便宜上、Heresyは座標を変換した部分を手紙式に書いた. GeneratePointCloud()の内容は次のとおりです.
この手紙は 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歩目のときに先にやってもいいのですが、あちらで色を作る部分は面倒です.
メインプログラムに戻ると、本来データを読み取るプログラムは次のようになります.
前述したように、Heresy側はOpenGLで表示されている部分について言及するつもりはないので、こちらは絶えず資料を更新するため、無限ループの形式に変更して絶えず資料を更新し、座標変換を行う.変換後の結果も、簡単にその点の数だけ出力されます.
OpenGLで描くなら、基本的には無限ループではなく、描くたびにKinectの資料を読み、透過する GeneratePointCloud() 変換をして多角形を再構築するつもりはなく、Heresyのように少しずつ描けば、結果は上の映画のようになるかもしれませんね~
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のように少しずつ描けば、結果は上の映画のようになるかもしれませんね~