将OpenCV集成到较大的程序中

12

有人可以推荐一份OpenCV与大型基于GUI的程序集成的操作指南或提供简要概述吗?有哪些流行的方法可以做到这一点?

特别是,在执行视频捕获/预览时使用OpenCV处理视频,而不使用HighGUI似乎特别神秘。我希望有人能解密这个过程。

我的具体配置是使用Juce或Qt,具体取决于可行性。跨平台并不重要--如果在Windows中有一个很棒的方法来完成这项任务,我可能会被说服。社区支持的可用性很重要。

我听说HighGUI完全是用于测试,并且不适用于实际应用程序。有人推荐了VideoInput库,但它是实验性的。


答案中的关键点:

  • 使用Qt (因为Qt很好且拥有庞大的社区)。
  • 打开新线程,在循环中运行cv::VideoCapture并在帧捕获后emit信号。使用Qt的msleep机制,而不是OpenCV。 因此,我们仍然使用OpenCV高级界面进行捕获。
  • 将cv::Mat转换为QtImage:

    QImage qtFrame(cvFrame.data, cvFrame.size().width, cvFrame.size().height, cvFrame.step, QImage::Format_RGB888);

    qtFrame = qtFrame.rgbSwapped();

  • 可选:使用GLWidget进行渲染。使用Qt内置方法将QtImage转换为GLFormat:

    m_GLFrame = QGLWidget::convertToGLFormat(frame);

    this->updateGL();


非常好的总结!我会说,并不一定需要使用 VideoCapture(例如,您可以使用CMU1394、特定供应商驱动程序等),但如果它直接支持您的摄像头,则非常方便。 - mevatron
2个回答

10

这是我使用Qt的方式。你可以使用对你有用的任何东西 :)

/// OpenCV_GLWidget.h
#ifndef OPENCV_GLWIDGET_H_
#define OPENCV_GLWIDGET_H_

#include <qgl.h>
#include <QImage>

class OpenCV_GLWidget: public QGLWidget {
public:
    OpenCV_GLWidget(QWidget * parent = 0, const QGLWidget * shareWidget = 0, Qt::WindowFlags f = 0);
    virtual ~OpenCV_GLWidget();

    void renderImage(const QImage& frame);
protected:
    virtual void paintGL();
    virtual void resizeGL(int width, int height);

private:
    QImage m_GLFrame;
};

#endif /* OPENCV_GLWIDGET_H_ */

/// OpenCV_GLWidget.cpp
#include "OpenCV_GLWidget.h"

OpenCV_GLWidget::OpenCV_GLWidget(QWidget* parent, const QGLWidget* shareWidget, Qt::WindowFlags f) :
QGLWidget(parent, shareWidget, f)
{
    // TODO Auto-generated constructor stub

}

OpenCV_GLWidget::~OpenCV_GLWidget() {
    // TODO Auto-generated destructor stub
}

void OpenCV_GLWidget::renderImage(const QImage& frame)
{
    m_GLFrame = QGLWidget::convertToGLFormat(frame);
    this->updateGL();
}

void OpenCV_GLWidget::resizeGL(int width, int height)
{
    // Setup our viewport to be the entire size of the window
    glViewport(0, 0, width, height);

    // Change to the projection matrix and set orthogonal projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, height, 0, 0, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void OpenCV_GLWidget::paintGL() {
    glClear (GL_COLOR_BUFFER_BIT);
    glClearColor (0.0, 0.0, 0.0, 1.0);
    if (!m_GLFrame.isNull()) {
        m_GLFrame = m_GLFrame.scaled(this->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

        glEnable(GL_TEXTURE_2D);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, m_GLFrame.width(), m_GLFrame.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, m_GLFrame.bits() );
        glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex2f(0, m_GLFrame.height());
        glTexCoord2f(0, 1); glVertex2f(0, 0);
        glTexCoord2f(1, 1); glVertex2f(m_GLFrame.width(), 0);
        glTexCoord2f(1, 0); glVertex2f(m_GLFrame.width(), m_GLFrame.height());
        glEnd();
        glDisable(GL_TEXTURE_2D);

        glFlush();
    }
}

这个类处理将图像呈现到推广QWidget上的任务。接下来,我创建了一个线程来为小部件提供数据。(我在这里使用了Qt信号槽架构作弊,因为它很容易...可能不是最佳表现者,但应该能帮助您入门)。

void VideoThread::run()
{
    cv::VideoCapture video(0);

    while(!m_AbortCapture)
    {
        cv::Mat cvFrame;
        video >> cvFrame;

        cv::Mat gray(cvFrame.size(), CV_8UC1);
        cv::GaussianBlur(cvFrame, cvFrame, cv::Size(5, 5), 9.0, 3.0, cv::BORDER_REPLICATE);
        cv::cvtColor(cvFrame, gray, CV_RGB2GRAY);

        m_ThresholdLock.lock();
        double localThreshold = m_Threshold;
        m_ThresholdLock.unlock();

        if(localThreshold > 0.0)
        {
            qDebug() << "Threshold = " << localThreshold;
            cv::threshold(gray, gray, localThreshold, 255.0,  cv::THRESH_BINARY);
        }

        cv::cvtColor(gray, cvFrame, CV_GRAY2BGR);

        // convert the Mat to a QImage
        QImage qtFrame(cvFrame.data, cvFrame.size().width, cvFrame.size().height, cvFrame.step, QImage::Format_RGB888);
        qtFrame = qtFrame.rgbSwapped();

        // queue the image to the gui
        emit sendImage(qtFrame);
        msleep(20);
    }
}

花费了我一些时间才搞清楚,希望这能帮助你和其他人节省一些时间 :D


1
好的答案 - 你也可以直接显示QImage而不是OpenGL,只需提供自己的QWidget :: paintevent()即可。这还为您提供了所有QImage文件加载/保存功能,除了OpenCV的功能。 - Martin Beckett
有不同的选项总是很好的。考虑到没有缩放或图像变换,OpenGL会更快吗? - Matt Montag
1
@Matt - 取决于情况,如果机器硬件足够好,glSubImage可能更快。如果使用glPixelBuffer甚至可以更快,因为它可以异步传输数据。但是在openGL较差的机器上(例如Intel嵌入式芯片组),QPainter更快。 - Martin Beckett
1
@Matt - 只是为了明确一下。将QImage(特别是ARGB_Prescaled)绘制到QPainter上非常快。如果您的Qt设置为使用Opengl2绘图引擎,则可能在幕后调用glSubTex2D。绘制到QPixmap以前很慢。 - Martin Beckett
如果要在QGraphicsScene中呈现图像,我认为这不会正常工作。如果我没有弄错的话,启用OpenGL渲染的查看器和另一个视口,在其中呈现cv::Mat的内容将导致混乱。我宁愿使用QPainter或仅使用QLabel的像素图(效率不如其他两个选项,但非常容易实现)。 - rbaleksandar
显示剩余2条评论

2
创建一个openCV图像来保存你所捕获的图像。
对其进行处理,然后将数据复制到你想要显示的图像中(例如QImage)。
你可以通过创建opencv cv :: Mat图像来优化事情,以共享内存与QImage,但由于QImage通常使用ARGB,并且大多数图像处理任务最好作为灰度或RGB完成,因此最好复制图像并使用opencv cvtColor()函数在它们之间进行转换。
然后只需包含opencv头文件并链接opencv库 - opencv维基上有适用于您特定环境的指南。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接