Pygame 中的 Bloom 效果使文本发光

Reb*_*cca 0 python text pygame glow python-3.x

我想复制 Watson-scott 测试中显示的效果,其中文本似乎会发光。示例链接:https://www.youtube.com/watch?v =2ySNm4gltkE

跳到 11:17,文字似乎在发光;我如何用 pygame 复制这种效果?我尝试在文本背景中添加一个灰色矩形,但它看起来很糟糕。我也尝试像这个例子一样对文本进行阴影处理,但没有任何效果。

我也在使用Python 3.7.4。感谢您的帮助,我真的需要它!

Thy*_*men 12

有时我们可以说这是不可能的,但很多时候这并不是该软件包的主要目标。尽管如此,让我们看看能否解决这个问题。

我冒昧地假设除此之外pygame还允许使用其他包,但最终结果应该在pygame. 为了创建绽放/发光效果,我使用了包opencv-python(cv2)和numpy(np)。

该解决方案的第一部分将讨论创建发光边框和一些发光文本。第二部分将讨论如何在表面上渲染它pygame

TL;博士; 跳到下面的摘要部分并将代码复制到各自的文件中。

第1部分

盛开

为了获得一些漂亮的发光边框和文本,我们可以使用opencv的模糊功能,也称为平滑。由于我们想要创建不同强度的发光,因此我们首先应用GaussianBlur, 在图像周围创建一些随机模糊度,然后使用法线 扩展该模糊度blur

def apply_blooming(image: np.ndarray) -> np.ndarray:
    # Provide some blurring to image, to create some bloom.
    cv2.GaussianBlur(image, ksize=(9, 9), sigmaX=10, sigmaY=10, dst=image)
    cv2.blur(image, ksize=(5, 5), dst=image)
    return image
Run Code Online (Sandbox Code Playgroud)

ksize注意:内核大小 ( ) 和西格玛 (sigmaX和)的值是sigmaY根据经验选择的,您可以稍微调整一下这些值,直到得到您想要的值。

颜色

一个小插曲,因为我们需要提供一些非常漂亮的可怕的颜色,所以下面的类包含一些(可怕的)颜色。

class Colors:
    WHITE_ISH = (246, 246, 246)
    YELLOW_ISH = (214, 198, 136)
    RED_ISH = (156, 60, 60)
Run Code Online (Sandbox Code Playgroud)

发光边框

为了获得发光边框,创建了一个辅助函数,它将绘制一个具有一些预定义属性的矩形。所选属性为:

  • margin:边框将绘制在距图像两侧较远的地方。
  • 厚度:边框将由这么多像素组成。
  • color:边框的颜色,以便可以轻松更改。
def create_border(image: np.ndarray, margin: int, thickness: int, color: Colors) -> np.ndarray:
    """
    Create a normal border around an image, with specified colors.

    Args:
        image: The image, that requires a border.
        margin: The border distance from the sides of the image.
        thickness: The thickness of the border.
        color: The border color, by default a slightly yellow color.

    Modifies:
        The input image, will be modified with a border.

    Returns:
        The same image, with a border inserted.

    """

    # Numpy uses the convention `rows, columns`, instead of `x, y`.
    # Therefore height, has to be before width.
    height, width = image.shape[:2]
    cv2.rectangle(image, (margin, margin), (width - margin, height - margin), color, thickness=thickness)
    return image
Run Code Online (Sandbox Code Playgroud)

apply_blooming然后可以使用和函数绘制最终边框create_border

def glowing_border(image: np.ndarray, margin=20, thickness=10, color: Colors = Colors.WHITE_ISH):
    """

    Create a glowing border around an image.

    Args:
        image: The image, that requires a border.
        margin: The border distance from the sides of the image.
        thickness: The thickness of the border.
        color: The border color, by default a slightly yellow color.

    Modifies:
        The input image, will be modified with a blooming border.

    Returns:
        The same image, with a blooming border inserted.
    """

    # Generate yellowish colored box
    image = create_border(image, margin, thickness, color)

    # Apply the blooming.
    image = apply_blooming(image)

    # Reassert the original border, to get a clear outline.
    # Similar to the Watson-Scott test, two borders were added here.
    image = create_border(image, margin - 1, 1, color)
    image = create_border(image, margin + 1, 1, color)
    return image
Run Code Online (Sandbox Code Playgroud)

测试代码

为了测试发光边框,我们可以使用cv2.imshow, 来显示图像。由于我们稍后将使用此功能,因此创建了一个小函数。该函数将图像和显示时间(代码继续执行之前的等待时间)作为输入。


def show(image, delay=0):
    """
    Display an image using cv2.

    Notes:
        By default cv2 uses the BGR coloring, instead RGB.
        Hence image shown by cv2, which are meant to be RGB,
        has to be transformed using `cvtColor`.

    Args:
        image: Input image to be displayed
        delay: Time delay before continuing running.
            When 0, The program will wait until a key stroke or window is closed.
            When 1, The program will continue as quickly as possible.

    Returns:
        Nothing, it displays the image.

    """
    cv2.imshow('Test', cv2.cvtColor(image, cv2.COLOR_RGB2BGR))
    cv2.waitKey(delay)
Run Code Online (Sandbox Code Playgroud)

实际测试代码:

image = np.zeros((480, 640, 3), dtype=np.uint8)
border = glowing_border(image.copy(), color=Colors.YELLOW_ISH)
show(border, delay=0)
Run Code Online (Sandbox Code Playgroud)

发光文字

类似的方法可以用于发光文本,通过使用cv2.putText.

def glowing_text(image: np.ndarray, text: str, org: Tuple[int, int], color: Colors) -> np.ndarray:
    """

    Args:
        image: The image, that requires a border.
        text: The text to be placed on the image.
        org: The starting location of the text.
        color: The color of the text.


    Modifies:
        The input image, will be modified with a blooming text.

    Returns:
        The same image, with a blooming text inserted.
    """

    image = cv2.putText(image, text, org, cv2.FONT_HERSHEY_COMPLEX_SMALL, fontScale=.7, color=color, thickness=1)
    image = apply_blooming(image)
    image = cv2.putText(image, text, org, cv2.FONT_HERSHEY_COMPLEX_SMALL, fontScale=.7, color=color, thickness=1)
    return image
Run Code Online (Sandbox Code Playgroud)

有测试代码

image = np.zeros((480, 640, 3), dtype=np.uint8)
text = glowing_text(image.copy(), text="Welcome to this game", org=(50, 70), color=Colors.YELLOW_ISH)
show(text, delay=0)
Run Code Online (Sandbox Code Playgroud)

间奏曲

在我继续展示如何在 中显示它之前pygame,我将添加一个额外的内容并展示文本如何显示在屏幕上,就好像一个人正在慢慢地输入它一样。以下代码之所以有效,是因为我们分别绘制边框和文本,然后使用np.bitwise_or.

image = np.zeros((480, 640, 3), dtype=np.uint8)

# Create the glowing border, and a copy of the image, for the text, that will be placed on it later.
border = glowing_border(image.copy(), color=Colors.YELLOW_ISH)
text = image.copy()

# This message will be incrementally written
message = "Welcome to this game. Don't be scared :)."

for idx in range(len(message) + 1):
    text = glowing_text(image.copy(), text=message[:idx], org=(50, 70), color=Colors.YELLOW_ISH)

    # We use a random time delay between keystrokes, to simulate a human.
    show(np.bitwise_or(border, text), delay=np.random.randint(1, 250))

# Pause the screen after the full message.
show(np.bitwise_or(border, text), delay=0)
Run Code Online (Sandbox Code Playgroud)

注意:或者,我们可以首先在同一图像上生成边框和文本,然后应用绽放滤镜。请记住,我们必须再次重新绘制边框和文本,为它们提供坚实的基础。

第2部分

现在我们可以生成具有正确的绽放边框和文本的画布,必须将其插入到pygame. 让我们将所有以前的函数放入一个名为 的文件中blooming.py,并在新文件中引用它game.py

以下代码是如何将 numpy 数组放入 pygame 的最小工作示例。

import contextlib
from typing import Tuple

# This suppresses the `Hello from pygame` message.
with contextlib.redirect_stdout(None):
    import pygame

import numpy as np
import blooming


def image_generator(size: Tuple[int, int], color: blooming.Colors):
    image = np.zeros((*size[::-1], 3), dtype=np.uint8)

    # Create the glowing border, and a copy of the image, for the text, that will be placed on it later.
    border = blooming.glowing_border(image.copy(), color=color)
    text = image.copy()

    # This message will be incrementally written
    message = "Welcome to this game. Don't be scared :)."

    for idx in range(len(message) + 1):
        text = blooming.glowing_text(image.copy(), text=message[:idx], org=(50, 70), color=color)
        yield np.bitwise_or(border, text)
    return np.bitwise_or(border, text)


if __name__ == '__main__':
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    running = True

    while running:
        for image in image_generator(screen.get_size(), color=blooming.Colors.YELLOW_ISH):
            screen.fill((0, 0, 0))

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        running = False

            # This is where we insert the numpy array.
            # Because pygame and numpy use different coordinate systems,
            # the numpy image has to be flipped and rotated, before being blit.
            img = pygame.surfarray.make_surface(np.fliplr(np.rot90(image, k=-1)))
            screen.blit(img, (0, 0))

            pygame.display.flip()
            clock.tick(np.random.randint(10, 30))

    pygame.quit()
Run Code Online (Sandbox Code Playgroud)

摘要(TL;DR;)

  • blooming.py
from typing import Tuple

import cv2
import numpy as np


class Colors:
    WHITE_ISH = (246, 246, 246)
    YELLOW_ISH = (214, 198, 136)
    RED_ISH = (156, 60, 60)


def create_border(image: np.ndarray, margin: int, thickness: int, color: Colors) -> np.ndarray:
    """
    Create a normal border around an image, with specified colors.

    Args:
        image: The image, that requires a border.
        margin: The border distance from the sides of the image.
        thickness: The thickness of the border.
        color: The border color, by default a slightly yellow color.

    Modifies:
        The input image, will be modified with a border.

    Returns:
        The same image, with a border inserted.

    """

    # Numpy uses the convention `rows, columns`, instead of `x, y`.
    # Therefore height, has to be before width.
    height, width = image.shape[:2]
    cv2.rectangle(image, (margin, margin), (width - margin, height - margin), color, thickness=thickness)
    return image


def apply_blooming(image: np.ndarray) -> np.ndarray:
    # Provide some blurring to image, to create some bloom.
    cv2.GaussianBlur(image, ksize=(9, 9), sigmaX=10, sigmaY=10, dst=image)
    cv2.blur(image, ksize=(5, 5), dst=image)
    return image


def glowing_border(image: np.ndarray, margin=20, thickness=10, color: Colors = Colors.WHITE_ISH):
    """

    Create a glowing border around an image.

    Args:
        image: The image, that requires a border.
        margin: The border distance from the sides of the image.
        thickness: The thickness of the border.
        color: The border color, by default a slightly yellow color.

    Modifies:
        The input image, will be modified with a blooming border.

    Returns:
        The same image, with a blooming border inserted.
    """

    # Generate yellowish colored box
    image = create_border(image, margin, thickness, color)

    # Apply the blooming.
    image = apply_blooming(image)

    # Reassert the original border, to get a clear outline.
    # Similar to the Watson-Scott test, two borders were added here.
    image = create_border(image, margin - 1, 1, color)
    image = create_border(image, margin + 1, 1, color)
    return image


def glowing_text(image: np.ndarray, text: str, org: Tuple[int, int], color: Colors) -> np.ndarray:
    """

    Args:
        image: The image, that requires a border.
        text: The text to be placed on the image.
        org: The starting location of the text.
        color: The color of the text.


    Modifies:
        The input image, will be modified with a blooming text.

    Returns:
        The same image, with a blooming text inserted.
    """

    image = cv2.putText(image, text, org, cv2.FONT_HERSHEY_COMPLEX_SMALL, fontScale=.7, color=color, thickness=1)
    image = apply_blooming(image)
    image = cv2.putText(image, text, org, cv2.FONT_HERSHEY_COMPLEX_SMALL, fontScale=.7, color=color, thickness=1)
    return image


def show(image, delay=0):
    """
    Display an image using cv2.

    Notes:
        By default cv2 uses the BGR coloring, instead RGB.
        Hence image shown by cv2, which are meant to be RGB,
        has to be transformed using `cvtColor`.

    Args:
        image: Input image to be displayed
        delay: Time delay before continuing running.
            When 0, The program will wait until a key stroke or window is closed.
            When 1, The program will continue as quickly as possible.

    Returns:
        Nothing, it displays the image.

    """
    cv2.imshow('Test', cv2.cvtColor(image, cv2.COLOR_RGB2BGR))
    cv2.waitKey(delay)


if __name__ == '__main__':
    image = np.zeros((480, 640, 3), dtype=np.uint8)

    # Create the glowing border, and a copy of the image, for the text, that will be placed on it later.
    border = glowing_border(image.copy(), color=Colors.YELLOW_ISH)
    text = image.copy()

    # This message will be incrementally written
    message = "Welcome to this game. Don't be scared :)." + " " * 10

    for idx in range(len(message) + 1):
        text = glowing_text(image.copy(), text=message[:idx], org=(50, 70), color=Colors.YELLOW_ISH)

        # We use a random time delay between keystrokes, to simulate a human.
        show(np.bitwise_or(border, text), delay=np.random.randint(1, 250))

    # Pause the screen after the full message.
    show(np.bitwise_or(border, text), delay=0)
Run Code Online (Sandbox Code Playgroud)
  • game.py
import contextlib
from typing import Tuple

# This suppresses the `Hello from pygame` message.
with contextlib.redirect_stdout(None):
    import pygame

import numpy as np
import blooming


def image_generator(size: Tuple[int, int], color: blooming.Colors):
    image = np.zeros((*size[::-1], 3), dtype=np.uint8)

    # Create the glowing border, and a copy of the image, for the text, that will be placed on it later.
    border = blooming.glowing_border(image.copy(), color=color)
    text = image.copy()

    # This message will be incrementally written
    message = "Welcome to this game. Don't be scared :)." + " " * 10

    for idx in range(len(message) + 1):
        text = blooming.glowing_text(image.copy(), text=message[:idx], org=(50, 70), color=color)
        yield np.bitwise_or(border, text)
    return np.bitwise_or(border, text)


if __name__ == '__main__':
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    running = True

    while running:
        for image in image_generator(screen.get_size(), color=blooming.Colors.YELLOW_ISH):
            screen.fill((0, 0, 0))

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        running = False

            # This is where we insert the numpy array.
            # Because pygame and numpy use different coordinate systems,
            # the numpy image has to be flipped and rotated, before being blit.
            img = pygame.surfarray.make_surface(np.fliplr(np.rot90(image, k=-1)))
            screen.blit(img, (0, 0))

            pygame.display.flip()
            clock.tick(np.random.randint(10, 30))

    pygame.quit()
Run Code Online (Sandbox Code Playgroud)

结果

请注意,真实的东西看起来比这张图片清晰得多。另外,调整文本的粗细和模糊滤镜的大小也会对结果产生很大的影响。对于此图像, 的ksizeGaussianBlur增加至(17, 17),并且sigmaXsigmaY均已设为 100。

在此输入图像描述