zoukankan      html  css  js  c++  java
  • 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下载

  • 相关阅读:
    【转载】理解本真的REST架构风格
    Linux常用命令
    使用MongoDB存储集合的一些问题
    AutoMapper快速上手
    JavaScript instanceof 运算符深入剖析
    使用c#对MongoDB进行查询(1)
    centos7安装rabbitmq3.7.9
    nginx1.14.0版本高可用——keepalived双机热备
    nginx1.14.0版本https加密配置
    nginx1.14.0版本负载均衡upstream配置
  • 原文地址:https://www.cnblogs.com/tornadomeet/p/2708029.html
Copyright © 2011-2022 走看看