透過 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 目前簡單的測試看來,應該差異不大就是了,如果有需要的人,可以自己玩看看了∼

92 thoughts on “透過 OpneNI 合併 Kinect 深度以及彩色影像資料”

  1. 您好!我想把userGenerator得到的user部份直接在rgb圖中摳出來,得到一個RGB的人體,如果不進行校正,就會出現偏差。請問應該怎樣標定才不會出現偏差呢?OPENNI有沒有直接提供這種方法?謝謝!

  2. 您好,不知道您有沒有針對 depth generator 去設定 Alternative View 這個 capability?

  3. 你好,非常感谢你的这几篇关于kinect的教程,现在我又两个问题。1.请问怎么把mDepthGenerator.GetDepthMap()获得的深度信息转换为每个像素点距离kinect的绝对距离?精度有多大?2.请问mImageGenerator.GetImageMap()得到的彩色图像格式是RGB24吗?

  4. to Incredible 請參考:http://viml.nchc.org.tw/blog/paper_info.php?CLASS_ID=1&SUB_ID=1&PAPER_ID=217

  5. to vincent 如果有去看程式碼的話,就可以發現,這個程式本來就沒有任何畫面的輸出。如果你要有畫面輸出的版本,請參考:http://viml.nchc.org.tw/blog/paper_info.php?CLASS_ID=1&SUB_ID=1&PAPER_ID=254

  6. 可以問一下如果要用OPENCV合併兩張圖,該用哪個函式呢? 感激不盡QQ

  7. to YUCK 抱歉,Heresy 這邊沒有再用 OpenCV,所以無法告訴你明確的函式名稱。

  8. heresy大大您好我看到您获取的 彩色图像的质量是很好的,不知道你有没有碰过这样的的问题,运行Niviewer的程式的时候,彩色图像出现了相当明显的锯齿现象,我运行的时候就是这样…开始的时候以为是primesense的bayer程式问题,但在我单独的使用opencv来输出图像的时候图像质量却又了很大改观。希望您可以指导一下。

  9. to Nireyflip基本上,Kinect 的解析度是有限的(640×480),而官方的範例程式是使用全螢幕的方法來做顯示的,所以當螢幕解析度很高的時候,就是必要把 VGA 大小的圖放大,這時候本來就是會有鋸齒狀的現象的。

  10. 作者您好 我是一個剛接觸Kinect的新手,可以請問一下
    Kinect裡面的深度以及彩色影像資料是以什麼型式和格式來表示呢??

  11. 已经有avi格式的深度以及彩色影像资料,如何把它们转化为oni格式?
    目的是为了使用openni的API,麻烦你详细解释,谢谢!

  12. to logger2010

    基本上,當用一般的影像格式儲存的時候,其實已經破壞了深度資料的資訊了(值的範圍的問題)。
    而 OpenNI 官方也沒有提供格式的轉換工具,所以真的要的話,應該是必須要自己根據 OpenNI 的原始碼、去解析他的 Player 的讀取過程,然後改成自己的格式。

  13. 代码中没有合并两幅图像并显示最后结果的语句啊。

  14. to zhang

    這個範例本來就是單就 OpenNI 在做介紹,並沒有包含任何圖像顯示、輸出的功能。

    有畫面的版本,可以參考《使用 Qt GraphicsView 顯示 OpenNI 影像資料》http://viml.nchc.org.tw/blog/paper_info.php?CLASS_ID=1&SUB_ID=1&PAPER_ID=254

  15. 请问一下:
    从OpenNI中可以得到一幅图640*480,如何能知道该图上一点(P.x, P.y)的在实际空间中的,X,Y, Z 距离(X似乎应该是物体与kinect的水平距离吧?)。

  16. 我的程序中
    XnMapOutputMode mapMode;
    mapMode.nXRes = 320;
    mapMode.nYRes = 240;
    mapMode.nFPS = 30;

    eResult = mDepthGenerator.SetMapOutputMode( mapMode );
    eResult = mImageGenerator.SetMapOutputMode( mapMode );
    mDepthGenerator.GetAlternativeViewPointCap().SetViewPoint( mImageGenerator );

    也就是把输出模式改成320*240,在运行SetViewPoint这条指令时,报错:Bad Parameter sent to the device。查了些资料,但仍解决不了!烦请heresy指教!

  17. to 小书童
    建議在呼叫 SetMapOutputMode() 後,檢查 eResult 是否正確,這樣可以看出來到底是死在那裡。
    另外,Kinect for Xbox 360 目前在 OpenNI 環境下,似乎對 QVGA 的支援有問題。

  18. 你好,我想问一下,SetViewPoint这个视角修正的函数,在读取oni文件的时候可以用么?

  19. to yaoshunyu
    抱歉,這個 Heresy 不確定,可能要試試看才知道

  20. Heresy,按照你说的方法,虽然叠加上了,但是由于深度图的抖动很厉害,效果不好,openni中有没有办法可以让深度图或是叠加后的图更加稳定呢?其他的办法也行,只要让边缘看起来平滑正常就ok….在线等,也可以通过邮箱跟我交流wwj_pro@126.com,谢啦~~~~~

  21. to peter_wwj
    這邊基本上就是要自己套用影像處理的方法來做去雜訊了

  22. Heresy,你好
    最近看到有文章提出对Kinect进行标定,为此我感到有些困惑,Kinect需要标定吗?其内部不是已经标定好了吗?
    望不吝赐教!

  23. to 小书童

    抱歉,不知道你的「标定」是什麼意思?

  24. Sorry,Heresy! What I want to ask is whether Kinect needs to be calibrated?

  25. to 小书童
    以 OpenNI 來說,已經有提供現成的轉換方法了。
    在一般的應用來說,應該是不需要額外去處理。

  26. heresy,你好!
    我已经得到了Kinect的深度信息和颜色信息,但是如何将彩色图和深景图在一幅图中同时显示出来,却没有一个很好的办法,想请你给些建议!

  27. to 小书童
    建議可以參考 OpenNI 官方範例、NiViewer 的顯示方法。
    他提供了很多種不同的顯示方法可以切換。

  28. 你好,我想问一下ms sdk for kinect有校正视角的功能么?

  29. 您好,麻烦问您一下,我把深度数据转化为色调及饱和度来显示图片,但是得到的结果是,单个物体基本上是同一个颜色,颜色变化比较不够细致,这是硬件问题还是软件编程可以控制的?有什么资料可以参考吗?谢谢!

  30. to henry
    這邊要看你是怎麼轉換的。
    由於 OpenNI 取得的深度已經是以 mm 為單位了,所以接下來就是看你轉換的演算法是什麼?

    不過基本上,同一個物體的深度就不會差很遠,所以你如果是以線性轉換的方法把深度轉換成色彩,本來就不會有很大的差異。

  31. 和小书童的问题类似,弄成320 * 240 & 30fps,没有报错,但是得到的所有深度数据都是0。请问,OpenNI Kinect到底支持320 * 240吗?
    我用xn::MapGenerator::GetSupportedMapOutputModes();查看过所有支持的格式,其中是包括320 * 240 & 30fps的(所以它不会崩掉?)。

  32. to seeyasnow

    Kinect 目前在 OpenNI 環境應該還是只支援 VGA 解析度。
    他回報可以支援 QVGA 是 driver 的 bug。

  33. 大谢heresy,我在好像也是你写的另外一个华硕和kinect对比那文章里也看到了。

  34. Dear Heresy,

    Are there any methods to get the calibration parameters by OpenNI? Thanks.

  35. to 寒塘雁迹
    OpenNI 應該沒有提供 API 來直接讀取這個資料。

  36. 您好,我是新手,我想請問一下,kinect内部有沒有直接改深度的偏移量的指令在(NuiImpl里)?因爲我要和彩色圖像結合找出人形,發現深度和彩色之間有偏移量~,還有我想請問深度圖抓取得到的人形要如何讀出(就是移動的人形圖)?謝謝~

  37. 謝謝你的回答~
    我再冒昧的問一個基本問題(小弟知識淺薄)就是openCV,openGL和oenNI有什麽差別?哪一個利用在kinect比較好~?我真的不曉得差別在哪裏,也查了許多資料還不是很清楚~麻煩Heresy大大幫我解決我的疑問,謝謝。

  38. To long

    你所提及的三個東西用途基本上是完全不同用途的。
    OpenCV 是用來做電腦視覺相關計算的函式庫, OpenGL 則是用來做 3D顯示的函式庫。
    只有 OpenNI 才是用來存取 Kinect 的。

  39. 您好,又来麻烦您了。最近弄了一个华硕的Xtion Pro,重新安装了它安装盘里提供的OpenNI,并按照说明替换了一个XML。
    运行OpenNI目录里的例子NiViewer.exe,是可以正常捕获深度数据的。
    但是,换成以前用Kinect的源程序(基本是参照你上面的写法)编译后运行会崩溃掉,请问如果由Kinect换成Xtion Pro在代码上需要什么改变吗?因为记得您应该也做过两个产品的对比。
    如果回复麻烦的话也可以给一个链接地址等。
    谢谢大大先。

  40. To seeyasnow

    Heresy 這邊的範例程式基本上應該都是可以在兩種硬體上使用;理論上程式碼應該是不需要修改的。
    不知道你那邊的錯誤訊息是什麼?

  41. 报错在mImageGenerator.SetMapOutputMode( mapMode );这里,信息和没有插硬体是的错误很想,就是Unhandle ****, Access violation reading loacation 0x00000004.
    看了下例子里的做法,它是用context读取一个xml文件,然后用context枚举所有支持的模块,放到到一个NodeInfoList类型的list里面,用list的iterator列举硬体各个支持的模块,其中包括深度部分,然后用一个(*it).GetInstance()方法初始化好depthGenerator,剩下的步骤就一样了。而您上面的方式是context直接调用init方法,然后程序里面配置参数(宽,高,fps),然后得到depthGenerator

  42. 請確認一下, 您所購買的是「Xtion Pro」還是「Xtion Pro Live」?前者是沒有彩色攝影機的版本、後者才是有彩色攝影機加深度攝影機的版本。
    如果你是使用沒有彩色攝影機的版本的話,是無法建立出 Image Generator 的。

  43. 恩,谢谢大大了。
    确实是老版本,不是live,live没货了……等过段时间再买吧。

  44. heresy老師你好,我是剛開始使用Kinect的新手,想請問老師,如果要校正IR影像與RGB影像的視角要用何種方法?GetAlternativeViewPointCap只能校正DEPTH跟RGB影像,我想這應該是IR影像和RGB影像無法同時顯視的限制,如果我各取IR和RGB的單偵圖片來做圖片的視角校正,該如何下手?還有GetAlternativeViewPointCap這個指令是對DEPTH的校正做了哪些動作?是單純的平移嗎?如果是的話他平移的pixel是固定的?又移動了多少pixel呢?先謝過老師了。

  45. to Eason
    Alternative ViewPoint Capability 基本上應該只能用在 depth generator 上,所以沒辦法用來做 IR 和 RGB 的校正。
    而且,這個校正基本上也不是單純的平移,如果你仔細看圖的話,可以發現其實校正後的結果,在邊的部分是有內縮的。
    官方似乎是沒有明確地寫出之間的轉換的方法,如果要自己做的話,或許是得去看相關的原始碼了。

  46. 补充一点,mDepthGenerator.GetAlternativeViewPointCap().SetViewPoint( mImageGenerator )这个函式对保存下来的的oni视频不具有校正功能了,可以推断该函数是在node生成数据时完成的校正。上述结论通过了实验验证了。

  47. heresy老师您好,请问kinect将深度和彩色图像合并后,是否还需要对kinect摄像机的参数进行标定呢?OpenNI是否已经将这一部分帮我们完成了呢?Kinect参数的标定可参照http://www.ee.oulu.fi/~dherrera/papers/2011-depth_calibration.pdf 这篇文章中的方法,作者也给出了标定包?

  48. to guang11180

    OpenNI 本身已經提供 AlternativeViewPointCapability 的功能了。
    一般情況下應該是用他提供的功能就夠了。

  49. 您好,有個關於物體距離的問題想跟您請教。
    Kinect抓到畫面中人體的距離(人跟kinect的距離)和其兩肩膀的關節點座標(x:左右,y:上下,z:前後),因為人跟kinect距離越遠,兩肩膀的點距就會拉近,距離越近兩肩膀的點距就會拉遠,那如果想得到兩肩膀之間實際的距離,有沒有合適的公式可以使用呢?

    這邊我知道兩點距離可以使用[ (x2-x1)^2 (y2-y1)^2 (z2-z1)^2 ]^0.5 取得距離,但是因為畫面中的人會因為距離遠或近導致這兩點的點距會有所變動,所以就沒有辦法直接用這公式來計算實際距離了。

  50. to SirMarine

    OpenNI / NiTE 所追蹤的人體骨架的座標系統,是所謂真實世界座標系統,除非你特別把他轉換到投影座標系統,否則不會有離越近兩點離越遠的問題。

  51. 您好,感謝您的回應,我瞭解了。不知道微軟的SDK是否也是如此呢?不好意思麻煩您了。

  52. to SirMarine

    抱歉,Heresy 基本上沒有在使用 Kinect for Windows SDK 進行開發,所以不確定這個狀況。建議參考微軟官方的文件。

  53. 請問openni2.0 裡有 xncppwrapper.h 這個標頭檔嗎??
    我搜尋整個資料夾都沒有
    請問是改了另一個名稱嗎??
    還是2.0 有另外 修正色彩 深度影像的 方法???
    謝謝!!

  54. 无法打开包括文件:“XnCppWrapper.h”: No such file or directory
    你好,请问这个错误是怎么决解?您之前有遇见过吗?

  55. @ liyu

    找不到 header 檔通常是 include path 不對的關係。
    請確定你設定的 Additional Include Directories 是正確的。
    如果只有安裝 64 位元版本 OpenNI,需要設定為 $(OPEN_NI_INCLUDE64)

    另外,也請確認你安裝的 OpenNI 版本是 1.x 還是 2.x,兩者是完全不同的。

  56. 谢谢您的解答,我用的是华硕的xtion pro live ,64位的openni2,在vc2010上配置后第一次出现会出现‘’无法打开包括文件:“XnCppWrapper.h”: No such file or directory‘’这个错误,我下载该头文件并放入openni2文件夹后不再出现该错误提示,后我又按照您的文章重新配置并确认配置无误后,这个错误也不再出现,但两次又都出现 ‘’无法打开包括文件:“XnOpenNI.h”: No such file or directory‘’这个错误,期待您的解答。

  57. Hi~
    请问,用了这句校正深度图之后,IRGenerator 和 UserGenerator获取的数据是对应新的校正后的深度图,还是校正前的????
    mDepthGenerator.GetAlternativeViewPointCap().SetViewPoint( mImageGenerator );

  58. @gangan1121

    User Generator 是基於深度圖做分析,所以會是校正後的。

    IR 的部分因為是使用 color sensor 的資料,所以應該不會變化。

  59. @heresy
    谢谢,我试了,IR不会变化…
    有什么方法是IR也一样变化么?

  60. @gangan1121

    基本上 OpenNI 應該是由 depth 去對 IR/Color,所以 IR 是沒有必要變化的。

  61. 就是我如果定义了IRGenerator又定义了ImageGenerator,这时imageGenerator拿到的图是全黑的….

  62. @ gangan1121
    這兩者是共用同一個感應器的,所以一次只能開啟一個。

  63. 我有一些calibration的问题,请问能实时请教一下么?请加我Q492494351,thx…

  64. 我校准深度图和彩色图的方法步骤是:(基于opencv和openni)
    1、分别计算深度相机和彩色相机的intrinsic,拍board放在不同位置的图,找到corners用calibrateCamera()得到内参…
    2、取几组同时拍摄到board的深度图和ir图&rgb图,取undistort的深度图和ir图,把找到board的corners转换为深度相机坐标系下的3d坐标(看成是世界坐标系),根据3D到2D重建函数SolvePnpRansac来得到世界坐标系的points与彩色相机坐标系对应的转换数据(translation和rotation)

    这个理论上有问题么?
    我得出来的校准结果总是有偏移,就是不能适用于任何位置任何深度…

  65. @gangan1121

    抱歉,Heresy 沒有做過這方面的研究,所以也不確定這樣做的效果。

  66. @heresy 谢谢..
    如果有研究calibration的朋友,且方便的话..leave a contact…thx

  67. @heresy 你好~我想问一下kinect读取骨骼并进行显示的时候骨骼很抖、拼命地震的问题有什么解决办法么??

  68. @小小小西瓜

    OpenNI 有提供平滑化的設定,可以試試看。

  69. heresy大大,您好,一直没弄明白getdepthmap()获取的数据该怎么读出来,用opencv可以直接读出getdepthmap()的数据吗?

  70. 还有就是用opencv显示深度和rgb图像融合时,虽说校准过视角了,但是效果还是不行,是不是还得对设备进行标定??

  71. Heresy老師你好:我想請問一下再openni2裡有可以修正彩色及深度影像偏差的函式嗎?
    像是:
    //6. correct view port DepthGenerator.GetAlternativeViewPointCap().SetViewPoint( mImageGenerator );

    麻煩您了,謝謝!!

  72. 你好~因為偵測彩色的鏡頭只支援到VGA ( 640 x 480 ),所以原來的程式就設這個大小輸出,想問要如何將彩色視窗放大,例如1024*768,感謝!

  73. to Charlie
    這部分和 OpenNI 無關了,要看你是用什麼環境開發視窗環境的。
    請參考你使用的 GUI 的說明文件。

發佈回覆給「yingxiaosan」的留言 取消回覆

發佈留言必須填寫的電子郵件地址不會公開。