You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:19:55