如何将OpenCV的mat图像转换为OpenGL中的纹理并显示

您所在的位置:网站首页 opengl绘制纹理 如何将OpenCV的mat图像转换为OpenGL中的纹理并显示

如何将OpenCV的mat图像转换为OpenGL中的纹理并显示

2024-01-19 05:22| 来源: 网络整理| 查看: 265

如果你还不知道什么是纹理,可以先看一下LearnOpenGL-CN中的纹理章节。

简单的思路就是用OpenGL画一个矩形,然后将一张图片作为纹理贴到这个正方形上。为了能够将纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分,这样的话每个顶点都会关联一个纹理坐标。

首先,需要定义好矩形(两个三角形)的顶点和纹理坐标的对应关系:

在OpenGL中,所有的坐标都是3D坐标(x, y, z),OpenGL会把所有的3D坐标转换为所谓的标准化设备坐标,坐标在范围为[-1.0, 1.0],只有落在这个范围内的坐标最终才会呈现在屏幕上。 标准化设备坐标的坐标系: 原点在屏幕中间,y轴正方向向上,x轴正方向向右,z轴正方向垂直屏幕指向屏幕前的你。 所以,如果我们要顶一个矩形的4个顶点,使它撑满视窗的话,坐标定义如下: 在这里插入图片描述 然后定义每个顶点对应的纹理坐标: 我们使用的是2D纹理图像,纹理坐标在x轴和y轴,范围在[0,1],纹理坐标起始于(0, 0),也就是纹理图像的左下角。坐标定义如下所示: 在这里插入图片描述 于是,矩形的坐标和纹理的坐标的对应关系就找到了。定义为数组的形式如下:

GLfloat vertices[] = { // Positions // Texture Coords 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Top Right 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // Bottom Right -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // Bottom Left -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // Top Left };

然后要解决的是如何将cvMat转换为OpenGL中的纹理: 首先,定义一个函数用来创建一个纹理,返回该纹理对应的ID:

static GLuint createTexture() { GLuint textureID; // 创建一个纹理对象,并返回一个独一无二的标识保存在textureID中 glGenTextures(1, &textureID); // 绑定textureID标识的纹理,之后的所有操作都是相对于该纹理的 glBindTexture(GL_TEXTURE_2D, textureID); // 设置纹理环绕方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); // 设置纹理过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 解绑 glBindTexture(GL_TEXTURE_2D, 0); return textureID; }

然后用下面的函数设置纹理的内容:

static void cvmatToTexture(GLuint& textureId, const cv::Mat& mat) { // 绑定textureID标识的纹理,之后的所有操作都是相对于该纹理的 glBindTexture(GL_TEXTURE_2D, textureId); // 注意OpenCV中图像通道的顺序是BGR glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mat.cols, mat.rows, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, mat.data); glGenerateMipmap(GL_TEXTURE_2D); // 解绑 glBindTexture(GL_TEXTURE_2D, 0); }

剩下的就是常规的渲染正方形的操作了。参考LearnOpenGL-CN的代码即可。

下面基于上面的内容,编写一个小的工程,用OpenCV从USB摄像头获取图像,然后用OpenGL显示。 获取摄像头图像线程函数定义如下:

void captureImage(int deviceId) { cv::VideoCapture cap(deviceId); if (!cap.isOpened()) { printf("Open camera failed!!!\n"); exit(-1); } while (true) { std::this_thread::sleep_for(std::chrono::milliseconds(5)); if (prepared) continue; cv::Mat frame; cap >> frame; if (!frame.data) continue; imageBuf[bufId] = frame; prepared = true; } }

主线程定义如下:

int main() { std::thread t(captureImage, 0); t.detach(); // 初始化GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 创建一个OpenGL上下文 GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Camera", nullptr, nullptr); glfwMakeContextCurrent(window); glfwSetKeyCallback(window, key_callback); // 初始化glew glewExperimental = GL_TRUE; glewInit(); glViewport(0, 0, WIDTH, HEIGHT); // 创建并编译shader程序 Shader ourShader("./vertex_shader.vs", "./fragment_shader.frag"); // 定义顶点属性 GLfloat vertices[] = { // Positions // Texture Coords 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // Top Right 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // Bottom Right -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // Bottom Left -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // Top Left }; // 一个正方形是由两个三角形组成的 GLuint indices[] = { 0, 1, 3, // First Triangle 1, 2, 3 // Second Triangle }; GLuint VBO, VAO, EBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); // 绑定VAO // 给VBO指定缓冲目标,并设置缓冲数据 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 给EBO指定缓冲目标,并设置缓冲数据 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 指定顶点数据格式 glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * sizeof(GL_FLOAT), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * sizeof(GL_FLOAT), (void*)(3 * sizeof(GL_FLOAT))); glEnableVertexAttribArray(1); glBindVertexArray(0); // 解绑 // 创建一个纹理 GLuint texture = createTexture(); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); // 清空颜色缓冲区 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); if (prepared) { cvmatToTexture(texture, imageBuf[bufId]); bufId = (bufId + 1) % 2; prepared = false; // 激活纹理 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glUniform1i(glGetUniformLocation(ourShader.program, "ourTexture1"), 0); ourShader.Use(); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); glfwSwapBuffers(window); } std::this_thread::sleep_for(std::chrono::milliseconds(5)); } glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glfwTerminate(); return 0; }

运行结果: 在这里插入图片描述 完整工程代码可以参考OpenGL_show_camera。

参考文献: LearnOpenGL-CN入门章节 如何将OpenCV中的Mat类绑定为OpenGL中的纹理



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3