Numpy array to and from ModernGL buffer (open and save with cv2)

Numpy array to and from ModernGL buffer (open and save with cv2)

我想:

  1. 通过 cv2 从图像 打开纹理,而不是通过 ModernGL 的 load_texture_2d 方法。
  2. 保存生成的图像(在 write 方法中)通过 cv2 而不是 Pillow

我目前有以下代码:

from pathlib import Path
from array import array

import cv2
import numpy as np
from PIL import Image

import moderngl
import moderngl_window


class ImageProcessing(moderngl_window.WindowConfig):
    window_size = 3840 // 2, 2160 // 2
    resource_dir = Path(__file__).parent.resolve()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.image_processing = ImageTransformer(self.ctx, self.window_size)

        # Not working:
        #img = cv2.imread("test6.png")
        #self.texture = img.astype('f4')
        
        self.texture = self.load_texture_2d("test6.png")

    def render(self, time, frame_time):
        # View in Window
        self.image_processing.render(self.texture, target=self.ctx.screen)

        # Headless
        self.image_processing.render(self.texture)
        self.image_processing.write("output.png")


class ImageTransformer:

    def __init__(self, ctx, size, program=None):
        self.ctx = ctx
        self.size = size
        self.program = None
        self.fbo = self.ctx.framebuffer(
            color_attachments=[self.ctx.texture(self.size, 4)]
        )

        # Create some default program if needed
        if not program:
            self.program = self.ctx.program(
                vertex_shader="""
                    #version 330

                    in vec2 in_position;
                    in vec2 in_uv;
                    out vec2 uv;

                    void main() {
                        gl_Position = vec4(in_position, 0.0, 1.0);
                        uv = in_uv;
                    }
                """,
                fragment_shader = """
                    #version 330

                    uniform sampler2D image;
                    in vec2 uv;
                    out vec4 out_color;

                    void main() {
                        vec4 color = texture(image, uv);
                        // do something with color here
                        out_color = vec4(color.r, 0, 0, color.a);
                    }
                """,          
            )

        # Fullscreen quad in NDC
        self.vertices = self.ctx.buffer(
            array(
                'f',
                [
                    # Triangle strip creating a fullscreen quad
                    # x, y, u, v
                    -1,  1, 0, 1,  # upper left
                    -1, -1, 0, 0, # lower left
                     1,  1, 1, 1, # upper right
                     1, -1, 1, 0, # lower right
                ]
            )
        )
        self.quad = self.ctx.vertex_array(
            self.program,
            [
                (self.vertices, '2f 2f', 'in_position', 'in_uv'),
            ]
        )

    def render(self, texture, target=None):
        if target:
            target.use()
        else:
            self.fbo.use()

        texture.use(0)
        self.quad.render(mode=moderngl.TRIANGLE_STRIP)

    def write(self, name):

        # This doesn't work:
        raw = self.fbo.read(components=4, dtype='f4')
        buf = np.frombuffer(raw, dtype='f4')
        cv2.imwrite("OUTPUT_IMAGE.png", buf)

        # But this does:
##        image = Image.frombytes("RGBA", self.fbo.size, self.fbo.read())
##        image = image.transpose(Image.FLIP_TOP_BOTTOM)
##        image.save(name, format="png")


if __name__ == "__main__":
    ImageProcessing.run()

目前,当代码按原样 运行 时,不会保存任何图像。 window 只是挂起,没有任何反应。我不确定是我的代码有问题还是数据类型有误。

pillow 代码(如果您取消注释)可以保存它,但请注意:虽然我可以从 Pillow 转换为 numpy 数组,但我不想在我的用例中这样做。

澄清:window 加载和显示图像结果很好,但在 write 方法中没有正确保存。

你能说出,在 write() 方法中,buf.shape 是什么吗?我认为此时它是一个一维数组,它可能是 height * width * 4 个元素长。

imwrite() 需要它的形状正确。在 imwrite() 之前试试这个:

buf.shape = (self.size[1], self.size[0], 4)

应该将数据重塑为 (height, width, 4) 然后 imwrite() 应该接受它。

您的应用程序中缺少 som 代码

方法load_texture_2d creates a moderngl.Texture对象。因此,该方法加载文件,创建纹理对象并将纹理图像从 CPU 加载到 GPU。

cv2.imread just load the image file to a NumPy array, but it doesn't create a moderngl.Texture 对象。

您必须从 NumPy 数组生成一个 moderngl.Texture 对象:

img = cv2.imread("test6.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # optional
img = np.flip(img, 0).copy(order='C')      # optional
self.texture = self.ctx.texture(img.shape[1::-1], img.shape[2], img)

在将缓冲区写入图像之前,您必须根据图像格式reshape NumPy 数组。例如:

raw = self.fbo.read(components=4, dtype='f1')
buf = np.frombuffer(raw, dtype='uint8').reshape((*self.fbo.size[1::-1], 4))
cv2.imwrite("OUTPUT_IMAGE.png", buf)