Search code examples
qtopenglopencvqglwidget

Rendering a OpenCV IplImage to a QGLWidget


I am having trouble with rendering a OpenGL scene.

The background is I want to display the frames from a video capture device in a preview window. I am using OpenCV and Qt. And to test I am capturing from my MacBook webcam. The preview window is 200x200 and the frame captured is 640x480. I am not worried about maintaining aspect ratios.

Other info from the IplImage struct:

  • Debug: channels: 3
  • Debug: depth: 8
  • Debug: dataOrder: 0
  • Debug: align: 4. Alignment of image rows 4 or 8
  • Debug: origin: 0. 0=top left, 1=bottom left
  • Debug: widthStep: 2560.
  • Debug: colorModel: RGB

So this image shows the current state of affairs.

Current capture http://clinsoftsolutions.com/fgvc4.png

I started with using glDrawPixels, but this didn't work well. I got output, but no scaling.

Currently I am trying with textures and here is the code I am using for the GL interactions

void VideoCaptureWidget::initializeGL()
{
    qDebug("initializeGL called");
    qglClearColor(QColor::fromRgb(0,0,0));      // set clear colur to black
    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, &m_texture);
    glBindTexture(GL_TEXTURE_2D, m_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,m_texture);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,this->width(),this->height(),0,GL_BGR,GL_UNSIGNED_BYTE,NULL);
    glDisable(GL_TEXTURE_2D);
}


void VideoCaptureWidget::paintGL()
{    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);

    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,m_texture);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,m_image->width,m_image->height,0,GL_BGR_EXT,GL_UNSIGNED_BYTE,m_image->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 VideoCaptureWidget::resizeGL(int width,int height)
{
    qDebug("reszieGL called");

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

The image is captured in a timer slot and m_image is a _IplImage pointer

void VideoCaptureWidget::_captureFrame() {
    m_image = cvQueryFrame(m_capture);
    if(!m_image) {
        qDebug("VideoCaptureWidget::_captureFrame(): Error capturing a frame...");
    }

    //Draw the scene
    glDraw();
}

I am really hoping someone will have seen this sort of distorted image before and know what the problem is.


Solution

  • A bit more thinking and looking at the image I figured it must be a byte / channel alignment problem. So a bit more googling pointed mt in the direction of

    glPixelStorei with GL_UNPACK_ALIGNMENT and then also GL_UNPACK_ROW_LENGTH
    

    The final code that now works is:

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4 );
    glPixelStorei(GL_UNPACK_ROW_LENGTH, m_image->widthStep/m_image->nChannels);
    

    This needs to be added above the glTexImage2D call.

    It's a weird format for the image data. But that is the output you get from the Apple iSight built in webcam. Hope this helps someone else.