開始之前,讓我們自己開始再熟練熟練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常用圖像轉換函數和圖像格式,我想這個程序就很好寫,並且很明了易懂了。