透過 OpneNI 合併 Kinect 深度以及彩色影像資料
本文同時讀取它的深度圖,以及一般的彩色影像、並將之做位置上的校正、合併。為了做到這個目的,除了上一篇有用到的 xn::DepthGenerator 外,這一篇還會用到 xn::ImageGenerator 來讀取 Kinect 的彩色影像資料;而另外為了修正兩個攝影機的視角不同,也會用到 xn::AlternativeViewPointCapability 這個特別的 Capability。
而由於這邊的程式碼是基於上一個程式所修改的,所以在重複的地方,Heresy 不打算重複說明了,請自行回去參考上一篇文章的部分。而接下來,就直接先看程式碼的內容:
#include <stdlib.h> #include <iostream> #include <string> #include <XnCppWrapper.h> using namespace std; void CheckOpenNIError( XnStatus eResult, string sStatus ) { if( eResult != XN_STATUS_OK ) cerr << sStatus << " Error : " << xnGetStatusString( eResult ) << endl; } int main( int argc, char** argv ) { XnStatus eResult = XN_STATUS_OK; // 2. initial context xn::Context mContext; eResult = mContext.Init(); CheckOpenNIError( eResult, "initialize context" ); // 3. create depth generator xn::DepthGenerator mDepthGenerator; eResult = mDepthGenerator.Create( mContext ); CheckOpenNIError( eResult, "Create depth generator" ); // 4. create image generator xn::ImageGenerator mImageGenerator; eResult = mImageGenerator.Create( mContext ); CheckOpenNIError( eResult, "Create image generator" ); // 5. set map mode XnMapOutputMode mapMode; mapMode.nXRes = 640; mapMode.nYRes = 480; mapMode.nFPS = 30; eResult = mDepthGenerator.SetMapOutputMode( mapMode ); eResult = mImageGenerator.SetMapOutputMode( mapMode ); // 6. correct view port mDepthGenerator.GetAlternativeViewPointCap().SetViewPoint( mImageGenerator ); // 7. tart generate data eResult = mContext.StartGeneratingAll(); // 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(); } // 10. stop mContext.StopGeneratingAll(); mContext.Shutdown(); return 0; }
Image Generator
基本上,這邊的程式架構和之前單純讀取深度影像的範例是相同的。不同的地方在於,這邊除了建立用來讀取深度的 xn::DepthGenerator 這個 node、mDepthGenerator 外,也另外建立了一個用來讀取彩色影像用的 node,也就是形別是 xn::ImageGenerator 的物件 mImageGenerator。而它的使用方法基本上和xn::DepthGenerator 是相同的,一樣是先宣告出他的物件後,再透過 Create() 這個成員函式來建立;程式寫法的部分,可以參考上方「4. create image generator」的部分。
而同樣的,在這邊 Heresy 也是把彩色影像的輸出模式,設定為 640 x480 @ 30FPS;而 xn::ImageGenerator設定輸出模式的方法和 xn::DepthGenerator 一樣,也是呼叫 SetMapOutputMode() 做設定就可以了。(上方「5. set map mode」的部分。)
取得影像資料
相較於 xn::DepthGenerator 是透過 GetDepthMap() 這個函式來取得深度的影像,xn::ImageGenerator 則是透過 GetImageMap() 這個函式來取得彩色的影像資料。而在預設的情況下,這樣取得的影像資料會是一個型別是 XnUInt8(實際上就是 unsigned char)的 const 指標,指向一個一維陣列。而這個陣列的大小會是 640 x 480 x 3,每三個為一組、分別代表他的 RGB 的值。
另外,也可以透過 GetRGB24ImageMap() 這個函式,來取得 XnRGB24Pixel 這種封裝好的格式,不過實際使用上差異不大就是了。而如果有需要的話,image generator 也可以透過 SetPixelFormat(),來修改每一個像素的資料形式,不過在這邊就不細提。
視角修正
接下來比較特別的地方,則是「6. correct view port 」的部分。這邊程式的目的,是要修正兩個感應器取得的畫面座標,讓兩者畫面的視角一致。為什麼要做這件事呢?因為 Kinect 的深度攝影機和彩色攝影機是在不同的位置,而且鏡頭本身的參數也不完全相同,所以其實兩個攝影機所取得的畫面,是會有些微的差異的。下面就是在不修正的情況下,所抓到的畫面(左邊是彩色、右邊是深度):
直接看的話感覺好像差不多?不過仔細看的話,是可以發現兩張圖的可視範圍是不一樣的~彩色攝影機的可視範圍,基本上是比深度攝影機大一些的;而如果把兩張圖重疊在一起,看起來會更明顯。右邊的圖就是直接把兩張圖重疊在一起的結果,可以很明顯地看出來,有很多地方都沒有辦法對應得很好,像是天花板上的日光燈罩和機櫃上的東西,都很明顯地看的出來有出入。
為了解決這個問題,OpenNI 有提供一個名為「Alternative View」的 Capability,可以幫助使用 OpenNI 的程式開發者,快速地修正這種視角上的不同;不過這項功能,基本上是要有支援的 production node 才可以用的就是了。
Alternative View 這個 capability 的型別是 xn::AlternativeViewPointCapability,可以直接透過呼叫 Production Node 的「GetAlternativeViewPointCap()」來取得用來操作這項 capability 的物件;基本上 OpenNI 的所有 capability,都是以這樣的形式來使用的。而以 xn::AlternativeViewPointCapability 來說,他有提供 SetViewPoint() 這個函式,可以把其他的 production node 傳進去、把目前這個 node 的視角改為指定 node 的視角。
而 Heresy 這邊實際寫成程式碼,就是:
mDepthGenerator.GetAlternativeViewPointCap().SetViewPoint( mImageGenerator );
這樣寫的意思,就是把 mDepthGenerator 這個 depth generator 的視角、設定成和 mImageGenerator 一樣了。(註:反過來想讓 image generator 根據 depth generator 調整的話,OpenNI 會當掉…)
而加上這一行後, depth generator 所抓到的畫面,就會變成左下方這樣的圖。如果和之前沒有執行過視角調整的結果相比,可以發現調整過後的結果周圍的黑邊明顯地比較大,同時也有一些幾何上的修正(四邊有稍微內凹);而這樣取得的結果再和彩色影像重疊在一起(右下圖),則可以發現位置變得相當一致,不會有之前畫面內東西不一致的問題了!
所以如果有需要要將深度和彩色影像做整合的話,應該就要記得做視角修正的動作,以避免對應的位置出錯。
這一篇文章大概就先寫到這裡了。基本上,這邊主要應該算是在講 Alternative View 這個 capability 了~而實際上,如果有需要的話,可能也可以考慮使用 Frame-Sync 這個 capability,來讓深度影像與彩色影像之間的時間差更小;不過就 Heresy 目前簡單的測試看來,應該差異不大就是了,如果有需要的人,可以自己玩看看了~
trackback: http://kheresy.wordpress.com/2011/01/21/combine_depth_and_image_from_kinect/