Рендеринг OpenCV IplImage в QGLWidget

У меня проблемы с рендерингом сцены OpenGL.

Фон - я хочу отображать кадры с устройства видеозахвата в окне предварительного просмотра. Я использую OpenCV и Qt. И для проверки я снимаю с веб-камеры моего MacBook. Окно предварительного просмотра имеет размер 200x200, а захваченный кадр - 640x480. Я не беспокоюсь о сохранении пропорций.

Другая информация из структуры IplImage:

  • Отладка: каналы: 3
  • Отладка: глубина: 8
  • Отладка: dataOrder: 0
  • Отладка: align: 4. Выравнивание строк изображения 4 или 8
  • Отладка: происхождение: 0. 0=вверху слева, 1=внизу слева
  • Отладка: widthStep: 2560.
  • Отладка: colorModel: RGB

Так что это изображение показывает текущее положение дел.

http://clinsoftsolutions.com/fgvc4.png

Я начал с glDrawPixels, но это не сработало. Я получил вывод, но без масштабирования.

В настоящее время я пытаюсь работать с текстурами, и вот код, который я использую для взаимодействия с GL.

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

Изображение захватывается в слоте таймера, а m_image является указателем _IplImage.

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

    //Draw the scene
    glDraw();
}

Я очень надеюсь, что кто-то уже видел подобное искаженное изображение и знает, в чем проблема.


person NevilleDastur    schedule 29.03.2013    source источник


Ответы (2)


Другой подход заключается в преобразовании кадров BGR в RGBA перед их загрузкой в ​​графический процессор с помощью glTexImage2D(). Я загрузил полную демонстрацию в мой репозиторий, проверьте cvQTcameraGL.

Вот соответствующий код:

// Note: trying to retrieve more frames than the camera can give you
// will make the output video blink a lot.
cv_capture >> cv_frame;
if (cv_frame.empty())
{
    std::cout << "GLWidget::paintGL: !!! Failed to retrieve frame" << std::endl;
    return;
}
cv::cvtColor(cv_frame, cv_frame, CV_BGR2RGBA);

glEnable(GL_TEXTURE_RECTANGLE_ARB);

// Typical texture generation using data from the bitmap
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture);

// Transfer image data to the GPU
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0,
             GL_RGBA, cv_frame.cols, cv_frame.rows, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, cv_frame.data);
if (glGetError() != GL_NO_ERROR)
{
    std::cout << "GLWidget::paintGL: !!! Failed glTexImage2D" << std::endl;
}
person karlphillip    schedule 29.03.2013
comment
Спасибо. Я тоже попробую. Думаете, исполнение будет другим? т.е. делать преобразование вне GL? - person NevilleDastur; 29.03.2013
comment
Не нужно благодарить, просто проголосуйте за ответ, если он показался вам интересным. Да, мой подход может быть немного медленнее. - person karlphillip; 29.03.2013

Немного подумав и посмотрев на изображение, я понял, что это проблема выравнивания байтов/каналов. Так что еще немного гугления указало mt в направлении

glPixelStorei with GL_UNPACK_ALIGNMENT and then also GL_UNPACK_ROW_LENGTH

Окончательный код, который теперь работает:

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

Это нужно добавить над вызовом glTexImage2D.

Это странный формат для данных изображения. Но это результат, который вы получаете от встроенной веб-камеры Apple iSight. Надеюсь, это поможет кому-то еще.

person NevilleDastur    schedule 29.03.2013