将 pygame 转换为 cv2 到 pygame 时表面 alpha 丢失

Surface alpha lost when converting pygame to cv2 to pygame

我正在 pygame 中制作一个 bloom 系统作为一个项目。 bloom 的代码有效,但是随着它如何从 pygame.Surface 转换为 np.array,它失去了它的 alpha层.

这是代码:

def Bloom(canvas: pygame.Surface):
    size = canvas.get_size()
    canvas_zero = canvas
    canvas = pygame.surfarray.array3d(canvas)
    newCanvas = pygame.Surface(size, pygame.SRCALPHA)
    newCanvas.set_colorkey((0,0,0))


    cv2.GaussianBlur(canvas, ksize=(9, 9), sigmaX=10, sigmaY=10, dst=canvas)
    cv2.blur(canvas, ksize=(9, 9), dst=canvas)

    newCanvas.blit(pygame.surfarray.make_surface(canvas), (0,0))
    newCanvas.blit(canvas_zero, (0,0))

    return newCanvas

这是系统的样子

如您所见,粒子上有这个由 set_colorkey 创建的黑色轮廓。

我想要的是从白色到完全透明的渐变,我得到的是从白色到黑色的渐变。

我该如何解决这个问题?

(我试过 PIL 而不是 cv2,但它太慢了(15 fps))

pygame.surfarray.array3d()仅returns表面的RGB通道。

您必须连接 RGB 颜色通道和 Alpha 通道。 Alpha 通道可以通过 pygame.surfarray.array_alpha():

获取
canvas_rgb = pygame.surfarray.array3d(canvas)
canvas_alpha = pygame.surfarray.array_alpha(canvas).reshape((*canvas_rgb.shape[0:2], 1))
canvas_rgba = numpy.concatenate((canvas_rgb, canvas_alpha), 2)

通过 pygame.surfarray.array2d() and creating a new view of the array with numpy.ndarray.view:

获取颜色值可能会获得更好的性能
canvas_color = pygame.surfarray.array2d(canvas)
canvas_rgba = canvas_color.view(dtype=numpy.uint8).reshape((*canvas_rgb.shape[0:2], 4))

Bloom 函数选项 1:

def Bloom(canvas: pygame.Surface):
    size = canvas.get_size()
    newCanvas = pygame.Surface(size, pygame.SRCALPHA)
    newCanvas.set_colorkey((0,0,0))

    canvas_rgb = pygame.surfarray.array3d(canvas)
    canvas_alpha = pygame.surfarray.array_alpha(canvas).reshape((*canvas_rgb.shape[0:2], 1))
    canvas_rgba = numpy.concatenate((canvas_rgb, canvas_alpha), 2)

    cv2.GaussianBlur(canvas_rgba, ksize=(9, 9), sigmaX=10, sigmaY=10, dst=canvas_rgba)
    cv2.blur(canvas_rgba, ksize=(9, 9), dst=canvas_rgba)

    newCanvas.blit(pygame.image.frombuffer(canvas_rgba.transpose((1, 0, 2)).copy(order='C'), size, 'RGBA'), (0,0))
    newCanvas.blit(canvas, (0,0))

    return newCanvas

Bloom 函数选项 2:

def Bloom(canvas: pygame.Surface):
    size = canvas.get_size()
    newCanvas = pygame.Surface(size, pygame.SRCALPHA)
    newCanvas.set_colorkey((0,0,0))

    canvas_color = pygame.surfarray.array2d(canvas)
    canvas_rgba = canvas_color.view(dtype=numpy.uint8).reshape((*canvas_color.shape, 4))

    cv2.GaussianBlur(canvas_rgba, ksize=(9, 9), sigmaX=10, sigmaY=10, dst=canvas_rgba)
    cv2.blur(canvas_rgba, ksize=(9, 9), dst=canvas_rgba)

    pygame.surfarray.blit_array(newCanvas, canvas_color)
    newCanvas.blit(canvas, (0,0))

    return newCanvas

最小示例:

import pygame, cv2, numpy

def Bloom1(canvas: pygame.Surface):
    size = canvas.get_size()
    newCanvas = pygame.Surface(size, pygame.SRCALPHA)
    newCanvas.set_colorkey((0,0,0))

    canvas_rgb = pygame.surfarray.array3d(canvas)
    canvas_alpha = pygame.surfarray.array_alpha(canvas).reshape((*canvas_rgb.shape[0:2], 1))
    canvas_rgba = numpy.concatenate((canvas_rgb, canvas_alpha), 2)

    cv2.GaussianBlur(canvas_rgba, ksize=(9, 9), sigmaX=10, sigmaY=10, dst=canvas_rgba)
    cv2.blur(canvas_rgba, ksize=(9, 9), dst=canvas_rgba)

    newCanvas.blit(pygame.image.frombuffer(canvas_rgba.transpose((1, 0, 2)).copy(order='C'), size, 'RGBA'), (0,0))
    return newCanvas

def Bloom2(canvas: pygame.Surface):
    size = canvas.get_size()
    newCanvas = pygame.Surface(size, pygame.SRCALPHA)
    newCanvas.set_colorkey((0,0,0))

    canvas_color = pygame.surfarray.array2d(canvas)
    canvas_rgba = canvas_color.view(dtype=numpy.uint8).reshape((*canvas_color.shape, 4))

    cv2.GaussianBlur(canvas_rgba, ksize=(9, 9), sigmaX=10, sigmaY=10, dst=canvas_rgba)
    cv2.blur(canvas_rgba, ksize=(9, 9), dst=canvas_rgba)

    pygame.surfarray.blit_array(newCanvas, canvas_color)
    return newCanvas

pygame.init()
window = pygame.display.set_mode((550, 300))
clock = pygame.time.Clock()

font = pygame.font.SysFont(None, 90)
text = font.render("Bloom", True, 0)

surface = pygame.Surface((250, 250))
surface.fill(0)
pygame.draw.circle(surface, (255, 255, 255), surface.get_rect().center, 100)
surface.blit(text, text.get_rect(center = surface.get_rect().center))
surface.set_colorkey(0)
surface = surface.convert_alpha()
surface1 = Bloom1(surface)
surface2 = Bloom1(surface)

run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False 

    window.fill((64, 64, 64))
    window.blit(surface1, surface1.get_rect(center = (150, 150)), special_flags = pygame.BLEND_PREMULTIPLIED)
    window.blit(surface2, surface2.get_rect(center = (400, 150)), special_flags = pygame.BLEND_PREMULTIPLIED)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
exit()