Kinect开发教程八:OpenNI2显示深度、彩色及融合图像

      在《Kinect开发教程二:OpenNI读取深度图像与彩色图像并显示》中,小斤介绍了OpenNI读取深度与彩色图像数据的方法,并且借助OpenCV进行显示。

      OpenNI2在接口上与OpenNI有了较大变化,具体更新可以查看《OpenNI Migration Guide》。从获取深度,彩色传感器的数据而言,小斤觉得调用更为直观,但对于Kinect,一大缺憾是不支持OpenNI2提供的深度与彩色图像配准的方法(体现在下文中的device.isImageRegistrationModeSupported()方法)。

      但使用Kinect的童鞋也不必沮丧,在OpenNI2.1 beta中,小斤看到了新增的convertDepthToColorCoordinates()方法可以做一些深度与彩色坐标数据的转化,它的效果应该 是与device.setImageRegistrationMode( IMAGE_REGISTRATION_DEPTH_TO_COLOR )类似的,有兴趣的童鞋可以尝试一下。

      在显示方面,小斤还是使用OpenCV,这次是使用OpenCV的C++接口进行操作。

    /************************* 
    OpenNI2 Deep, Color and Fusion Image 
    Author: Xin Chen, 2013.2 
    Blog: http://blog.csdn.net/chenxin_130 
    *************************/  
      
    #include <stdlib.h>  
    #include <iostream>  
    #include <string>  
    #include "OpenNI.h"  
    #include "opencv2/core/core.hpp"  
    #include "opencv2/highgui/highgui.hpp"  
    #include "opencv2/imgproc/imgproc.hpp"  
    using namespace std;  
    using namespace cv;  
    using namespace openni;  
      
    void CheckOpenNIError( Status result, string status )  
    {   
        if( result != STATUS_OK )   
            cerr << status << " Error: " << OpenNI::getExtendedError() << endl;  
    }  
      
    int main( int argc, char** argv )  
    {  
        Status result = STATUS_OK;    
          
        //OpenNI2 image  
        VideoFrameRef oniDepthImg;  
        VideoFrameRef oniColorImg;  
      
        //OpenCV image  
        cv::Mat cvDepthImg;  
        cv::Mat cvBGRImg;  
        cv::Mat cvFusionImg;  
          
        cv::namedWindow("depth");  
        cv::namedWindow("image");  
        cv::namedWindow("fusion");  
        char key=0;  
      
        //【1】  
        // initialize OpenNI2  
        result = OpenNI::initialize();  
        CheckOpenNIError( result, "initialize context" );    
      
        // open device    
        Device device;  
        result = device.open( openni::ANY_DEVICE );  
      
        //【2】  
        // create depth stream   
        VideoStream oniDepthStream;  
        result = oniDepthStream.create( device, openni::SENSOR_DEPTH );  
      
        //【3】  
        // set depth video mode  
        VideoMode modeDepth;  
        modeDepth.setResolution( 640, 480 );  
        modeDepth.setFps( 30 );  
        modeDepth.setPixelFormat( PIXEL_FORMAT_DEPTH_1_MM );  
        oniDepthStream.setVideoMode(modeDepth);  
        // start depth stream  
        result = oniDepthStream.start();  
       
        // create color stream  
        VideoStream oniColorStream;  
        result = oniColorStream.create( device, openni::SENSOR_COLOR );  
        // set color video mode  
        VideoMode modeColor;  
        modeColor.setResolution( 640, 480 );  
        modeColor.setFps( 30 );  
        modeColor.setPixelFormat( PIXEL_FORMAT_RGB888 );  
        oniColorStream.setVideoMode( modeColor);  
          
    //【4】  
        // set depth and color imge registration mode  
        if( device.isImageRegistrationModeSupported(IMAGE_REGISTRATION_DEPTH_TO_COLOR ) )  
        {  
            device.setImageRegistrationMode( IMAGE_REGISTRATION_DEPTH_TO_COLOR );  
        }  
        // start color stream  
        result = oniColorStream.start();    
      
        while( key!=27 )   
        {    
            // read frame  
            if( oniColorStream.readFrame( &oniColorImg ) == STATUS_OK )  
            {  
                // convert data into OpenCV type  
                cv::Mat cvRGBImg( oniColorImg.getHeight(), oniColorImg.getWidth(), CV_8UC3, (void*)oniColorImg.getData() );  
                cv::cvtColor( cvRGBImg, cvBGRImg, CV_RGB2BGR );  
                cv::imshow( "image", cvBGRImg );  
            }  
        
            if( oniDepthStream.readFrame( &oniDepthImg ) == STATUS_OK )  
            {  
                cv::Mat cvRawImg16U( oniDepthImg.getHeight(), oniDepthImg.getWidth(), CV_16UC1, (void*)oniDepthImg.getData() );  
                cvRawImg16U.convertTo( cvDepthImg, CV_8U, 255.0/(oniDepthStream.getMaxPixelValue()));  
                //【5】  
                // convert depth image GRAY to BGR  
                cv::cvtColor(cvDepthImg,cvFusionImg,CV_GRAY2BGR);  
                cv::imshow( "depth", cvDepthImg );  
            }  
            //【6】  
            cv::addWeighted(cvBGRImg,0.5,cvFusionImg,0.5,0,cvFusionImg);  
            cv::imshow( "fusion", cvFusionImg );  
            key = cv::waitKey(20);  
        }  
      
        //cv destroy  
        cv::destroyWindow("depth");  
        cv::destroyWindow("image");  
        cv::destroyWindow("fusion");  
      
        //OpenNI2 destroy  
        oniDepthStream.destroy();  
        oniColorStream.destroy();  
        device.close();  
        OpenNI::shutdown();  
      
        return 0;  
    }  

 小斤由上到下解释一把:

      【1】使用OpenNI::initialize()方法进行初始化,对于错误处理,可以使用OpenNI::getExtendedError()方法。在这里,Device对象打开任意一个可用设备。

      【2】在OpenNI2中,可以通过创建VideoStream视频流对象来读取设备的深度图像和色彩图像数据。

      【3】对于VideoStream视频流对象,我们可以设备它的Mode,包括分辨率,FPS,像素格式等等。对于像素格式的类型,可以使用 VideoStream的getSensorInfo()方法获得,目前Kinect只有PIXEL_FORMAT_DEPTH_1_MM可供选择。

      【4】如果设备支持深度与彩色图像配准的话,小斤在这里使用OpenNI2自带的接口进行配准。在while循环中,各个VideoStream对象通过readFrame()来读取对应的图像数据。

      【5】将OpenNI的图像数据转换为OpenCV可显示的图像格式。对于彩色图像,可以先将数据塞入OpenCV三通道(8位)RGB对象,再转换到 BGR来显示。对于深度图像,先放入单通道(16位)对象(这是因为深度数据的值域较大),最近将深度值等比例缩小到[0,255]的值域中,作为灰度图 显示。

      【6】最后的图像融合,由于addWeighted()方法需要两个输入图像是同一类型,所以小斤首先将深度灰度图(单通道),转化为BGR图像,这样就 与彩色图像一致了。再通过该方法进行融合,小斤使用的比例是0.5,0.5,也就是融合图像的每个像素点的值,都是(深度图像该点的像素值*0.5)+ (彩色图像该点的像素值*0.5)。

 

----------------------------------

作者:小斤(陈忻)

新浪围脖:@小斤陈

本文属于原创文章,如需转载引用请注明原文作者和链接,谢谢。

你可能感兴趣的:(kinect)