【问题标题】:Efficient integration of Qt and OpenCVQt 和 OpenCV 的高效集成
【发布时间】:2012-04-26 20:59:42
【问题描述】:

我正在开发一个交互式应用程序,该应用程序需要一次读取和处理多个非常大的图像(一次 25 个图像,总大小约为 350 Mb)。 OpenCV 速度非常快,并且处理算法相对容易。但是用 Qt 绘制它们被证明是一个问题。以下是我尝试过的两种不太理想的解决方案。

解决方案 1(太慢)

每次需要绘制不同的 OpenCV 图像时,将其转换为 QImage 并画出来。不幸的是,转换需要一段时间 我们无法以交互速度在图像之间切换。

解决方案 2(太占用内存)

维护两组图像,一组用于 OpenCV,一组用于 Qt。使用 在适当的时候适当的。

我可以直接访问 OpenCV 像素数据。我知道图像的宽度和高度,并且我知道像素是 3 字节的 RGB 值。似乎应该可以快速绘制 OpenCV 图像,而无需将其复制到 QImage 容器(据我所知)只包含数据的副本。

要从 Qt 中获得这种功能,我需要在哪里寻找?

【问题讨论】:

    标签: c++ qt opencv


    【解决方案1】:

    我不知道这在 3 个月后是否对您现在有用。但是我有相同类型的应用程序,我必须使用 OpenCV 操作图像流并将其显示在 QT 界面上。在谷歌搜索了很多之后,我遇到了一个非常巧妙的解决方案。使用opengl的glDrawPixels直接在Qt界面上绘制原始图像数据。最好的部分,你不必编写任何额外的转换代码。只是用于设置视口和坐标的 opengl 的基本代码。查看代码,该代码具有一个函数,该函数采用 IplImage* 指针并使用该数据绘制图像。您可能需要稍微调整参数(尤其是 WIDTH 和 HEIGHT 变量)以显示具有特定尺寸的图像。 是的,我不知道您使用的是什么构建系统。尽管我使用的是 Qt 的 opengl 库,但我使用了 cmake 并且必须为 opengl 设置依赖项。

    我已经实现了一个类 QIplImage,它派生自 QGLWidget 并重写了它的 paintGL 方法来将像素数据绘制到框架上。

    //File qiplimage.h
    class QIplImage : public QGLWidget
    {
      Q_OBJECT
    
     public:
        QIplImage(QWidget *parent = 0,char *name=0);
       ~QIplImage();
       void paintGL();
       void initializeGL();
       void resizeGL(int,int);
       bool drawing;
    
     public slots:
       void setImage(IplImage);
    
     private:
      Ui::QIplImage ui;
      IplImage* original;
      GLenum format;
      GLuint texture;
      QColor bgColor;
      char* name;
      bool hidden;
      int startX,startY,endX,endY;
      QList<QPointF*> slopes;
      QWidget* parent;
      int mouseX,mouseY;
    
    };
    //End of file qiplimage.h
    
    //file qiplimage.cpp
    #include "qiplimage.h"
    #include <Globals.h>
    
    QIplImage::QIplImage(QWidget *parent) :
        QGLWidget(parent)
    {
    
    }
    QIplImage::QIplImage(QWidget *parent,char* name): QGLWidget(parent)
    {
         ui.setupUi(this);
        //This is required if you need to transmit IplImage over
        // signals and slots.(That's what I am doing in my application
        qRegisterMetaType<IplImage>("IplImage");
        resize(384,288);
        this->name=name;
        this->parent=parent;
        hidden=false;
        bgColor= QColor::fromRgb(0xe0,0xdf,0xe0);
    
        original=cvCreateImage(cvSize(this->width(),this->height()),IPL_DEPTH_8U,3);
        cvZero(original);
        switch(original->nChannels) {
            case 1:
                format = GL_LUMINANCE;
                break;
            case 2:
                format = GL_LUMINANCE_ALPHA;
                break;
            case 3:
                format = GL_BGR;
                break;
            default:
                return;
    }
        drawing=false;
        setMouseTracking(true);
        mouseX=0;mouseY=0;
        initializeGL();
    
    }
    void QIplImage::initializeGL()
    {
       qglClearColor(bgColor);  
       //glClearColor(0.5f, 0.5f, 0.5f, 1.0f);              
       glDisable(GL_DEPTH_TEST);
       glMatrixMode(GL_PROJECTION);
       glLoadIdentity();
           glOrtho(0,this->width(),this->height(),0.0f,0.0f,1.0f);
       glMatrixMode(GL_MODELVIEW);
       glLoadIdentity();
    
       glEnable(GL_TEXTURE_2D);
       glGenTextures(3,&texture);
       glBindTexture(GL_TEXTURE_2D,texture);
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
       glBindTexture(GL_TEXTURE_2D,texture);                glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,this->width(),this->height(),0,GL_BGR,GL_UNSIGNED_BYTE,NULL);
       glDisable(GL_TEXTURE_2D);
    
    
    }
    void QIplImage::setImage(IplImage image){
    original=&image;
    //cvShowImage(name,original);
    
    updateGL();
    }
    
    void QIplImage::paintGL (){
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);
    if(!hidden){
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
                glOrtho(0.0f,this->width(),this->height(),0.0f,0.0f,1.0f);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glEnable(GL_TEXTURE_2D);
                glBindTexture(GL_TEXTURE_2D,texture);
                glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,original->width,original->height,0,GL_BGR_EXT,GL_UNSIGNED_BYTE,original->imageData);
        glBegin(GL_QUADS);
                glTexCoord2i(0,1); glVertex2i(0,this->height());
        glTexCoord2i(0,0); glVertex2i(0,0);
                glTexCoord2i(1,0); glVertex2i(this->width(),0);
                glTexCoord2i(1,1); glVertex2i(this->width(),this->height());
        glEnd();
        glFlush();
        }
    
    }
    
    
    void QIplImage::resizeGL(int width,int height){
    
        glViewport(0,0,this->width(),this->height());
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();       
        glOrtho(0.0f,this->width(),this->height(),0.0f,0.0f,1.0f);
        glMatrixMode(GL_MODELVIEW);         
        glLoadIdentity();
     }
    

    希望对您有所帮助。

    【讨论】:

    • 我们在应用程序的图像上绘制了一些叠加层(不是在图像本身上,而是在屏幕上的图像上)。 OpenGL 视图对这类事情往往有点奇怪。我喜欢这种方法,但(我认为)这意味着我们需要将一些代码从 Qt 绘图例程移植到 OpenGL 绘图例程。为伟大的代码示例 +1。
    • 只是对此的更新,glDrawPixels 很慢而且有问题。我改用带纹理的四边形。性能非常好,没有任何内存泄漏或其他错误。
    • @RichardMacwan 你能分享你的“纹理四边形”版本而不是这个“glDrawPixels”版本吗?
    • 我用纹理四边形实现更新了代码。主要动作发生在 initializeGL 方法中,其中 glGenTextures 和其他函数用于设置纹理。在paintGL方法中。
    • @RichardMacwan 我正在尝试您的方式,但是当我尝试读取图像数据时遇到读取访问冲突。你可以在这里阅读我的代码:stackoverflow.com/questions/45013214/…。当你发出信号时,有什么方法可以分享你的课程吗?
    【解决方案2】:

    您可以在 QImage 和 openCV 之间共享数据 - 它们都有一个使用现有数据的 ctor - 由指针提供。

    cv::Mat(int _rows, int _cols, int _type, void* _data, size_t _step=AUTO_STEP)
    QImage ( uchar * data, int width, int height, int bytesPerLine, Format format)

    如果行最终不是 4 字节的倍数,则填充可能存在问题,但我希望填充在具有相同像素大小的两种类型上对齐 - 至少在相同的硬件上

    一个问题是,openCV 默认使用 BGR,这对于 QImage(或任何其他显示器)来说不是非常理想的。虽然我不确定 QImage::Format_ARGB32_Premultiplied 在使用加速 openGL 渲染 QImage 的 Qt 上是否一定要快得多。

    另一种方法是使用 opencv,然后将生成的数据直接复制到 openGL 纹理,然后使用 QGlWidget 来显示图像而无需另一个副本。

    【讨论】:

    • BGR 确实很烦人;我们现在通过要求 OpenCV 以 BGR 格式存储图像(相当于 Qt 的 RGB;不幸的是 Qt 没有 BGR 格式)来绕过它。这意味着我们需要小心一些 OpenCV 的例程,但应该不是问题。我还将研究 OpenGL 纹理选项。谢谢!
    • FWIW,因为我不知道它是否有效:方法 QImage::rgbSwapped 将 RGB 转换为 BGR,请参阅 qt-project.org/doc/qt-5.0/qtgui/qimage.html#rgbSwapped。我从这个项目的源代码中了解到这一点:code.google.com/p/qt-opencv-multithreaded
    • @JongBorLeem 不是 RGB/BGR,这是显示 API 倾向于使用 BGRA(包括 QImage)而图像处理 API 使用 BGR 的问题 - 所以你需要遍历整个图像并复制它填充额外的“A”字节
    • @MartinBeckett 我不明白一件事:数据指针指向特定格式的单个数据源。由于 OpenCV 和 QImage 需要两种不同的格式,即使共享数据也不必进行转换吗?例如:假设原始数据在 BGR 中。这对 OpenCV 来说很好,但是除非你将其转换为 RGB,否则 Qimage 将无法正确显示,对吧?
    • @user1218748 IIRC Qt的RGB888格式其实是BGR排序
    猜你喜欢
    • 1970-01-01
    • 2013-07-28
    • 2016-09-14
    • 2014-02-10
    • 2010-12-11
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    • 2018-04-06
    相关资源
    最近更新 更多