12

Can anyone recommend a how-to guide or provide a brief overview of what's involved with integrating OpenCV with larger GUI-based programs? What are the popular ways to do it?

Particularly, processing video with OpenCV while doing video capture/preview without using HighGUI seems especially arcane. I hope someone can demystify this.

My particular configuration is with either Juce or Qt depending on what can be done. The cross platform thing is not critical -- if there is an awesome way of doing this in Windows, I might be convinced. The availability of community support is important.

I have heard that HighGUI is entirely for testing and unsuitable for real applications. Someone recommended the VideoInput library, but it is experimental.


Key points from answers:

  • Use Qt (because Qt is great and has a big community).
  • Open a new thread to run cv::VideoCapture in a loop and emit signal after frame capture. Use Qt's msleep mechanism, not OpenCV. So, we are still using OpenCV highgui for capture.
  • Convert cv::Mat to QtImage:

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

    qtFrame = qtFrame.rgbSwapped();

  • Optional: Render with GLWidget. Convert QtImage to GLFormat with Qt built-in method:

    m_GLFrame = QGLWidget::convertToGLFormat(frame);

    this->updateGL();

Matt Montag
  • 7,105
  • 8
  • 41
  • 47
  • Great summary! I will say that it isn't necessarily required to use `VideoCapture` (e.g., you could use CMU1394, vendor specific driver, etc), but it is incredibly convenient if it supports your camera out of the box. – mevatron Oct 19 '11 at 15:54

2 Answers2

10

Here is how I am doing it with Qt. You are welcome to use whatever may be useful to you :)

/// 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();
    }
}

This class handles the rendering of the image onto a promoted QWidget. Next, I created a thread to feed the widget. (I cheated using the Qt signal-slot architecture here because it was easy...may not be the best performer in the book, but it should get you started).

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);
    }
}

Took me a bit to figure that out, so hopefully it will help you and others save some time :D

mevatron
  • 13,911
  • 4
  • 55
  • 72
  • 1
    Good answer - you can also display QImage directly rather than OpenGL just provide your own QWidget::paintevent(). That also gives you all the QImage file load/save functions in addition to OpenCV's. – Martin Beckett Oct 18 '11 at 17:08
  • Nice to have different options. Is OpenGL any faster, considering there is no scaling or image transform going on? – Matt Montag Oct 18 '11 at 17:16
  • @MartinBeckett - I didn't know that about the QWidget::paintevent(). I will have to check that out. I had also looked at drawing to a QPixmap, which works, but I read that if you have a video card (generally a safe assumption) that the OpenGL widget would be faster. Plus, OpenGL buys you some nice flexibility if you want to do overlays and that kind of stuff. – mevatron Oct 18 '11 at 17:28
  • 1
    @Matt - depends, glSubImage can be faster on machines with decent HW. glPixelBuffer can be even faster since it can transfer data asynchronously. But on machines with crappy openGL (Intel embedded chipsets) the QPainter is faster. – Martin Beckett Oct 18 '11 at 17:31
  • OK, so we are running cv::VideoCapture in another thread. I don't see any way around it. I don't think the signal emit is a big deal - it's only happening 30 times per second. – Matt Montag Oct 18 '11 at 18:28
  • 1
    @Matt - just to make that clear. Drawing a QImage (especially a ARGB_Prescaled one) to a QPainter is very fast. If you have Qt set to use the Opengl2 paint engine it probably calls glSubTex2D behind the scenes anyway. Drawing to a QPixmap used to be slow. – Martin Beckett Oct 19 '11 at 15:38
  • I don't think this would work properly if the image is to be rendered in a `QGraphicsScene`. If I'm not mistaken having a viewer with OpenGL-rendering enabled and an additional viewport where you render the `cv::Mat`'s contents would result in a mess. I would rather go with `QPainter` or by just using the pixmap of a `QLabel` (not as efficient as the other two options but very, very easy to implement). – rbaleksandar Mar 04 '16 at 11:24
2

Create an openCV image to hold the image you have captured.

Do processing on it and then copy the data into the image you want to display (eg QImage)

You can optomise things by creating the opencv cv::Mat image to share the memory with the

QImage but since QImage generally uses ARGB and most image processing tasks are better done as greyscale or RGB it's probably better to copy images and convert between them using the opencv cvtColor() function

Then simply include include the opencv headers, and link with the opencv libs - there are guides on the opencv wiki for your particular environment

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263