OpenGL作为纹理渲染到OpenCV垫并在OpenCV中重用

时间:2018-11-30 13:29:00

标签: c++ opencv opengl

我有一个要用OpenGL渲染的OpenCV垫。因此,我尝试通过在网上找到的代码将openCV垫加载为纹理并将其渲染为该纹理:

void BindCVMat2GLTexture(cv::Mat& image, GLuint& imageTexture)
{
    if (image.empty()) {
        std::cout << "image empty" << std::endl;
    }
    else {
        glEnable(GL_TEXTURE_2D);
        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
        glGenTextures(1, &imageTexture);
        glBindTexture(GL_TEXTURE_2D, imageTexture);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        // Set texture clamping method
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

        cv::cvtColor(image, image, CV_RGB2BGR);

        glTexImage2D(GL_TEXTURE_2D,         // Type of texture
            0,                   // Pyramid level (for mip-mapping) - 0 is the top level
            GL_RGB,              // Internal colour format to convert to
            image.cols,          // Image width  i.e. 640 for Kinect in standard mode
            image.rows,          // Image height i.e. 480 for Kinect in standard mode
            0,                   // Border width in pixels (can either be 1 or 0)
            GL_RGB,              // Input image format (i.e. GL_RGB, GL_RGBA, GL_BGR etc.)
            GL_UNSIGNED_BYTE,    // Image data type
            image.ptr());        // The actual image data itself
            //NULL);
    }
}

然后我创建一个FBO:

//init the texture
    GLuint imageTexture;
    BindCVMat2GLTexture(target, imageTexture);

    //init the frame buffer
    GLuint fbo = 0;
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    // Set "renderedTexture" as our colour attachement #0
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, imageTexture, 0);

然后我打电话:

glViewport(0, 0, target.cols, target.rows);
MyDraw();

调用此命令后,我使用了另一个功能,可以将纹理数据复制回openCV垫:

cv::Mat GetOcvImgFromOglImg(GLuint ogl_texture_id)
{
    glBindTexture(GL_TEXTURE_2D, ogl_texture_id);
    GLenum gl_texture_width, gl_texture_height;

    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, (GLint*)&gl_texture_width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, (GLint*)&gl_texture_height);

    unsigned char* gl_texture_bytes = (unsigned char*)malloc(sizeof(unsigned char)*gl_texture_width*gl_texture_height * 3);
    glGetTexImage(GL_TEXTURE_2D, 0 /* mipmap level */, GL_BGR, GL_UNSIGNED_BYTE, gl_texture_bytes);

    return cv::Mat(gl_texture_height, gl_texture_width, CV_8UC3, gl_texture_bytes);
}

并通过以下方式致电:

target = GetOcvImgFromOglImg(imageTexture);

我还创建了三角形顶点和一个顶点着色器,该着色器仅使它们保持不变,而一个片段着色器仅将红色显示为红色。从理论上讲,我只是希望在此纹理上绘制一个红色三角形作为第一步。但是,由于我根本没有渲染图像,因此无法正常工作。我是openGL的初学者,现在我自己找不到任何解决方案。有什么想法我做错了吗?

2 个答案:

答案 0 :(得分:1)

OpenCV将图像存储为紧密打包的行数据,但可能会将它们对齐到某些字节边界(可能是4或8),但我们不知道如果您很幸运,它将使用4字节对齐,这也是OpenGL的默认设置。但是最好是手动设置像素存储模式,以确保安全:

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

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

然后,您必须考虑到OpenCV从上到下存储图像,而GL从下到上存储图像这一事实。

cv::flip(image, flipped, 0); image = flipped;

OpenCV以BGR格式存储彩色图像,因此将其上传为RGB会使颜色失真。因此,请在GL_BGR中使用glTexImage2D

答案 1 :(得分:1)

所以我解决了我的问题,应该添加更多我在本教程中完全忽略的VertexBufferObject,以发布更轻松的答案。同样,这非常重要,我同时使用SFML lib来显示我的框架。 SFML还内部使用OpenGL。因此,我需要保存和恢复相互干扰的OpenGL状态。

如果有人遇到类似问题,我会删除此链接: using-opengl-together-with-the-graphics-module

在我使用SFML以及缺少的VAO代码和Draw()方法使其工作后重置OpenGL绑定之前,此添加的状态保存/恢复。这可能不是性能最高的解决方案,但就目前而言,这是一种实现我所需要的方法。