OpenCV 的全名是「Open Source Computer Vision」(官方網站、中文網站),是一套採用 BSD 授權的開放原始碼的電腦視覺函式庫,在相關領域來說,算是一套相當之行的函式庫;在 OpenCV 裡面,包含了很多影像處理的功能,同時也包含了基本的圖形介面、以及攝影機的操作等功能。
而對於 OpenNI 這樣、針對深度影像和彩色影像做處理的架構,其實如果不是單純只是想靠 NITE 來追蹤人體骨架的話,OpenCV 是一個相當適合拿來搭配使用的函式庫;實際上,OpenCV 現在也可以直接整合 OpenNI 來讀取影像(請參考《Using Kinect and other OpenNI compatible depth sensors》)。
這篇呢,Heresy 則是以簡單的範例,大概講一下怎麼把 OpenNI 的深度和彩色資料,讀出來轉換成 OpenCV 的格式。下面就直接看原始碼吧~
// OpenNI Header #include <XnCppWrapper.h> // link OpenNI library #pragma comment( lib, "OpenNI.lib" ) // OpenCV Header #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> // Link OpenCV Library #ifdef _DEBUG #pragma comment( lib, "opencv_core242d.lib" ) #pragma comment( lib, "opencv_highgui242d.lib" ) #pragma comment( lib, "opencv_imgproc242d.lib" ) #else #pragma comment( lib, "opencv_core242.lib" ) #pragma comment( lib, "opencv_highgui242.lib" ) #pragma comment( lib, "opencv_imgproc242.lib" ) #endif // main function int main( int argc, char** argv ) { // 1a. initial OpenNI xn::Context xContext; xContext.Init(); // 1b. create depth generator xn::DepthGenerator xDepth; xDepth.Create( xContext ); // 1c. create image generator xn::ImageGenerator xImage; xImage.Create( xContext ); // 1d. set alternative view point xDepth.GetAlternativeViewPointCap().SetViewPoint( xImage ); // 2. create OpenCV Windows cv::namedWindow( "Depth Image", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "Color Image", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "Depth Edge", CV_WINDOW_AUTOSIZE ); cv::namedWindow( "Color Edge", CV_WINDOW_AUTOSIZE ); // 3. start OpenNI xContext.StartGeneratingAll(); // main loop while( true ) { // 4. update data xContext.WaitAndUpdateAll(); // 5. get image data { xn::ImageMetaData xColorData; xImage.GetMetaData( xColorData ); // 5a. convert to OpenCV form cv::Mat cColorImg( xColorData.FullYRes(), xColorData.FullXRes(), CV_8UC3, (void*)xColorData.Data() ); // 5b. convert from RGB to BGR cv::Mat cBGRImg; cv::cvtColor( cColorImg, cBGRImg, CV_RGB2BGR ); cv::imshow( "Color Image", cBGRImg ); // 5c. convert to signle channel and do edge detection cv::Mat cColorEdge; cv::cvtColor( cColorImg, cBGRImg, CV_RGB2GRAY ); cv::Canny( cBGRImg, cColorEdge, 5, 100 ); cv::imshow( "Color Edge", cColorEdge ); } // 6. get depth data { xn::DepthMetaData xDepthData; xDepth.GetMetaData( xDepthData ); // 6a. convert to OpenCV form cv::Mat cDepthImg( xDepthData.FullYRes(), xDepthData.FullXRes(), CV_16UC1, (void*)xDepthData.Data() ); // 6b. convert to 8 bit cv::Mat c8BitDepth; cDepthImg.convertTo( c8BitDepth, CV_8U, 255.0 / 7000 ); cv::imshow( "Depth Image", c8BitDepth ); // 6c. convert to 8bit, and do edge detection cv::Mat CDepthEdge; cv::Canny( c8BitDepth, CDepthEdge, 5, 100 ); cv::imshow( "Depth Edge", CDepthEdge ); } cv::waitKey( 1 ); } }
在這邊的例子裡,Heresy 並沒有去使用整合 OpenNI 的 OpenCV,而是獨立使用 OpenNI 來做資料的讀取,然後再轉換成 OpenCV 的格式。實際上,如果只是要使用 OpenNI 的影像資料的話,使用整合過的 OpenCV 可以直接使用內建的 VideoCapture 來做畫面的讀取,在使用上會比較單純、簡單一點,不過由於這樣會少掉一些 OpenNI 的功能,所以在這邊 Heresy 不使用這樣的方法。
所以,在上面的範例裡面,1a 到 1d 的部分,就是用標準 OpenNI 的流程,來進行初始化的動作;詳細的說明,請參考《透過 OpneNI 讀取 Kinect 深度影像資料》。而接下來 2 的部分,則是使用 OpenCV 的 highgui 這個模組的 namedWindow() 這個函式(官方文件),來建立四個不同名稱的視窗、作為畫面的顯示。
接下來,則是透過一個無窮迴圈,來不停地更新資料了~裡面主要分成兩塊,也就是 5、讀取 Image Generator 的彩色影像、以及 6、讀取 Depth Generator 的深度影像的部分。
其中,在 5a 和 6a 的部分,就是把 OpenNI 讀出來的 map(xn::ImageMetaData 和 xn::DepthMetaData)轉換成 OpenCV 的影像格式、cv::Mat 的部分(官方文件)。
以彩色影像來說,就是在建立 cv::Mat 物件的時候,把影像的大小、也就是 Y 軸、X 軸的解析度,以及資料的形式、資料的位址,都傳遞給建構子、以建立出一張 OpenCV 的影像、cColorImg。其中,CV_8UC3 是指 3 channel 的 8bit 正整數(unsigned char)的資料(參考)。
不過,由於 OpenCV 所使用的彩色影像的色彩,預設是以 Blue、Green、Red 來做排列,和一般 Red、Green、Blue 排列不同,所以要拿來用的話,還需要先做一個轉換;在這邊(5b)就是透過 cvtColor() 這個 OpenCV 的 imgproc 這個模組裡的函式(官方文件),把本來的 RGB 影像、轉換成 BGR 的影像(cBGRImg)。而在轉換好之後,則就是在透過 imshow() 這個函式,把轉換完成的影像、顯示在對應的視窗(這邊是 Color Image)上了。
而接下來(5c),Heresy 則是試著用 OpenCV 提供的 Canny 這種方法的邊緣偵測(官方文件)。不過由於 OpenCV 所提供的 Canny edge 只有針對 8bit 1 channel 的影像作處理,所以這邊要先再用 cvtColor(),把影像轉成灰階的、然後再來進行;而之後,則是一樣透過 imshow() 這個函式,把偵測完的結果、顯示在對應的視窗上。
深度的部分(6a)也是類似的,不過由於 OpenNI 的深度影像的單一像素的格式是 XnDepthPixel、實際上是單一 channel 的 16bit 的正整數(unsigned short),所以在建立 cv::Mat 的時候的資料型別,則是要設定為 CV_16UC1。
不過,雖然 OpenNI 的深度影像是 16bit 的正整數,理論上值的範圍是 0 – 65,535,但是實際上深度的最大值只會到 10,000,所以如果不處理、直接畫的話,會有整個畫面偏暗的問題(基本上,畫面會接近全黑);所以在這邊,Heresy 也先透過 cv::Mat 的 convertTo() 的函式,把這個 16bit 的影像裡的每一個像素都乘上一個 scale(255.0 / 7000)後,轉換成 8bit 的影像(c8BitDepth)。再之後,就是一樣把轉換好的影像,進行 canny edge 偵測了~
而這樣的程式執行的結果,會有下面這樣、四個不同資料的視窗,分別代表彩色影像、基於彩色影像的邊緣偵測結果、深度影像、以及基於深度影像進行邊緣偵測的結果。
這篇就先到這了。基本上,Heresy 是把這篇文章定位成一個極為簡單的 OpenNI 和 OpenCV 的資料整合範例;而由於 OpenCV 還有提供相當多的影像處理的功能,接下來要怎麼做,就是看自己想要做什麼了~
Hereys,非常感謝你的文章,我有一個問題想問您,就是採集的彩色圖像和深度圖像的幀數不能一一對應,該怎麼處理啊,比如說,彩色圖像總比深度圖像快,並且相差的幀數不是固定的,而是一個隨機值,有時候相差一幀,有時候相差多幀,能給我點提示嗎,非常感謝,
讚讚
建議考慮試試看使用使用他提供的 Frame-Sync capability
讚讚
您好 我想請問一下
您有些程式前面是xn::
有些是openni::
請問這兩種有什麼差別????
讚讚
那是 OpenNI 1.x 和 2.x 的差別。
請參考: https://kheresy.wordpress.com/2013/09/30/about-openni-2013/
讚讚
Heresy您好,
參考本篇文章改寫讀取IR Camera的資料,也順利讀到資料並imshow在視窗,
但輸出的xIrData.FullYRes()是240,xIrData.FullXRes()是320,
顯示圖有格子狀,感覺是240×320的pixel放到480×640圖內,每4格裡面有1格放資料,而且有疊影的狀況。
想請教您要如何透過程式碼更改讓IR camera擷取480×640或更高解析度的資料輸出?
Thanks in advance,
Seigfried
—-
OpenNI 的版本是1.3.2.1
硬體: Xtion Pro Live
以下為程式碼
int main( int argc, char** argv )
{
xn::Context xContext;
xContext.Init();
xn::IRGenerator xIrImage;
xIrImage.Create( xContext );
cv::namedWindow(“IR Image", 0 );
xContext.StartGeneratingAll();
while( true ){
xContext.WaitAndUpdateAll();
xn::IRMetaData xIrData;
xIrImage.GetMetaData( xIrData );
cv::Mat IrImg( xIrData.FullYRes(), xIrData.FullXRes(), CV_8UC1, (void*)xIrData.Data() );
cout << "y"<<xIrData.FullYRes();
cout << "x"<<xIrData.FullXRes()<<endl;
cv::imshow( "IR Image", IrImg );
cv::waitKey( 1 );
}
}
讚讚
Heresy您好,
經由閱讀您其他的文章,已經可以更改解析度(640*480/1280*1024),但格子狀的略為有改善但沒有消除,手上剛好有一個別人給的程式用同樣的設備能work,發現這程式抓出來的畫面範圍比直接從IRGenerator的大,而且也沒格子狀(條紋)和疊影,是否哪裡設錯了,所以資料放錯造成畫面被左右交疊?
讚讚
Heresy您好,
經過確認是資料型別轉換的問題,IR generator產生的是16bits的灰階不能用CV_8UC1去接,屬性不對。在google上查資料,發現要轉對資料再增亮才能較為清楚的看到IR image,應該解決這個的問題了,非常感謝您用心的文章!!
Many thanks,
Seigfried
—
部分code如下
IplImage *irImage = cvCreateImage( cvSize(640,480), 16, 1);
const XnIRPixel* irMap;
XnDepthPixel tempIR[640*480];
irMap = xIrImage.GetIRMap();
unsigned int max = 250;
for(int i = 0; i widthStep);
cvShowImage(“IR Image", irImage);
讚讚
for(int i = 0; i widthStep);
中間漏掉,奇怪了,補上…
讚讚
抱歉,有po但系統吃掉了,請刪掉吧上兩篇和這篇吧,謝謝。
讚讚
恭喜你的問題解決了。
附帶一提,< 等字元在網頁上也是特殊語法,所以在一般的留言系統裡,沒有做特殊處理的話都會被濾掉。
讚讚
Hi Heresy 老師您好.
我也遇到了Seigfried的問題,照您的sample code可以擷取影像,
並用open CV顯示出來,但是解析度為320X240.
Seigfried的code似乎不是很完整,請問老師有什麼方法,可以使得
解析度變成640×480嗎?
我有試過
XnMapOutputMode mapMode;
mapMode.nXRes = 640;
mapMode.nYRes = 480;
mapMode.nFPS = 30;
result = depthGenerator.SetMapOutputMode( mapMode );
result = imageGenerator.SetMapOutputMode( mapMode );
可是加上去之後反而完全得不到Xtion pro live的資料.
謝謝
讚讚
建議請先檢查 SetMapOutputMode() 回傳的結果,看看錯誤訊息到底是什麼。
讚讚
Hi Heresy 老師您好.
我看到SetMapOutputMode()的回傳值是65575,
請問哪裡可以查這個數值表示的是什麼呢?
另外我用同一份code,同一台Xtion pro live,
在PC上顯示的resolution是320×240,但是在embeded system顯示的resolution是160×120.
請問您有什麼想法嗎?
謝謝
讚讚
請使用 xnGetStatusString() 來查詢錯誤內容。
https://kheresy.wordpress.com/2011/01/20/read_kinect_depth_data_via_openni/
讚讚
因為想要建立一個系統的關係,所以我想使用OpenCV將這程式的"6a"的cDepthImg(16bit)存下來,而不是將"6b"的c8BitDepth(8bit)存下來,但是卻發生存下來的畫面跟截取到的畫面完全失真,查了一些資料但不知問題出在哪,想請教Heresy。
我用的OpenCV函數是cvSaveImage,存檔格式為bmp檔。謝謝
讚讚
個人不建議用 BMP 來儲存 16bit 的影像。
基本上,大部分的影像格式主要都是針對單一 channel 8bit 來做設計的,只有少部分的格式,有支援 16bit 這類的特殊格式。
建議先看一下官方文件給的說明:
http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#imwrite
imwrite 的部分也有提到:
Only 8-bit (or 16-bit unsigned (CV_16U) in case of PNG, JPEG 2000, and TIFF) single-channel or 3-channel (with ‘BGR’ channel order) images can be saved using this function.
讚讚
你好,我是初學者,如果我要執行上面這個程式,要做那些環境設定?
可否請你指導一下??
因為我看網路上許多對於openCV&openNI環境設定有太多種惹QQ
一整個混亂~~
如果要執行上面的城市需要在什麼樣的環境之下呢??
((我是用visual studio 2010
謝謝Heresy大大~~
讚讚
如果是完全都沒用過,建議你先獨立測試。
例如,先確定 OpenCV 的設定、並可以正常運作,然後再去測試 OpenNI 的設定。
當兩者都可以正確設定後,再來考慮合併。(通常這時候應該已經可以理解一般的專案設定是怎麼做的了)
OpenCV 有在玩的人很多,網路上也可以看到很多很仔細的教學。
例如: http://mark-jo-prog.blogspot.tw/2012/08/opencv-242-visual-studio-2010.html
不同的差異大多是在於版本、以及有用到的部分。
OpenNI 的部分,這篇使用的是 OpenNI 1 的 API,建議請參考
https://kheresy.wordpress.com/index_of_openni_and_kinect/documents-of-openni-1-x/
這系列的文章。
Visual Studio 的設定在課程的投影片裡面也有
https://skydrive.live.com/view.aspx?cid=E0070FB8ECF9015F&resid=E0070FB8ECF9015F%2113696&app=WordPdf&wdo=1
讚讚
補充一下,我是先學習的openNi最新的2版本,然後網上又看到了一些範例,但是好多都是用1的xn寫的,不太看得懂,電腦裡也沒裝1,所以無法編譯運行,想自己給轉換成2的,在進行後面的操作。謝謝
讚讚
您好,初學者,請見諒,我想問一下,您提到是用OpenNi先讀影像資料然後再轉換成openCV格式而沒有用videocapture(),這兩個有什麼區別呢,如果是爲了後期的手勢識別,比如需要對捕獲的影響進行手部分析優化之類的,該用哪種方法呢?
另外,如果要進行openNi 1和 2版本的互轉,該如何做呢,只需修改前面獲取資料的那一部份是吧?ImageMetaData,跟depth generator 相當於2裡的什麽呢? VideoStream mDepthStream 跟 VideoFrameRef 嗎?
非常感謝!
同時也該謝您對之前幾次提問的回覆~
讚讚
透過整合好的 OpenCV 來讀取深度影像,對於熟系 OpenCV 的人來說,會比較簡單,但是相對的,在細部控制上,就沒辦法像直接使用 OpenNI 那樣完整了。
再加上 OpenCV 整合的應該只有 OpenNI 1,不適用於 OpenNI 2,所以 Heresy 會建議直接使用 OpenNI。
OpenNI 2 的教學 Heresy 已經有把基礎的部分都寫完了,建議請參考
https://kheresy.wordpress.com/index_of_openni_and_kinect/
讚讚
[…] 放棄了比較繁瑣、檔案很多的 Qt,而改採用相對簡單的 OpenCV,來做為 2D […]
讚讚
Heresy大你好,
不好意思我用另一種方法照您步驟打程式碼
做出基本的深度圖像與彩色圖像
但是關於Color Edge 與 Depth Edge 不知道如何打
是否可以稍微提點一下 感謝你
程式碼如下:
#include
#include
#include
#include
#include “opencv/cv.h"
#include “opencv/highgui.h"
using namespace std;
using namespace cv;
void CheckOpenNIError( XnStatus result, string status )
{
if( result != XN_STATUS_OK )
cerr << status << " Error: " << xnGetStatusString( result ) <imageData,depthMD.Data(),640*480*2);
cvConvertScale(imgDepth16u,depthShow,255/4096.0,0);
memcpy(imgRGB8u->imageData,imageMD.Data(),640*480*3);
cvCvtColor(imgRGB8u,imageShow,CV_RGB2BGR);
cvShowImage(“depth", depthShow);
cvShowImage(“image",imageShow);
key=cvWaitKey(20);
}
//destroy
cvDestroyWindow(“depth");
cvDestroyWindow(“image");
cvReleaseImage(&imgDepth16u);
cvReleaseImage(&imgRGB8u);
cvReleaseImage(&depthShow);
cvReleaseImage(&imageShow);
context.StopGeneratingAll();
context.Shutdown();
return 0;
}
讚讚
程式碼貼上去出現一些問題 我是參考這一篇的
http://rritw.com/a/bianchengyuyan/_NET/20110818/103744.html
是否可以依這篇的方法做出這四張圖呢?
讚讚
你所使用的是 OpenCV 的 C 版本的 API,所以介面不同。
建議請參考 OpenCV 官網的 C API 的說明文件。
http://opencv.willowgarage.com/documentation/c/imgproc_feature_detection.html
讚讚
板大您好
我照您是面程式碼RUN過
但是出現ERROR:無法開啟包含檔案: ‘imgproc.hpp’: No such file or directory
發現那三行Header程式都找不到
是因為路徑問題嗎?
但我搜尋一下我的OPENCV內似乎沒有imgproc 跟 core 這兩個的hpp檔
讚讚
請確認一下你的 OpenCV 版本。
目前官方網站上下載的 OpenCV 應該都有提供 opencv2,也就是 C++ 版本的 API,裡面應該都有必要的檔案。
(檔案在 \build\include\opencv2 )
讚讚