C++ OpenGL 纹理的 OpenCV 图像加载
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16809833/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
OpenCV image loading for OpenGL Texture
提问by glethien
I want to load an image (jpg and png) with OpenCV as OpenGL Texture.
我想用 OpenCV 作为 OpenGL 纹理加载图像(jpg 和 png)。
Here is how I load the image to OpenGL:
这是我将图像加载到 OpenGL 的方法:
glEnable(GL_TEXTURE_2D);
textureData = loadTextureData("textures/trashbin.png");
cv::Mat image = cv::imread("textures/trashbin.png");
if(image.empty()){
std::cout << "image empty" << std::endl;
}else{
glGenTextures( 1, &textureTrash );
glBindTexture( GL_TEXTURE_2D, textureTrash );
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexImage2D(GL_TEXTURE_2D,0,3,image.cols, image.rows,0,GL_RGB,GL_UNSIGNED_BYTE, image.data);
}
The image is loaded, as "image.empty" always returns false
图像已加载,因为“image.empty”始终返回 false
Here is how I render the scene using the created texture:
以下是我如何使用创建的纹理渲染场景:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureTrash);
glm_ModelViewMatrix.top() = glm::translate(glm_ModelViewMatrix.top(),0.0f,-13.0f,-10.0f);
glUniformMatrix4fv(uniformLocations["modelview"], 1, false, glm::value_ptr(glm_ModelViewMatrix.top()));
std::cout << "textureShaderID: " << glGetUniformLocation(shaderProgram,"texture") << std::endl;
glUniform1i(glGetUniformLocation(shaderProgram,"texture"), 0);
objLoader->getMeshObj("trashbin")->render();
And finally the fragmentShader where I want to apply the texture to my geometry
最后是我想将纹理应用到我的几何体的片段着色器
#version 330
in vec2 tCoord;
// texture //
// TODO: set up a texture uniform //
uniform sampler2D texture;
// this defines the fragment output //
out vec4 color;
void main() {
// TODO: get the texel value from your texture at the position of the passed texture coordinate //
color = texture2D(texture, tCoord);
}
The texture coordinates are comeing from a Vertex Buffer Object and are correctly set from the .obj file. Also I can see the Object in my scene when I set the color to e.g. red in the fragment shader, or to vec4(tCoord,0,1); then the object is shaded in different color.
纹理坐标来自顶点缓冲区对象,并从 .obj 文件正确设置。当我在片段着色器中将颜色设置为例如红色或 vec4(tCoord,0,1) 时,我也可以看到场景中的对象;然后对象以不同的颜色着色。
Unfortunately the screen stays black when I want to apply the texture... Can someone help me and tell me why is stays black?
不幸的是,当我想应用纹理时屏幕保持黑色......有人可以帮助我并告诉我为什么保持黑色吗?
回答by Christian Rau
From only looking at your texture loading code you are ignoring many considerations about how OpenCV lays out images in memory. I've already explained that for the opposite direction (glGetTexImage
into OpenCV image) in this answer, but will recapitulate it here for the CV-GL direction:
仅查看纹理加载代码,您忽略了有关 OpenCV 如何在内存中布置图像的许多考虑因素。我已经glGetTexImage
在这个答案中解释了相反的方向(进入 OpenCV 图像),但将在这里重述 CV-GL 方向:
First of all OpenCV doesn't neccessarily store image rows tightly packed but might align them to certain byte boundaries (don't know how much, at least 4, but maybe 8 or more?). If you're lucky it will use 4-byte alignment and the GL is set to the default pixel storage mode of 4-byte alignment, too. But it's best to manually fiddle with the pixel storage modes in order to be on the safe side:
首先,OpenCV 不一定要存储紧密打包的图像行,但可能会将它们与某些字节边界对齐(不知道多少,至少 4,但可能 8 或更多?)。如果幸运的话,它将使用 4 字节对齐,并且 GL 也设置为 4 字节对齐的默认像素存储模式。但为了安全起见,最好手动调整像素存储模式:
//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());
Then you have to account for the fact that OpenCV stores images from top to bottom, while the GL uses bottom to top. This could be taken care of by just mirroring the t-texture coordinate appropriately (maybe directly in the shader), but you could also just flip the image before uploading:
然后你必须考虑这样一个事实,即 OpenCV 从上到下存储图像,而 GL 使用从下到上。这可以通过适当地镜像 t-texture 坐标来解决(可能直接在着色器中),但您也可以在上传之前翻转图像:
cv::flip(image, flipped, 0);
image = flipped; //maybe just cv::flip(image, image, 0)?
Last but not least OpenCV stores color images in BGR format so uploading it as RGB would distort the colors. So use GL_BGR
(requires OpenGL 1.2, but who doesn't have that?) in glTexImage2D
.
最后但并非最不重要的一点是,OpenCV 以 BGR 格式存储彩色图像,因此将其上传为 RGB 会使颜色失真。所以GL_BGR
在glTexImage2D
.
These might not be the complete solution to your problem (since I think those errors should rather result in a distorted rather than a black image), but they are definitely problems to take care of.
这些可能不是您问题的完整解决方案(因为我认为这些错误应该导致图像失真而不是黑色),但它们绝对是需要处理的问题。
EDIT:Does your fragment shader actually compile successfully (in the complete version using the texture)? I'm asking because in the GLSL 3.30 you're using the word texture
is also the name of a builtin function (which should actually be used instead of the deprecated texture2D
function), so maybe the compiler has some name resolution problems (and maybe this error is ignored in your simplified shaders, given that the whole uniform will be optimized away and many GLSL compilers are known to be anything else than strictly standard compliant). So just try to give that sampler uniform a different name.
编辑:您的片段着色器是否真的编译成功(在使用纹理的完整版本中)?我问是因为在 GLSL 3.30 中,您使用的词texture
也是内置函数的名称(实际上应该使用它而不是已弃用的texture2D
函数),所以编译器可能有一些名称解析问题(也许这个错误在您的简化着色器中被忽略,因为整个统一将被优化掉,并且许多 GLSL 编译器被认为不是严格符合标准的其他任何东西)。因此,请尝试为该采样器制服命名不同的名称。
回答by glethien
Okay here is my working solution - based on the ideas of "Christan Rau" - Thanks for that!
好的,这是我的工作解决方案 - 基于“Christan Rau”的想法 - 谢谢!
cv::Mat image = cv::imread("textures/trashbin.png");
//cv::Mat flipped;
//cv::flip(image, flipped, 0);
//image = flipped;
if(image.empty()){
std::cout << "image empty" << std::endl;
}else{
cv::flip(image, image, 0);
glGenTextures(1, &textureTrash);
glBindTexture(GL_TEXTURE_2D, textureTrash);
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);
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_BGR, // 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
glGenerateMipmap(GL_TEXTURE_2D);
}