如何在 Python 的 OpenGL 3.2 GtkGLArea 中渲染 OpenCV 帧?

问题描述 投票:0回答:1

如何在 Python 的 OpenGL 3.2 GtkGLArea 中渲染由 OpenCV 捕获的图像?许多在线示例已有 10 年历史,已过时,并且使用的是 OpenGL 2.1 或 1.1 示例。

我能做到这一点的唯一方法是通过着色器。在 GtkGLArea 中绘制任何东西的唯一工作示例是这个:
在 Python GTK3 中使用 Gtk GLArea

我在 C++ 中找到了这个示例并将其翻译成 Python,但我无法让它工作或输出任何东西。
如何使用 OpenGL 显示图像

这是我的代码,它不显示任何内容。我做错了什么?

编写OpenCV捕获线程调用的显示函数:

import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GdkPixbuf, GLib

USE_OPENGL = True

def writeDisplay(uiBuilder, frame, imageDisplay):
    # Write Frame
    frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)

    if USE_OPENGL:
        # Render frame using OpenGL
        GLib.idle_add(imageDisplay.render, frame)

OpenGL 渲染器 GTK 小部件

import gi
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=1) in vec3 color;
layout (location=2) in vec2 texCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(position,1.0);
ourColor = color;
TexCoord= vec2(texCoord.x,1.0-texCoord.y);
}'''

FRAGMENT_SOURCE ='''
#version 330
in vec3 ourColor;
in vec2 TexCoord;
out vec4 color;
uniform sampler2D ourTexture;
void main(){
color = texture(ourTexture , TexCoord);
};'''

recVertices = np.array([
    # Positions           Colors           Texture Coords
    0.5,  0.5, 0.0,   1.0, 0.0, 0.0,    1.0, 1.0,   # Top Right
    0.5, -0.5, 0.0,   0.0, 1.0, 0.0,    1.0, 0.0,   # Bottom Right
    -0.5, -0.5, 0.0,   0.0, 0.0, 1.0,   0.0, 0.0,   # Bottom Left
    -0.5,  0.5, 0.0,   1.0, 1.0, 0.0,   0.0, 1.0    # Top Left
], dtype=np.float32)

indices = np.array([
    0, 1, 3, # First Triangle
    1, 2, 3  # Second Triangle
])

def checkGlError(op: str):
    error = glGetError()
    if error is not None and error != 0:
        print("after %s() glError (0x%x)", op, error)

# Based on examples:
# https://stackoverflow.com/questions/42153819/how-to-load-and-display-an-image-in-opengl-es-3-0-using-c
# https://stackoverflow.com/questions/47565884/use-of-the-gtk-glarea-in-pygobject-gtk3
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.area = None
        self.shaderProgram = None
        self.positionHandle = None
        self.textureId = None
        self.vao = None

    def onRealize(self, area):

        error = area.get_error()
        if error != None:
            print("your graphics card is probably too old : ", error)
        else:
            print(area, "realize... fine so far")

        self.ctx = self.get_context()
        self.ctx.make_current()

        print("OpenGL realized", self.ctx)

    def onRender(self, area, ctx):
        
        self.render(self.frame)
        return True

    def setupGraphics(self, width, height):

        if self.shaderProgram is None:
            # Load Shaders, Create program, Setup Graphics
            vertexShader = glCreateShader(GL_VERTEX_SHADER)
            glShaderSource(vertexShader, VERTEX_SOURCE)
            glCompileShader(vertexShader)

            pixelShader = glCreateShader(GL_FRAGMENT_SHADER)
            glShaderSource(pixelShader, FRAGMENT_SOURCE)
            glCompileShader(pixelShader)

            self.shaderProgram = glCreateProgram()
            glAttachShader(self.shaderProgram, vertexShader)
            glAttachShader(self.shaderProgram, pixelShader)
            glLinkProgram(self.shaderProgram)
            self.positionHandle = glGetAttribLocation(self.shaderProgram, "position")

        glViewport(0, 0, width, height)
    
    def initBuffers(self):
        # Initialize an buffer to store all the verticles and transfer them to the GPU
        self.vao = glGenVertexArrays(1) # Generate VAO
        vbos = glGenBuffers(1) # Generate VBO
        ebo = glGenBuffers(1) # Generate EPBO
        glBindVertexArray(self.vao) # Bind the Vertex Array

        glBindBuffer(GL_ARRAY_BUFFER, vbos) # Bind verticles array for OpenGL to use
        glBufferData(GL_ARRAY_BUFFER, len(recVertices), recVertices, GL_STATIC_DRAW)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) # Bind the indices for information about drawing sequence
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, len(indices), indices, GL_STATIC_DRAW)
        
        # 1. set the vertex attributes pointers
        # Position Attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)
        # Color Attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(3 * sizeof(GLfloat)))
        glEnableVertexAttribArray(1)
        # Texture Coordinate Attribute
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(6 * sizeof(GLfloat)))
        glEnableVertexAttribArray(2)

        glBindVertexArray(0) # 3. Unbind VAO
    
    def generateTexture(self, frame):
        # Update Frame
        self.frame = frame

        # If we have a frame to display
        if frame is not None:
            # extract array from Image
            h, w, d = frame.shape

            # Generate Texture
            self.textureId = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, self.textureId) # Bind our 2D texture so that following set up will be applied

            # Set texture wrapping parameter
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT)

            # Set texture Filtering parameter
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)

            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, frame)
            glGenerateMipmap(GL_TEXTURE_2D)
            glBindTexture(GL_TEXTURE_2D, 0) # Unbind 2D textures

    def render(self, frame):

        # Set OpenGL Render Context
        if self.ctx is not None and frame is not None:
            self.ctx.make_current()

            # extract array from Image
            h, w, d = frame.shape

            # Initialize Graphics
            self.setupGraphics(w, h)

            # Generate Texture
            self.generateTexture(frame)

            # Clear Screen
            glClearColor(0, 0, 1, 1)
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

            # Render Frame
            glUseProgram(self.shaderProgram)
            # checkGlError("glUseProgram")

            # glVertexAttribPointer(self.positionHandle, 2, GL_FLOAT, GL_FALSE, 0, recVertices)
            # checkGlError("glVertexAttribPointer")
            # glEnableVertexAttribArray(self.positionHandle)
            # checkGlError("glEnableVertexAttribArray")
            # glDrawArrays(GL_TRIANGLE_FAN, 0, 4)
            # checkGlError("glDrawArrays")
            glActiveTexture(GL_TEXTURE0)
            checkGlError("glActiveTexture")
            glBindTexture(GL_TEXTURE_2D, self.textureId)
            checkGlError("glBindTexture")
            mlocation = glGetUniformLocation(self.shaderProgram, "ourTexture")
            checkGlError("glGetUniformLocation")
            glUniform1i(mlocation, 0)
            checkGlError("glUniform1i")
            self.initBuffers()
            glBindVertexArray(self.vao)
            checkGlError("glBindVertexArray")
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
            
            # Queue Draw
            glFlush()
            self.queue_draw()
python opencv pygtk pyopengl pygobject
1个回答
0
投票

这是答案,这是用 OpenCV 帧测试的,它是一个 3 维数组 [width][height][RGB Color],需要展平成一个线性数组。 OpenGL 将获取图像的宽度和高度并正确地遍历它。本指南对让它发挥作用非常有帮助:https://open.gl/drawing

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from OpenGL.GL import *
import numpy as np

VERTEX_SOURCE = '''
#version 330
layout (location=0) in vec3 position;
layout (location=1) in vec3 color;
layout (location=2) in vec2 texCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(position,1.0);
ourColor = color;
TexCoord= vec2(texCoord.x,1.0-texCoord.y);
}'''

FRAGMENT_SOURCE ='''
#version 330
in vec3 ourColor;
in vec2 TexCoord;
out vec4 color;
uniform sampler2D ourTexture;
void main(){
color = texture(ourTexture, TexCoord);
};'''

recVertices = np.array([
    # Positions           Colors           Texture Coords
    1.0,  1.0, 0.0,   1.0, 0.0, 0.0,    1.0, 1.0,   # Top Right    0
    1.0, -1.0, 0.0,   0.0, 1.0, 0.0,    1.0, 0.0,   # Bottom Right 1
    -1.0, -1.0, 0.0,   0.0, 0.0, 1.0,   0.0, 0.0,   # Bottom Left  2
    -1.0,  1.0, 0.0,   1.0, 1.0, 0.0,   0.0, 1.0,   # Top Left     3
    1.0,  1.0, 0.0,   1.0, 0.0, 0.0,    1.0, 1.0,   # Top Right    4
], dtype=np.float32)

def checkGlError(op: str):
    error = glGetError()
    if error is not None and error != 0:
        print("after %s() glError (0x%x)", op, error)

# Based on examples:
# https://stackoverflow.com/questions/42153819/how-to-load-and-display-an-image-in-opengl-es-3-0-using-c
# https://stackoverflow.com/questions/47565884/use-of-the-gtk-glarea-in-pygobject-gtk3
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.area = None
        self.shaderProgram = None
        self.positionHandle = None
        self.textureId = None
        self.vao = None
        self.vbos = None

    def onRealize(self, area):

        error = area.get_error()
        if error != None:
            print("your graphics card is probably too old : ", error)
        else:
            print(area, "realize... fine so far")

        self.ctx = self.get_context()
        self.ctx.make_current()

        print("OpenGL realized", self.ctx)

    def onRender(self, area, ctx):
        
        self.render(self.frame)
        return True

    def setupGraphics(self):

        if self.shaderProgram is None:
            # Load Shaders, Create program, Setup Graphics
            vertexShader = glCreateShader(GL_VERTEX_SHADER)
            glShaderSource(vertexShader, VERTEX_SOURCE)
            glCompileShader(vertexShader)
            status = glGetShaderiv(vertexShader, GL_COMPILE_STATUS)
            print("Compile vertexShader status: " + str(status == GL_TRUE))

            pixelShader = glCreateShader(GL_FRAGMENT_SHADER)
            glShaderSource(pixelShader, FRAGMENT_SOURCE)
            glCompileShader(pixelShader)
            status = glGetShaderiv(pixelShader, GL_COMPILE_STATUS)
            print("Compile vertexShader status: " + str(status == GL_TRUE))

            self.shaderProgram = glCreateProgram()
            glAttachShader(self.shaderProgram, vertexShader)
            glAttachShader(self.shaderProgram, pixelShader)
            glLinkProgram(self.shaderProgram)
            glBindFragDataLocation(self.shaderProgram, 0, "color")
            self.positionHandle = glGetAttribLocation(self.shaderProgram, "position")

            # Initalize Vertex Buffers
            self.initBuffers()
    
    def initBuffers(self):
        # Initialize an buffer to store all the verticles and transfer them to the GPU
        self.vao = glGenVertexArrays(1) # Generate VAO
        self.vbos = glGenBuffers(1) # Generate VBO
        glBindVertexArray(self.vao) # Bind the Vertex Array

        glBindBuffer(GL_ARRAY_BUFFER, self.vbos) # Bind verticles array for OpenGL to use
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * len(recVertices), recVertices, GL_STATIC_DRAW)
        
        # 1. set the vertex attributes pointers
        # Position Attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)
        # Color Attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(3 * sizeof(GLfloat)))
        glEnableVertexAttribArray(1)
        # Texture Coordinate Attribute
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(6 * sizeof(GLfloat)))
        glEnableVertexAttribArray(2)

        glBindVertexArray(0) # 3. Unbind VAO
    
    def generateTexture(self, frame):
        # Update Frame
        self.frame = frame

        # Delete previous textures to avoid memory leak
        if self.textureId is not None:
            glDeleteTextures(1, [self.textureId])

        # If we have a frame to display
        if frame is not None:
            # extract array from Image
            h, w, d = frame.shape

            # Frame is a 3 dimentional array where shape eg. (1920, 1080, 3)
            # Where it is w, h, and 3 values for color
            # https://www.educba.com/numpy-flatten/
            pixels = frame.flatten(order = 'C')

            # Generate Texture
            self.textureId = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, self.textureId) # Bind our 2D texture so that following set up will be applied

            # Set texture wrapping parameter
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)

            # Set texture Filtering parameter
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels)
            glGenerateMipmap(GL_TEXTURE_2D)
            glBindTexture(GL_TEXTURE_2D, 0) # Unbind 2D textures

    def render(self, frame):

        # Set OpenGL Render Context
        if self.ctx is not None and frame is not None:
            self.ctx.make_current()

            # Initialize Graphics
            self.setupGraphics()

            # Generate Texture
            self.generateTexture(frame)

            # Clear Screen
            glClearColor(0, 0, 1, 1)
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

            # Use Shader Program, Bind Vertex Array and Texture
            glUseProgram(self.shaderProgram)
            checkGlError("glUseProgram")
            glActiveTexture(GL_TEXTURE0)
            checkGlError("glActiveTexture")
            glBindTexture(GL_TEXTURE_2D, self.textureId)
            checkGlError("glBindTexture")
            mlocation = glGetUniformLocation(self.shaderProgram, "ourTexture")
            checkGlError("glGetUniformLocation")
            glUniform1i(mlocation, 0)
            checkGlError("glUniform1i")
            glBindVertexArray(self.vao)
            checkGlError("glBindVertexArray")

            # Render Frame
            glDrawArrays(GL_TRIANGLES, 0, 3)
            glDrawArrays(GL_TRIANGLES, 2, 3)
            
            # Queue Draw
            glFlush()
            self.queue_draw()

在 Python 完整示例中的 GtkGLArea 中渲染纹理

import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import numpy as np
from OpenGL.GL import *
from OpenGL.GL import shaders

FRAGMENT_SOURCE ='''
#version 330
in vec3 Color;
in vec2 Texcoord;
out vec4 outColor;
uniform sampler2D imageTexture;
void main()
{
    outColor = texture(imageTexture, Texcoord);
}'''

VERTEX_SOURCE = '''
#version 330
layout (location=0) in vec3 position;
layout (location=1) in vec3 color;
layout (location=2) in vec2 texcoord;
out vec3 Color;
out vec2 Texcoord;
void main()
{
    Color = color;
    Texcoord = texcoord;
    gl_Position = vec4(position, 1.0);
}'''

def on_realize(self, area):        
    # We need to make the context current if we want to
    # call GL API
    area.make_current()

def on_render(area, context):
    print("%s\n", glGetString(GL_VERSION))
    area.make_current()

    ############################################
    # Init Shaders
    ############################################
    glClearColor(0, 0, 0, 1)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    VERTEX_SHADER_PROG = shaders.compileShader(VERTEX_SOURCE, GL_VERTEX_SHADER)
    FRAGMENT_SHADER_PROG = shaders.compileShader(FRAGMENT_SOURCE, GL_FRAGMENT_SHADER)
    shaderProgram = shaders.compileProgram(VERTEX_SHADER_PROG, FRAGMENT_SHADER_PROG)
    
    ############################################
    # Init Buffers
    ############################################
    # Create a new VAO (Vertex Array Object) and bind it
    vao = glGenVertexArrays(1)
    glBindVertexArray(vao)
    # Generate buffers to hold our vertices
    vertex_buffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer)
    # Send the data over to the buffer
    vertices = np.array([
        # Positions      Color           Texchords 
        1.0,  1.0, 0.0,  0.0, 1.0, 0.0,  1.0, 0.0, # Top Right       0
        1.0, -1.0, 0.0,  0.0, 0.0, 1.0,  1.0, 1.0, # Bottom Right    1
        -1.0, -1.0, 0.0, 1.0, 1.0, 1.0,  0.0, 1.0, # Bottom Left     2
        -1.0,  1.0, 0.0, 1.0, 0.0, 0.0,  0.0, 0.0, # Top Left        3
        1.0,  1.0, 0.0,  0.0, 1.0, 0.0,  1.0, 0.0, # Top Right       4
    ], dtype=np.float32)
    
    size = sizeof(GLfloat) * len(vertices)
    glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW)

    # Specify the layout of the vertex data
    posAttrib = glGetAttribLocation(shaderProgram, "position")
    glEnableVertexAttribArray(posAttrib)
    glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(0))

    colAttrib = glGetAttribLocation(shaderProgram, "color")
    glEnableVertexAttribArray(colAttrib)
    glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(3 * sizeof(GLfloat)))

    texAttrib = glGetAttribLocation(shaderProgram, "texcoord")
    glEnableVertexAttribArray(texAttrib)
    glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(6 * sizeof(GLfloat)))

    # Unbind the VAO first (Important)
    glBindVertexArray(0)

    ############################################
    # Render
    ############################################
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glUseProgram(shaderProgram)
    glBindVertexArray(vao)

    # Load Textures
    width = 2
    height = 2
    textureId = glGenTextures(1)
    glActiveTexture(GL_TEXTURE0)
    glBindTexture(GL_TEXTURE_2D, textureId)

    # Black/white checkerboard
    pixels = [
        0.0, 0.0, 0.0,   1.0, 1.0, 1.0,
        1.0, 1.0, 1.0,   0.0, 0.0, 0.0
    ]

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, pixels)
    glUniform1i(glGetUniformLocation(shaderProgram, "imageTexture"), 0)
    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_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)

    glDrawArrays(GL_TRIANGLES, 0, 3)
    glDrawArrays(GL_TRIANGLES, 2, 3)
    glBindVertexArray(0)
    glUseProgram(0)

    # we completed our drawing; the draw commands will be
    # flushed at the end of the signal emission chain, and
    # the buffers will be drawn on the window
    return True

win = Gtk.Window()
area = Gtk.GLArea()
#area.set_required_version(2, 1)
#major, minor = area.get_required_version()
#print("Version " + str(major) + "." + str(minor))
area.connect('render', on_render)
area.connect('realize', on_realize)
win.connect("destroy", Gtk.main_quit)
win.add(area)
win.show_all()
Gtk.main()
© www.soinside.com 2019 - 2024. All rights reserved.