【问题标题】:Converting data from glReadPixels() to OpenCV::Mat将数据从 glReadPixels() 转换为 OpenCV::Mat
【发布时间】:2012-02-24 05:56:03
【问题描述】:

我想使用 glReadPixels() 从动画中获取每个 OpenGL 帧,并将数据转换为 OpenCV::Mat。我知道 glReadPixels() 从下一行到上一行,从左到右逐行获取数据。另一方面,OpenCV 以不同的方式存储数据。

是否有人知道任何库或任何教程/示例可以帮助我将数据从 glReadPixels 转换为 C++ 中的 OpenCV:Mat

摘要

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

Data from left to right,                    Data from left to right,
bottom to top.                              top to bottom.

【问题讨论】:

    标签: c++ opengl opencv glreadpixels


    【解决方案1】:

    首先,我们创建一个空的(或未初始化的)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_REDGL_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 顺序存储彩色图像
    • 可能无法存储紧密排列的图像行

    【讨论】:

    • 非常非常好的答案。我已经在进行翻转,但我没有注意 glPixelStorei。谢谢
    • 我不能调用GL_BGR,我不知道为什么链接器找不到它,因为我有最新版本
    • @Jav_Rock 它是在 OpenGL 1.2 中引入的。因此,如果您的硬件和驱动程序支持它(这很有可能,除非您在 20 年前购买了显卡),您只需要定义这个符号,包括一个相当新的 glext.h 或通过在第一名(如 GLEW),它应该带有自己的带有常量定义的标题。无论如何,一旦您想使用高于 1.1 的任何功能,这将是必要的(例如 PBO,如果使用得当,它可能会加快您的阅读性能)。
    • 是的, glext.h 确实解决了这个问题。现在我让一切都使用正确的颜色。非常感谢。
    • 这条评论仍然有用,如果可以的话,我会投票 20 次。
    【解决方案2】:
    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;
    

    【讨论】:

      猜你喜欢
      • 2018-09-04
      • 2016-09-04
      • 1970-01-01
      • 2018-09-04
      • 2015-12-13
      • 2017-02-19
      • 2018-11-28
      • 2015-02-16
      相关资源
      最近更新 更多