Преобразование данных из glReadPixels () в OpenCV :: Mat

18

Я хочу получить каждый кадр OpenGL из анимации с помощью glReadPixels () и преобразовать данные в OpenCV :: Mat . Я знаю, что glReadPixels () получает данные по строкам от нижнего до верхнего, слева направо. С другой стороны, OpenCV сохраняет данные по-разному.

Кто-нибудь знает какую-либо библиотеку или какой-либо учебник / пример, который помогает мне преобразовывать данные из glReadPixels в OpenCV: Mat в C ++?

СУЩНОСТЬ

 OpenGL frame      ----------------------->        CV::Mat

Data from left to right,                    Data from left to right,
bottom to top.                              top to bottom.
    
задан Jav_Rock 01.02.2012 в 15:44
источник

2 ответа

43

Сначала мы создаем пустой (или unititialized) cv::Mat для того, чтобы наши данные считывались напрямую. Это можно сделать один раз при запуске, но, с другой стороны, cv::Mat::create действительно не стоит дорого, когда изображение уже имеет соответствующий размер и тип. Тип зависит от ваших потребностей, обычно это что-то вроде CV_8UC3 для 24-битного цветного изображения.

cv::Mat img(height, width, CV_8UC3);

или

img.create(height, width, CV_8UC3);

Затем вам нужно учитывать cv::Mat , не обязательно сохраняя строки изображений смежно. В конце каждой строки может быть небольшое значение заполнения, чтобы выровнять строки по 4 байта (или 8?). Поэтому вам нужно возиться с режимами хранения пикселей:

//use fast 4-byte alignment (default anyway) if possible
glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);

//set length of one complete row in destination data (doesn't need to equal img.cols)
glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize());

Далее тип матрицы влияет на параметры формата и типа glReadPixels . Если вам нужны цветные изображения, вы должны иметь в виду, что OpenCV обычно сохраняет значения цвета в порядке BGR, поэтому вам нужно использовать GL_BGR(A) (которые были добавлены с OpenGL 1.2) вместо GL_RGB(A) . Для изображений одного компонента используйте либо GL_LUMINANCE (который суммирует отдельные цветовые компоненты), либо GL_RED , GL_GREEN , ... (для получения отдельного компонента). Таким образом, для нашего изображения CV_8UC3 окончательный вызов, чтобы прочитать его непосредственно в cv::Mat , будет следующим:

glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);

Наконец, OpenCV сохраняет изображения сверху вниз. Таким образом, вам может потребоваться либо перевернуть их после их получения, либо сделать их первыми в OpenGL (это можно сделать, отрегулировав матрицу проекции, но в этом случае следите за ориентацией треугольника). Чтобы перевернуть cv::Mat по вертикали, вы можете использовать cv::flip :

cv::flip(img, flipped, 0);

Поэтому, чтобы иметь в виду OpenCV:

  • хранит изображения сверху донизу, слева направо
  • хранит цветные изображения в порядке BGR
  • может не хранить строки изображений плотно упакованные
ответ дан Christian Rau 01.02.2012 в 16:52
источник
  • Действительно действительно хороший ответ. Я уже работал над переворачиванием, но я не обращал внимания на glPixelStorei. благодаря –  Jav_Rock 01.02.2012 в 17:34
  • Я не могу назвать GL_BGR, я не знаю, почему компоновщик не может найти его, потому что у меня есть последняя версия –  Jav_Rock 01.02.2012 в 17:37
  • @Jav_Rock Он был представлен с OpenGL 1.2. Поэтому, если ваше оборудование и драйвер поддерживают его (что очень вероятно, если вы не приобрели свою графическую карту 20 лет назад), вам просто нужно определить этот символ, включив в него достаточно новый glext.h или используя библиотеку управления расширением в первое место (например, GLEW), которое должно содержать собственный заголовок с постоянными определениями. Это было бы необходимо, если вы захотите использовать любую функциональность выше 1,1, так или иначе (например, PBO, что может ускорить чтение производительности, используемой должным образом). –  Christian Rau 01.02.2012 в 17:39
  • Да, определенно glext.h исправляет проблему. Теперь у меня все работает с правильными цветами. Большое спасибо. –  Jav_Rock 01.02.2012 в 17:53
  • Этот комментарий по-прежнему полезен, если бы я мог увеличить его 20 раз. –  Jav_Rock 02.02.2012 в 10:32
Показать остальные комментарии
1
unsigned char* getPixelData( int x1, int y1, int x2, int y2 )
{
    int y_low, y_hi;
    int x_low, x_hi;

    if ( y1 < y2 )
    {
        y_low = y1;
        y_hi  = y2;
    }
    else
    {
        y_low = y2;
        y_hi  = y1;
    }

    if ( x1 < x2 )
    {
        x_low = x1;
        x_hi  = x2;
    }
    else
    {
        x_low = x2;
        x_hi  = x1;
    }

    while ( glGetError() != GL_NO_ERROR )
    {
        ;
    }

    glReadBuffer( GL_BACK_LEFT );

    glDisable( GL_TEXTURE_2D );

    glPixelStorei( GL_PACK_ALIGNMENT, 1 );

    unsigned char *data = new unsigned char[ ( x_hi - x_low + 1 ) * ( y_hi - y_low + 1 ) * 3 ];

    glReadPixels( x_low, y_low, x_hi-x_low+1, y_hi-y_low+1, GL_RGB, GL_UNSIGNED_BYTE, data );

    if ( glGetError() != GL_NO_ERROR )
    {
        delete[] data;
        return 0;
    }
    else
    {
        return data;
    }
}

использование:

CvSize size = cvSize( 320, 240 );

unsigned char *pixel_buf = getPixelData( 0, 0, size.width - 1, size.height - 1 );

if ( pixel_buf == 0 )
    return 0;

IplImage *result = cvCreateImage( size, IPL_DEPTH_8U, 3 );
memcpy( result->imageData, pixel_buf, size.width * size.height * 3 );
delete[] pixel_buf;
    
ответ дан Rin 28.03.2013 в 13:25
источник