Kinect+OpenNI學習筆記之2(獲取kinect的顏色圖像和深度圖像)


 

  前言

  網上有不少使用Qt做界面,OpenNI為庫來開發kinect。或許大家的第一個問題就是詢問該怎樣使用Kinect來獲取顏色信息圖和深度信息圖呢?這一節就是簡單來回答這個問題的。

  開發環境:QtCreator2.5.1+OpenNI1.5.4.0+Qt4.8.2

 

  實驗說明:

  在使用OpenNI來驅動讀取kinect數據時,我們需要了解context object這個名詞。查看了下OpenNI UserGuide文檔,簡單翻譯下這個名詞的意思:

  Context是openNI中一個主要的object,它掌握了OpenNI使用過程中應用程序的全部狀態,以及這些狀態的prodection chains,一個應用程序有多個context,但是這些context之間不能共享信息。例如一個中間件節點不能使用另一個context的驅動節點。Context在使用前必須被立即初始化,因此此時所有嵌入的模塊被下載和分析。為了釋放context的內存,應用程序需調用shutdown程序。      

  雖然翻譯得不准確,但是它的大概意思就是告訴我們在驅動kinect時,需要用到context這個類,且我們需要安裝一定順序去使用,這與一些常見的庫驅動差不多,比如opengl,這些都需要什么初始化啊,設置屬性啊等。因此我們只需要直接去看懂他人的一個工程實例就ok了。

  好了,本文參考Heresy的教程中的源碼寫的。

  在新建好工程文件后,需要包含XnCppWrapper頭文件,且需在Qt工程中設置好頭文件目錄和庫文件目錄。

  使用OpenNI讀取顏色圖和深度圖的步驟如下(這個是程序的核心部分):

  1. 定義一個Context對象,並 調用該對象的Init()方法來進行初始化。

  2. 定義一個XnMapOutputMode格式對象,設置好分圖像分辨率和幀率。

  3. 定義顏色圖和深度圖的節點對象,並用其Create()方法來創建,參數為Context對象.

  4. 設置顏色和深度圖的輸出模式,調用的方法是SetMapOutputMode();參數為步驟2中定義和設置好了的XnMapOutputMode對象。

  6. 如果深度圖和顏色圖在一張圖上顯示,則必須對深度圖像進行校正,校正的方法是調用深度圖的如下方法:.GetAlternativeViewPointCap().SetViewPoint();

  7. 調用context對象的StartGeneratingAll()來開啟設備讀取數據開關。

  8. 調用context對象的更新數據方法,比如WaitAndupdateAll()方法。

  9. 定義顏色圖和色彩圖的ImageMetaData對象,並利用對應的節點對象的方法GetMetaData(),將獲取到的數據保存到對應的ImageMetaData對象中。

  10. 如果需要將深度圖轉換成灰度圖來顯示,則需要自己將深度值轉換成0~255的單通道或者多通道數據,然后直接用來顯示。

  注意如果沒有設置視覺校正,則深度圖的顯示與顏色圖的顯示會出現對應不上的情況,后面的實驗可以看出這2者的區別,另外對於是否需要設置鏡像就要看自己的具體應用場合了。

 

  實驗結果:

  下面分別分是否設置圖像鏡像,是否對深度圖像進行校正來給出實驗結果.

  無鏡像無校正:

  

 

  無鏡像有校正:

  

 

  有鏡像無校正:  

  

 

  有鏡像有校正:

  

 

  從有無鏡像可以看出,設置鏡像的效果與字面的理解是一樣的,即有鏡像時就相當於取鏡子中的圖像。有無校正可以看出,沒有校正時,深度圖片和顏色圖片同一個物體都對應不起來,可以看下天花板上的吊燈就可以發現,沒校正,2者不重合,且相差不少。有校正時效果就好多了,只是此時的深度圖像顯示的范圍要稍小些。

 

  實驗主要部分代碼及注釋(附錄有工程code下載鏈接):

  首先來個最小工程,即去掉那些錯誤處理代碼:

  main.cpp:

#include <QtGui>
#include <XnCppWrapper.h> //包含OpenNI的頭文件

using namespace xn;//使用OpenNI庫中的命名空間

//全局的OpenNI object
Context g_context;
ImageGenerator g_image_generator;
DepthGenerator g_depth_generator;

//全局的Qt Object
QGraphicsPixmapItem *g_image_map;
QGraphicsPixmapItem *g_depth_map;

//CTimer類的定義
class CTimer : public QObject
{
public:
    void start() {        
        g_context.StartGeneratingAll();//開啟設備讀取數據的開關
        startTimer(33);//使用startTimer()啟動定時器,每當時間到時會自動調用timerEvent()函數
    }
private:
    void timerEvent(QTimerEvent *) {
        g_context.WaitAndUpdateAll();//更新數據

        //顏色數據
        ImageMetaData image_map;
        g_image_generator.GetMetaData(image_map);
        //為g_image_map設置圖片,圖片的數據來源於外部硬件設備
        g_image_map->setPixmap(QPixmap::fromImage(QImage(image_map.Data(), image_map.XRes(),
                                                         image_map.YRes(), QImage::Format_RGB888)));
        //深度數據
        DepthMetaData depth_map;
        g_depth_generator.GetMetaData(depth_map);
        XnDepthPixel max_depth_value = depth_map.ZRes();
        QImage depth_img(depth_map.XRes(), depth_map.YRes(), QImage::Format_ARGB32);//格式為ARGB32型的
        for(unsigned int i = 0; i < depth_map.XRes(); i++)
            for(unsigned int j = 0; j < depth_map.YRes(); j++)
            {
                XnDepthPixel depth_value_ij = depth_map(i, j);//獲取x,y處的坐標值
                if(depth_value_ij == 0) {
                    depth_img.setPixel(i, j, qRgba(0, 0, 0, 0));
                }//如果捕捉不到深度信息,則將其設置為0
                else {
                    float fscale = 1.0f*depth_value_ij/max_depth_value;//當前深度的比例因子
                    depth_img.setPixel(i, j, qRgba(255*(1-fscale), 0, 255*fscale, 255*(1-fscale)));
                }
            }
        g_depth_map->setPixmap(QPixmap::fromImage(depth_img));
    }
};

int  main(int argc, char **argv)
{
    QApplication app(argc, argv);

    g_context.Init();//context初始化
    g_context.SetGlobalMirror(true);//設置全局鏡像,就像照鏡子一樣,與設置為false時的2張圖片鏡像
    XnMapOutputMode xmode;//定義圖像的輸出模式
    xmode.nXRes = 640;//x方向分辨率
    xmode.nYRes = 480;//y方向分辨率
    xmode.nFPS = 30;//幀率
    //設置顏色節點屬性
    g_image_generator.Create(g_context);
    g_image_generator.SetMapOutputMode(xmode);
    //設置深度節點屬性
    g_depth_generator.Create(g_context);
    g_depth_generator.SetMapOutputMode(xmode);

    //視覺校正,否則深度圖和顏色圖感應到的區域不能一一對應
    g_depth_generator.GetAlternativeViewPointCap().SetViewPoint(g_image_generator);

    //Qt場景設置
    QGraphicsScene scene;
    g_image_map = scene.addPixmap(QPixmap());
    g_image_map->setZValue(1);//設置為z方向上的第1層
    g_depth_map = scene.addPixmap(QPixmap());
    g_depth_map->setZValue(2);//設置為z方向上的第2層

    //Qt視圖創建
    QGraphicsView view(&scene);
    view.resize(660, 500);

    //設置定時器,每隔一段時間讀取kinect的顏色信息和深度信息
    CTimer timer;
    timer.start();
    view.show();

    return app.exec();
}

 

  加入錯誤處理部分后的完整main.cpp:

#include <QtGui>
#include <XnCppWrapper.h> //包含OpenNI的頭文件

using namespace xn;//使用OpenNI庫中的命名空間

//全局的OpenNI object
XnStatus g_status;
Context g_context;
ImageGenerator g_image_generator;
DepthGenerator g_depth_generator;
bool g_has_image_generator = true;

//全局的Qt Object
QGraphicsPixmapItem *g_image_map;
QGraphicsPixmapItem *g_depth_map;

//CTimer類的定義
class CTimer : public QObject
{
public:
    void start() {
        g_status = g_context.StartGeneratingAll();//開啟設備讀取數據的開關
        if(g_status == XN_STATUS_OK) {
            startTimer(33);//使用startTimer()啟動定時器,每當時間到時會自動調用timerEvent()函數
        }
        else {
            QMessageBox::critical(NULL, "Create Data Error!", xnGetStatusString(g_status));//顯示創建數據失敗,該消息框沒有父窗口
        }
    }
private:
    void timerEvent(QTimerEvent *) {
        g_context.WaitAndUpdateAll();//更新數據

        //顏色數據
        if(g_has_image_generator) {
            ImageMetaData image_map;
            g_image_generator.GetMetaData(image_map);
            //為g_image_map設置圖片,圖片的數據來源於外部硬件設備
            g_image_map->setPixmap(QPixmap::fromImage(QImage(image_map.Data(), image_map.XRes(),
                                                         image_map.YRes(), QImage::Format_RGB888)));
        }
        //深度數據
        DepthMetaData depth_map;
        g_depth_generator.GetMetaData(depth_map);
        XnDepthPixel max_depth_value = depth_map.ZRes();
        QImage depth_img(depth_map.XRes(), depth_map.YRes(), QImage::Format_ARGB32);//格式為ARGB32型的
        for(unsigned int i = 0; i < depth_map.XRes(); i++)
            for(unsigned int j = 0; j < depth_map.YRes(); j++)
            {
                XnDepthPixel depth_value_ij = depth_map(i, j);//獲取x,y處的坐標值
                if(depth_value_ij == 0) {
                    depth_img.setPixel(i, j, qRgba(0, 0, 0, 0));
                }//如果捕捉不到深度信息,則將其設置為0
                else {
                    float fscale = 1.0f*depth_value_ij/max_depth_value;//當前深度的比例因子
                    depth_img.setPixel(i, j, qRgba(255*(1-fscale), 0, 255*fscale, 255*(1-fscale)));
                }
            }
        g_depth_map->setPixmap(QPixmap::fromImage(depth_img));
    }
};

int  main(int argc, char **argv)
{
    QApplication app(argc, argv);

    g_status = g_context.Init();//context初始化
    if(g_status != XN_STATUS_OK) {
        QMessageBox::critical(NULL, "Context Initial Error!", xnGetStatusString(g_status));
        return -1;
    }
   // g_context.SetGlobalMirror(true);//設置全局鏡像,就像照鏡子一樣,與設置為false時的2張圖片鏡像
    XnMapOutputMode xmode;//定義圖像的輸出模式
    xmode.nXRes = 640;//x方向分辨率
    xmode.nYRes = 480;//y方向分辨率
    xmode.nFPS = 30;//幀率
    //設置顏色節點屬性
    g_status = g_image_generator.Create(g_context);
    if(g_status != XN_STATUS_OK) {
        QMessageBox::critical(NULL, "Image map create failed", xnGetStatusString(g_status));
        g_has_image_generator = false;
    }
    if( g_has_image_generator ) {
        g_status = g_image_generator.SetMapOutputMode(xmode);
        if(g_status != XN_STATUS_OK) {
            QMessageBox::critical(NULL, "Image map output mode error!", xnGetStatusString(g_status));
            return -1;
        }
    }
    //設置深度節點屬性
    g_status = g_depth_generator.Create(g_context);
    if(g_status != XN_STATUS_OK) {
        QMessageBox::critical(NULL, "Depth map create failed", xnGetStatusString(g_status));
        return -1;
    }
    g_status = g_depth_generator.SetMapOutputMode(xmode);
    if(g_status != XN_STATUS_OK) {
        QMessageBox::critical(NULL, "Depth map output mode error!", xnGetStatusString(g_status));
        return -1;
    }

    if(g_has_image_generator)//視覺校正,否則深度圖和顏色圖感應到的區域不能一一對應
        ;//g_depth_generator.GetAlternativeViewPointCap().SetViewPoint(g_image_generator);
    //Qt場景設置
    QGraphicsScene scene;
    g_image_map = scene.addPixmap(QPixmap());
    g_image_map->setZValue(1);//設置為z方向上的第1層
    g_depth_map = scene.addPixmap(QPixmap());
    g_depth_map->setZValue(2);//設置為z方向上的第2層

    //Qt視圖創建
    QGraphicsView view(&scene);
    view.resize(660, 500);

    //設置定時器,每隔一段時間讀取kinect的顏色信息和深度信息
    CTimer timer;
    timer.start();
    view.show();

    return app.exec();
}

 

 

  總結:通過本次實驗,了解了怎樣使用OpenNI來顯示kinect的顏色圖像和深度圖像了。

 

 

  參考資料:http://kheresy.wordpress.com/index_of_openni_and_kinect/comment-page-5/

 

 

  附錄:實驗工程code下載

 

 

 


免責聲明!

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



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