透過 OpneNI 合併 Kinect 深度以及彩色影像資料


在前一篇的《透過 OpneNI 讀取 Kinect 深度影像資料》裡,已經解釋過如何在 PC 上透過 OpenNI 來讀取 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 目前簡單的測試看來,應該差異不大就是了,如果有需要的人,可以自己玩看看了~


OpenNI / Kinect 相關文章目錄

對「透過 OpneNI 合併 Kinect 深度以及彩色影像資料」的想法

  1. hersey大神,您好
    我有一个简单的问题,当您的程序写好以后,您如何形成影像的?我看过很多用opencv来形成,但有没有就是程序写好以后,直接用终端控制来形成的?

    • 抱歉,看不懂你的「用终端控制来形成」的意思。

      不過,基本上 OpenCV 是 C++ 各種函式庫中,最容易拿來顯示圖片的一個,再加上在做影像處理的大多知道這個函示庫,所以很多都會使用這個當範例。

      如果你不想使用 OpenCV 的話,那就需要去找其他的圖形介面函示庫(例如 Qt),才能把圖片顯示出來。

  2. Hersey大神你好(窝又来了QAQ),
    想问一下这个彩色图像和深度图像的校正,OpenNI2的显示结果是已经校正过了的结果吗?谢谢了!

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料