Kinect+OpenNI學習筆記之3(獲取kinect的數據並在Qt中顯示的類的設計)


 

  前言

  在上一篇文章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下載

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM