前言
在上一篇文章Kinect+OpenNI學習筆記之2(獲取kinect的顏色圖像和深度圖像) 中,已經介紹了怎樣使用OpenNI來獲取Kinect的深度數據和顏色數據,並將獲取到的結果在Qt中顯示,不過那個代碼是寫在同一個cpp文件中,以后用到的時候不能講這些顯示的基本過程單獨拿出來,比較麻煩。所以這節主要是將OpenNI獲取圖像的流程以及Qt顯示這些圖像的結果分開為了2個類來寫,方便以后工程的直接拷貝。
開發環境:QtCreator2.5.1+OpenNI1.5.4.0+Qt4.8.2
實驗說明
COpenNI這個類主要是初始化kinect設備,並獲取深度圖像和顏色圖像,參加上一篇博客的初始化過程步驟,如果遇到錯誤,則有相應的錯誤處理過程。CKinectReader類是將COpenNI這個類讀取到的結果顯示在Qt的界面上的。因此一個類是負責與硬件Kinect打交道,一個類是負責與人(界面顯示)打交道的。具體的過程見上篇文章的分析和后面的代碼。
這里發現一個小問題,與kinect有關的工程如果改變了代碼,則在每次編譯前最好clean一下,因為有可能是與硬件設備相關,沒有clean的工程和clean后的工程效果有時會不同。
C/C++知識點總結:
在構造函數中可以使用冒號給類中的數據成員賦值,這樣的好處就是可以給常量和引用變量賦值初始化賦值的效果。
類的私有成員只能是類內部的函數調用,連類的對象都不能去調用私有成員變量。
在類的內部使用qDebug(), cout等函數輸出調試時是不行的。
隱式數據類型轉換,如果是同種類型的數據進行四則運算,則得出的結果也是那種類型,如果其中有常數類型的數據常數參與,則得出的結果會自動轉換成跟常數類型相同的類型。
如果一個類以單獨一個cpp文件出現,在使用到該類的時候,直接include該cpp文件.
實驗結果
在程序中設置了鏡像和視覺校正,且將kinect感應不到深度信息的地方全部顯示為不透明的黑色,因此你在圖中看到的黑色部分就是kinect的深度盲區。
效果如下:
實驗主要部分代碼及注釋(附錄有工程code下載鏈接):
copenni.cpp:
#include <XnCppWrapper.h> #include <QtGui> #include <iostream> using namespace xn; using namespace std; class COpenNI { public: ~COpenNI() { context.Release();//釋放空間 } bool Initial() { //初始化 status = context.Init(); if(CheckError("Context initial failed!")) { return false; } context.SetGlobalMirror(true);//設置鏡像 //產生圖片node status = image_generator.Create(context); if(CheckError("Create image generator error!")) { return false; } //產生深度node status = depth_generator.Create(context); if(CheckError("Create depth generator error!")) { return false; } //視角校正 status = depth_generator.GetAlternativeViewPointCap().SetViewPoint(image_generator); if(CheckError("Can't set the alternative view point on depth generator")) { return false; } return true; } bool Start() { status = context.StartGeneratingAll(); if(CheckError("Start generating error!")) { return false; } return true; } bool UpdateData() { status = context.WaitNoneUpdateAll(); if(CheckError("Update date error!")) { return false; } //獲取數據 image_generator.GetMetaData(image_metadata); depth_generator.GetMetaData(depth_metadata); return true; } public: DepthMetaData depth_metadata; ImageMetaData image_metadata; private: //該函數返回真代表出現了錯誤,返回假代表正確 bool CheckError(const char* error) { if(status != XN_STATUS_OK ) { QMessageBox::critical(NULL, error, xnGetStatusString(status)); cerr << error << ": " << xnGetStatusString( status ) << endl; return true; } return false; } private: XnStatus status; Context context; DepthGenerator depth_generator; ImageGenerator image_generator; };
ckinectreader.cpp:
#include <QtGui> #include <QDebug> #include <XnCppWrapper.h> #include "copenni.cpp" //要包含cpp文件,不能直接包含類 #include <iostream> using namespace std; class CKinectReader: public QObject { public: //構造函數,用構造函數中的變量給類的私有成員賦值 CKinectReader(COpenNI &openni, QGraphicsScene &scene) : openni(openni), scene(scene) { test = 0.0; } ~CKinectReader() { scene.removeItem(image_item); scene.removeItem(depth_item); delete [] p_depth_argb; } bool Start(int interval = 33) { openni.Start();//因為在調用CKinectReader這個類的之前會初始化好的,所以這里直接調用Start了 image_item = scene.addPixmap(QPixmap()); image_item->setZValue(1); depth_item = scene.addPixmap(QPixmap()); depth_item->setZValue(2); openni.UpdateData(); p_depth_argb = new uchar[4*openni.depth_metadata.XRes()*openni.depth_metadata.YRes()]; startTimer(interval);//這里是繼承QObject類,因此可以調用該函數 return true; } float test ; private: COpenNI &openni; //定義引用同時沒有初始化,因為在構造函數的時候用冒號來初始化 QGraphicsScene &scene; QGraphicsPixmapItem *image_item; QGraphicsPixmapItem *depth_item; uchar *p_depth_argb; private: void timerEvent(QTimerEvent *) { openni.UpdateData(); //這里使用const,是因為右邊的函數返回的值就是const類型的 const XnDepthPixel *p_depth_pixpel = openni.depth_metadata.Data(); unsigned int size = openni.depth_metadata.XRes()*openni.depth_metadata.YRes(); //找深度最大值點 XnDepthPixel max_depth = *p_depth_pixpel; for(unsigned int i = 1; i < size; ++i) if(p_depth_pixpel[i] > max_depth ) max_depth = p_depth_pixpel[i]; test = max_depth; //將深度圖像格式歸一化到0~255 int idx = 0; for(unsigned int i = 1; i < size; ++i) { //一定要使用1.0f相乘,轉換成float類型,否則該工程的結果會有錯誤,因為這個要么是0,要么是1,0的概率要大很多 float fscale = 1.0f*(*p_depth_pixpel)/max_depth; if((*p_depth_pixpel) != 0) { p_depth_argb[idx++] = 255*(1-fscale); //藍色分量 p_depth_argb[idx++] = 0; //綠色分量 p_depth_argb[idx++] = 255*fscale; //紅色分量,越遠越紅 p_depth_argb[idx++] = 255*(1-fscale); //距離越近,越不透明 } else { p_depth_argb[idx++] = 0; p_depth_argb[idx++] = 0; p_depth_argb[idx++] = 0; p_depth_argb[idx++] = 255; } ++p_depth_pixpel;//此處的++p_depth_pixpel和p_depth_pixpel++是一樣的 } //往item中設置圖像色彩數據 image_item->setPixmap(QPixmap::fromImage( QImage(openni.image_metadata.Data(), openni.image_metadata.XRes(), openni.image_metadata.YRes(), QImage::Format_RGB888))); //往item中設置深度數據 depth_item->setPixmap(QPixmap::fromImage( QImage(p_depth_argb, openni.depth_metadata.XRes(), openni.depth_metadata.YRes() , QImage::Format_ARGB32))); } };
main.cpp:
#include <QtGui/QtGui> #include <QDebug> #include "ckinectreader.cpp" int main(int argc, char **argv) { COpenNI openni; if(!openni.Initial())//初始化返回1表示初始化成功 return 1; QApplication app(argc, argv); QGraphicsScene scene; QGraphicsView view; view.setScene(&scene); view.resize(650, 540); view.show(); CKinectReader kinect_reader(openni, scene); kinect_reader.Start();//啟動,讀取數據 qDebug() << kinect_reader.test; return app.exec(); }
總結:這次實驗的目的主要是將相互稍微獨立的代碼用單獨的類來寫,方便以后的代碼重復利用。
參考資料:http://kheresy.wordpress.com/2011/08/18/show_maps_of_openni_via_qt_graphicsview/
附錄:實驗工程code下載。