如何在Python的OpenGL 3.2 GtkGLArea中渲染OpenCV帧?
在Python的OpenGL 3.2 GtkGLArea中渲染OpenCV捕获图像的问题排查
我想在Python的OpenGL 3.2 GtkGLArea中渲染OpenCV捕获的图像,但网上多数示例都是10年前的旧版本(基于OpenGL 2.1或1.1),只能通过着色器实现此功能。目前能找到的可用GtkGLArea示例有限,我将一个C++示例翻译成Python后,代码无法运行也显示不出任何内容。以下是我的代码,请指出问题所在。
OpenCV捕获线程调用的显示函数
import os import gi import cv2 as cv gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GdkPixbuf, GLib USE_OPENGL = True def writeDisplay(uiBuilder, frame, imageDisplay): # 转换颜色空间为RGB frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB) if USE_OPENGL: # 通过GLib主线程回调更新帧并触发渲染 GLib.idle_add(imageDisplay.update_frame, frame)
OpenGL渲染器GTK部件
import gi import ctypes gi.require_version('Gtk', '3.0') from gi.repository import Gtk from OpenGL.GL import * from OpenGL.GL import shaders import numpy as np VERTEX_SOURCE = ''' #version 330 layout (location=0) in vec3 position; layout (location=2) in vec2 texCoord; out vec2 TexCoord; void main() { gl_Position = vec4(position, 1.0); TexCoord = vec2(texCoord.x, 1.0 - texCoord.y); }''' FRAGMENT_SOURCE =''' #version 330 in vec2 TexCoord; out vec4 color; uniform sampler2D ourTexture; void main(){ color = texture(ourTexture, TexCoord); };''' # 移除无用的颜色属性,简化顶点数据 recVertices = np.array([ # 位置 纹理坐标 0.5, 0.5, 0.0, 1.0, 1.0, # 右上 0.5, -0.5, 0.0, 1.0, 0.0, # 右下 -0.5, -0.5, 0.0, 0.0, 0.0, # 左下 -0.5, 0.5, 0.0, 0.0, 1.0 # 左上 ], dtype=np.float32) # 修正索引数组类型为无符号整数 indices = np.array([ 0, 1, 3, # 第一个三角形 1, 2, 3 # 第二个三角形 ], dtype=np.uint32) def checkGlError(op: str): error = glGetError() if error != 0: print(f"after {op}() glError (0x{error:0x})") class OpenGLRenderer(Gtk.GLArea): def __init__(self): Gtk.GLArea.__init__(self) self.connect("realize", self.onRealize) self.connect("render", self.onRender) self.ctx = None self.frame = None self.shaderProgram = None self.textureId = None self.vao = None self.vbo = None self.ebo = None def onRealize(self, area): error = area.get_error() if error is not None: print("显卡可能不支持OpenGL 3.2:", error) return self.ctx = self.get_context() self.ctx.make_current() # 初始化着色器和缓冲区,只执行一次 self.setupGraphics() self.initBuffers() # 初始化纹理 self.textureId = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.textureId) # 设置纹理参数 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glBindTexture(GL_TEXTURE_2D, 0) def setupGraphics(self): # 编译顶点着色器 vertexShader = glCreateShader(GL_VERTEX_SHADER) glShaderSource(vertexShader, VERTEX_SOURCE) glCompileShader(vertexShader) # 检查编译错误 if not glGetShaderiv(vertexShader, GL_COMPILE_STATUS): print(glGetShaderInfoLog(vertexShader)) # 编译片段着色器 pixelShader = glCreateShader(GL_FRAGMENT_SHADER) glShaderSource(pixelShader, FRAGMENT_SOURCE) glCompileShader(pixelShader) if not glGetShaderiv(pixelShader, GL_COMPILE_STATUS): print(glGetShaderInfoLog(pixelShader)) # 创建着色器程序 self.shaderProgram = glCreateProgram() glAttachShader(self.shaderProgram, vertexShader) glAttachShader(self.shaderProgram, pixelShader) glLinkProgram(self.shaderProgram) if not glGetProgramiv(self.shaderProgram, GL_LINK_STATUS): print(glGetProgramInfoLog(self.shaderProgram)) # 删除临时着色器对象 glDeleteShader(vertexShader) glDeleteShader(pixelShader) def initBuffers(self): # 初始化VAO、VBO、EBO self.vao = glGenVertexArrays(1) self.vbo = glGenBuffers(1) self.ebo = glGenBuffers(1) glBindVertexArray(self.vao) # 绑定VBO并传递顶点数据 glBindBuffer(GL_ARRAY_BUFFER, self.vbo) glBufferData(GL_ARRAY_BUFFER, recVertices.nbytes, recVertices, GL_STATIC_DRAW) # 绑定EBO并传递索引数据 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo) glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW) # 设置位置属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) # 设置纹理坐标属性 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), ctypes.c_void_p(3 * sizeof(GLfloat))) glEnableVertexAttribArray(2) glBindVertexArray(0) def update_frame(self, frame): # 更新帧数据并触发渲染 self.frame = frame self.queue_draw() def update_texture(self): if self.frame is None: return h, w, _ = self.frame.shape glBindTexture(GL_TEXTURE_2D, self.textureId) # 更新纹理数据(首次用glTexImage2D,后续用glTexSubImage2D优化) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, self.frame) glBindTexture(GL_TEXTURE_2D, 0) def onRender(self, area, ctx): if self.frame is None or self.shaderProgram is None: return True self.ctx.make_current() # 获取部件尺寸,设置视口 width = self.get_allocated_width() height = self.get_allocated_height() glViewport(0, 0, width, height) # 清空屏幕 glClearColor(0.0, 0.0, 1.0, 1.0) glClear(GL_COLOR_BUFFER_BIT) # 更新纹理 self.update_texture() # 使用着色器程序 glUseProgram(self.shaderProgram) # 绑定纹理 glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.textureId) glUniform1i(glGetUniformLocation(self.shaderProgram, "ourTexture"), 0) # 绑定VAO并绘制 glBindVertexArray(self.vao) glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None) glBindVertexArray(0) return True
关键修正说明
- 缓冲区初始化:将VAO/VBO/EBO的初始化移到
onRealize中,只执行一次,避免重复创建导致的OpenGL状态混乱。 - 纹理管理:首次创建纹理后,后续帧仅更新纹理数据,避免重复生成纹理造成内存泄漏。
- 渲染流程:拆分
update_frame(更新帧数据)和onRender(执行渲染逻辑),避免循环触发渲染回调。 - 数据类型修正:修复顶点/索引数据的字节数传递错误、索引数组类型与
glDrawElements参数不匹配问题。 - 简化着色器:移除无用的颜色属性,减少不必要的数据传递,提升渲染效率。
内容的提问来源于stack exchange,提问作者coolcatco888
相关产品推荐
相关产品推荐

