在 QGLWidget 中绘制一个 cv::Mat?

Posted

技术标签:

【中文标题】在 QGLWidget 中绘制一个 cv::Mat?【英文标题】:Draw a cv::Mat inside a QGLWidget? 【发布时间】:2013-04-15 14:46:11 【问题描述】:

我想知道如何将 cv::Mat 绘制到 QGLWidget 中。我找到了here 一种方法并尝试实现它。但是我的小部件是黑色的。

这是我的小部件的代码:

h 文件

#ifndef GLVIEWER_H
#define GLVIEWER_H
#include <QtOpenGL/QGLWidget>
#include <QtOpenGL/QtOpenGL>
#include <opencv2/core/core.hpp>

class GLViewer : public QGLWidget 
    Q_OBJECT
public:
    GLViewer(QWidget *parent = NULL);
    void setNewFrame(cv::Mat newframe);
protected:
    void initializeGL();
    void resizeGL(int w, int h);
    void paintGL();
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void keyPressEvent(QKeyEvent *event);
    cv::Mat frame;
;
#endif // GLVIEWER_H

cpp 文件

#include "glviewer.h"
#include <iostream>
#include <QtGui/QMouseEvent>

GLViewer::GLViewer(QWidget *parent) : QGLWidget(parent) 
    setMouseTracking(true);
    frame = cv::Mat::ones(25, 50, CV_8UC3) * 255;


void GLViewer::setNewFrame(cv::Mat newframe) 
    frame = newframe;
    paintGL();


void GLViewer::initializeGL() 
    std::cout << "initializeGL" << std::endl;
    glViewport(0,0,this->width(), this->height());
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-this->width()/2, this->width()/2, this->height()/2, -this->height()/2, -1, 1);


void GLViewer::resizeGL(int w, int h) 
glViewport(0, 0, w, h);
//    glMatrixMode(GL_PROJECTION);
//    glLoadIdentity();
//    gluOrtho2D(0, w, 0, h); // set origin to bottom left corner
//    glMatrixMode(GL_MODELVIEW);
//    glLoadIdentity();


void GLViewer::paintGL() 

    //std::cout << "paint" << std::endl;

    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(1.0f, 0, 0, 1.0f);

    glDrawPixels(frame.rows, frame.cols,
                 GL_RGB,
                 GL_UNSIGNED_BYTE,
                 frame.data);


    

由于我已经使用 'cv::Mat::ones(25, 50, CV_8UC3) * 255;' 初始化了 cv::Mat,我希望看到一个蓝色的小矩形,但是.. 什么也没发生,全黑的小部件.

谁能给我指出一个解决方案?

编辑

我试图将背景更改为红色,现在一切都是红色的。这是输出:

背景是正确的,但蓝色矩形..不是蓝色的,即使它看起来是一个矩形.. 发生了什么?我试图反转 frame.cols 和 frame.cols,以及 GL_BGR 而不是 GL_RGB 以及一些数据类型的随机试验(GL_UNSIGNED_SHORT 等),但没有什么比这更好的结果了..

【问题讨论】:

I've talked about this stuff here. 【参考方案1】:

glDrawPixels 不是一个明智的选择。 OpenGL 中的 2D 像素绘制速度非常慢。您真正想要做的是将矩阵加载到纹理中,然后在其上绘制带有纹理的图元。

但是对于您的简单用例:

我建议你使用 QGLWidget,但仍然使用 QPainter 来绘制东西。它将完全加速,但您不必处理所有这些转换等!

代码相当简单。看一下: https://sourceforge.net/p/gerbil/svn/HEAD/tree/trunk/gerbil-gui/scaledview.cpp (自动缩放内容的小部件示例)

要从 Mat 转换为 QImage,请参阅: https://sourceforge.net/p/gerbil/svn/HEAD/tree/trunk/common/qtopencv.cpp

转换 QImage QPixmap 可以通过从另一个构造一个来隐式地完成。请注意,它本质上只会将图像数据作为纹理存储在显卡上。

cv::Mat 到 QImage 的转换是“冗余”数据副本。不过真的可以忽略不计,试试吧!

【讨论】:

我更喜欢在纯opengl中保留绘图的东西。无论如何,我知道纹理更好但我认为它有点复杂..你能指出一些例子吗? 显然您尝试直接使用 Mat 的内存失败了。可能每行的跳过是罪魁祸首。或者……像那样。 OpenCV 文档中描述了如何在内部设计矩阵。好吧,在不知道自己在做什么的情况下访问 Mat.data 并不是一个好主意。即使你这样做,它也会变得复杂。顺便提一句。很抱歉,我手头没有 GL 示例。【参考方案2】:

您的resizeGL() 正在清除您在initializeGL() 中进行的矩阵设置,尤其是glOrtho() 调用。

尝试将矩阵设置移动到paintGL()

...

void GLViewer::initializeGL() 

    std::cout << "initializeGL" << std::endl;
    glViewport(0,0,width(), height());


void GLViewer::resizeGL(int w, int h) 

    glViewport(0, 0, w, h);


void GLViewer::paintGL() 
    std::cout << "paint" << std::endl;    
    glClear(GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-width()/2.0, width()/2.0, height()/2.0, -height()/2.0, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glRasterPos2i(width()/2.0,-height()/2.0);
    // also try glRasterPos2i(0,0)
    glPixelZoom(-1.0f,-1.0f);
    glDrawPixels
        (
        frame.rows, frame.cols,
        GL_RGB,
        GL_UNSIGNED_BYTE,
        frame.data
        );


...

编辑:尝试在glDrawPixels() 调用之前发出glPixelStore( GL_UNPACK_ALIGNMENT, 1 )。或切换到GL_RGBA 和四分量cv::Mat 格式。

【讨论】:

以上是关于在 QGLWidget 中绘制一个 cv::Mat?的主要内容,如果未能解决你的问题,请参考以下文章

QDockWidget - QGlWidget - 当小部件从停靠栏中拖出时,可绘制无效

C++ 中 cv::Rectangle 的问题

OpenCV-绘制圆角矩形

使用Opencv和C++在垂直方向的两点之间绘制直线

将 Cv::Mat 的数据存储在 std::vector 中

如何在向量中找到相同的 cv::Mat<cv::Mat>