OpenNI 2與OpenCV結合的第一個程序

開始之前,讓我們自己開始再熟練熟練OpenNI 2的基本使用,主要包括以下幾個步驟:

    1. 初始化OpenNI環境: openni::OpenNI::initialize();

    2. 聲明並打開Device設備: openni::Device devAnyDevice; devAnyDevice.open( openni::ANY_DEVICE );

    3. 創建並打開深度數據流:openni::VideoStream streamDepth; streamDepth.create( devAnyDevice, openni::SENSOR_DEPTH ); streamDepth.start();

    4. 讀取數據流信息並保存在VideoFrameRef中:openni::VideoFrameRef frameDepth;streamDepth.readFrame( &frameDepth );

    5. 獲取深度(或顏色等)數據,開始我們自己的開發之旅: const openni::DepthPixel* pDepth = (const openni::DepthPixel*)frameDepth.getData();

    6. 當結束使用數據時,首先關閉、銷燬數據流:streamDepth.destroy();

    7. 接着關閉設備: devAnyDevice.close();

    8. 最後關閉OpenNI:  openni::OpenNI::shutdown();

具體代碼如下(環境配置在之前的博文中提及了,這裏省去)

複製代碼
#include <iostream>
#include "OpenNI.h"

int main( int argc, char** argv )
{
    // 初始化OpenNI環境
    openni::OpenNI::initialize();

    // 聲明並打開Device設備,我用的是Kinect。
    openni::Device devAnyDevice;
    devAnyDevice.open( openni::ANY_DEVICE );

    // 創建並打開深度數據流
    openni::VideoStream streamDepth;
    streamDepth.create( devAnyDevice, openni::SENSOR_DEPTH );
    streamDepth.start();

    // 同樣的創建並打開彩色圖像數據流
    openni::VideoStream streamColor;
    streamColor.create( devAnyDevice, openni::SENSOR_COLOR );
    streamColor.start();

    // 循環讀取數據流信息並保存在VideoFrameRef中
    openni::VideoFrameRef frameDepth;
    openni::VideoFrameRef frameColor;
    for( int i = 0; i < 1000; ++ i )
    {
        // 讀取數據流
        streamDepth.readFrame( &frameDepth );
        streamColor.readFrame( &frameColor );

        // 獲取data array
        const openni::DepthPixel* pDepth
            = (const openni::DepthPixel*)frameDepth.getData();
        const openni::RGB888Pixel* pColor
            = (const openni::RGB888Pixel*)frameColor.getData();

        // 顯示深度信息和對應的彩色R、G、B數值
        int idx = frameDepth.getWidth() * ( frameDepth.getHeight() + 1 ) / 2;
        std::cout  << pDepth[idx]  << "( "
            << (int)pColor[idx].r << ","
            << (int)pColor[idx].g << ","
            << (int)pColor[idx].b << ")"
            << std::endl;
    }

    // 關閉數據流
    streamDepth.destroy();
    streamColor.destroy();

    // 關閉設備
    devAnyDevice.close();

    // 最後關閉OpenNI
    openni::OpenNI::shutdown();

    return 0;
}
複製代碼

    但我們使用OpenNI和Kinect更多的是爲了研究開發之用,其中用到最多的是OpenCV(OpenGL、QT也很多,只是本人用的最多的是OpenCV),下面就結合OPenNI 2和OpenCV 2.4.3開始我的第一個程序:利用OpenCV函數顯示深度圖像和彩色圖像。直接上代碼:

複製代碼
// YeOpenNI2SimpleUsingOpenCV.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <iostream>
#include "OpenNI.h"

// 載入OpenCV頭文件
#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace openni;
using namespace cv;

int main( int argc, char** argv )
{
    // 初始化OpenNI環境
    OpenNI::initialize();

    // 聲明並打開Device設備,我用的是Kinect。
    Device devAnyDevice;
    devAnyDevice.open(ANY_DEVICE );

    // 創建深度數據流
    VideoStream streamDepth;
    streamDepth.create( devAnyDevice, SENSOR_DEPTH );

    // 創建彩色圖像數據流
    VideoStream streamColor;
    streamColor.create( devAnyDevice, SENSOR_COLOR );

    // 設置深度圖像視頻模式
    VideoMode mModeDepth;
    // 分辨率大小
    mModeDepth.setResolution( 640, 480 );
    // 每秒30幀
    mModeDepth.setFps( 30 );
    // 像素格式
    mModeDepth.setPixelFormat( PIXEL_FORMAT_DEPTH_1_MM );

    streamDepth.setVideoMode( mModeDepth);
    
    // 同樣的設置彩色圖像視頻模式
    VideoMode mModeColor;
    mModeColor.setResolution( 640, 480 );
    mModeColor.setFps( 30 );
    mModeColor.setPixelFormat( PIXEL_FORMAT_RGB888 );

    streamColor.setVideoMode( mModeColor);

    // 圖像模式註冊
    if( devAnyDevice.isImageRegistrationModeSupported(
        IMAGE_REGISTRATION_DEPTH_TO_COLOR ) )
    {
        devAnyDevice.setImageRegistrationMode( IMAGE_REGISTRATION_DEPTH_TO_COLOR );
    }
        
    // 打開深度和圖像數據流
    streamDepth.start();
    streamColor.start();

    // 創建OpenCV圖像窗口
    namedWindow( "Depth Image",  CV_WINDOW_AUTOSIZE );
    namedWindow( "Color Image",  CV_WINDOW_AUTOSIZE );

    // 獲得最大深度值
    int iMaxDepth = streamDepth.getMaxPixelValue();

    // 循環讀取數據流信息並保存在VideoFrameRef中
    VideoFrameRef  frameDepth;
    VideoFrameRef  frameColor;

    while( true )
    {
        // 讀取數據流
        streamDepth.readFrame( &frameDepth );
        streamColor.readFrame( &frameColor );

        
        // 將深度數據轉換成OpenCV格式
        const cv::Mat mImageDepth( frameDepth.getHeight(), frameDepth.getWidth(), CV_16UC1, (void*)frameDepth.getData());
        // 爲了讓深度圖像顯示的更加明顯一些,將CV_16UC1 ==> CV_8U格式
        cv::Mat mScaledDepth;
        mImageDepth.convertTo( mScaledDepth, CV_8U, 255.0 / iMaxDepth );
        // 顯示出深度圖像
        cv::imshow( "Depth Image", mScaledDepth );

        // 同樣的將彩色圖像數據轉化成OpenCV格式
        const cv::Mat mImageRGB(frameColor.getHeight(), frameColor.getWidth(), CV_8UC3, (void*)frameColor.getData());
        // 首先將RGB格式轉換爲BGR格式
        cv::Mat cImageBGR;
        cv::cvtColor( mImageRGB, cImageBGR, CV_RGB2BGR );
        // 然後顯示彩色圖像
        cv::imshow( "Color Image", cImageBGR );

        // 終止快捷鍵
        if( cv::waitKey(1) == 'q')
            break;
    }

    // 關閉數據流
    streamDepth.destroy();
    streamColor.destroy();

    // 關閉設備
    devAnyDevice.close();

    // 最後關閉OpenNI
    openni::OpenNI::shutdown();

    return 0;
}
複製代碼

    顯示效果見圖:

    這個是在原有的程序上做了添加,主要添加了:
    1. 深度圖像和彩色圖像的視頻模式的設置:streamDepth.setVideoMode( mModeDepth);和streamColor.setVideoMode( mModeColor);

視頻模式VideoMode類主要包括了:get/set像素格式、get/set分辨率(座標x和y值)、以及每秒多少幀圖像等。其中像素格式主要包括以下幾種:

enum      PixelFormat { 
  PIXEL_FORMAT_DEPTH_1_MM = 100, PIXEL_FORMAT_DEPTH_100_UM = 101, PIXEL_FORMAT_SHIFT_9_2 = 102, PIXEL_FORMAT_SHIFT_9_3 = 103, 
  PIXEL_FORMAT_RGB888 = 200, PIXEL_FORMAT_YUV422 = 201, PIXEL_FORMAT_GRAY8 = 202, PIXEL_FORMAT_GRAY16 = 203, 
  PIXEL_FORMAT_JPEG = 204 
}

主要分成兩類:深度像素格式和彩色圖像格式。具體是什麼含義,我想利用OpenCV常用圖像格式知識,自己多嘗試使用對照,發現它們的不同之處和特別之處。
    2. 設置彩色圖像視頻與深度視頻的視覺校正,但我們從上面的彩色圖像和深度圖像可以看出來,它們通過“校正”之後還是存在着一定的偏差,對於這個問題目前已有人做了修改,但官方還是沒有做具體的說明和糾正。在OPenNI 2的架構中,要進行視覺的校正,是直接調用Device提供的setImageRegistrationMode(ImageRegistrationMode mode) 函數來進行視覺校正的設置。但目前的OpenNI 2裏,只提供IMAGE_REGISTRATION_OFF = 0(不用校正)和 IMAGE_REGISTRATION_DEPTH_TO_COLOR = 1(把深度映射到彩色圖像的位置上)這兩種模式可以使用。根據目前的OpenNI 2已經不是隻針對Kinect一種感應器了,針對不同的感應器,不一定都要支持視覺校正的功能,所以在設定前,最好先判斷使用的感應器是否支持“視覺校正”功能,借用官方的話說:“It is a good practice to first check if the mode is supported by calling isImageRegistrationModeSupported(). ”其中ImageRegistrationMode枚舉如下:

enum ImageRegistrationMode { IMAGE_REGISTRATION_OFF = 0, IMAGE_REGISTRATION_DEPTH_TO_COLOR = 1 }

     3. 讀取深度圖像信息和彩色圖像信息,然後相應的轉換爲OpenCV格式,並顯示:

複製代碼
// 讀取數據流
        streamDepth.readFrame( &frameDepth );
        streamColor.readFrame( &frameColor );

        
        // 將深度數據轉換成OpenCV格式
        const cv::Mat mImageDepth( frameDepth.getHeight(), frameDepth.getWidth(), CV_16UC1, (void*)frameDepth.getData());
        // 爲了讓深度圖像顯示的更加明顯一些,將CV_16UC1 ==> CV_8U格式
        cv::Mat mScaledDepth;
        mImageDepth.convertTo( mScaledDepth, CV_8U, 255.0 / iMaxDepth );
        // 顯示出深度圖像
        cv::imshow( "Depth Image", mScaledDepth );

        // 同樣的將彩色圖像數據轉化成OpenCV格式
        const cv::Mat mImageRGB(frameColor.getHeight(), frameColor.getWidth(), CV_8UC3, (void*)frameColor.getData());
        // 首先將RGB格式轉換爲BGR格式
        cv::Mat cImageBGR;
        cv::cvtColor( mImageRGB, cImageBGR, CV_RGB2BGR );
        // 然後顯示彩色圖像
        cv::imshow( "Color Image", cImageBGR );
複製代碼

這個主要就是OpenCV的圖像格式和顯示有關的知識了,此處省去N個字。。。
     總結:知道了如何獲取深度和彩色圖像信息+OpenCV常用圖像轉換函數和圖像格式,我想這個程序就很好寫,並且很明瞭易懂了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章