绘制多条线彼此接近时的白点

时间:2017-02-12 19:06:32

标签: python image pygame

如何在pygame中绘制多个彼此靠近的圆圈时摆脱白点?

Multiple circles drawn close to each other

这是我的代码:

import pygame
from pygame import gfxdraw
from math import pow, atan2

def getColor(r, col1, col2, fun="lin"):
    if fun =="pol2":
        r = pow(r,2)

    col1_r = tuple([r*x for x in col1])
    col2_r = tuple([(1-r)*x for x in col2])
    final_col = tuple(sum(i) for i in zip(col1_r, col2_r))
    return final_col

def draw(sizeX, sizeY):
    # Initialize the game engine
    pygame.init()

    screen = pygame.display.set_mode([sizeX, sizeY])

    #Loop until the user clicks the close button.
    done = False
    clock = pygame.time.Clock()

    while not done:

        # This limits the while loop to a max of 10 times per second.
        # Leave this out and we will use all CPU we can.
        clock.tick(10)

        for event in pygame.event.get(): # User did something
            if event.type == pygame.QUIT: # If user clicked close
                done=True # Flag that we are done so we exit this loop

        screen.fill(WHITE)

        for y in range(200,500):
            for x in range(0,10):
                gfxdraw.arc(screen, 400, 400, y, x*15, (x+1)*15, getColor(x/10,(0,0,(y-200)/2),(255,255,(y-200)/2), fun="lin"))

        pygame.display.flip()

    pygame.quit()

1 个答案:

答案 0 :(得分:2)

这种现象称为aliasing,当你接收到连续信号并samples时就会发生这种现象。在您的情况下,gfx.draw()使用连续函数(三角函数)来计算要绘制颜色的像素。由于这些计算是浮点数并且必须舍入为整数,因此可能会错过某些像素。

要解决此问题,您需要anti-aliasing filter。有许多不同的类型,如低通(模糊),过采样等。

由于这些孔几乎总是一个像素,我会创建一个识别这些孔的函数,并用它的邻居颜色的平均值填充它们。问题是Pygame不是很擅长手动操作像素,所以它可能会很慢,具体取决于图像的大小。虽然,Pygame有一个名为surfarray的模块,它基于numpy构建,允许您更轻松,更快地访问像素,因此可以加快一些像素。当然,它需要你安装numpy。

我无法让你的程序运行,所以下次确保你真的有Minimal, Complete, and Verifiable example。以下代码仅基于您提供的图像。

import numpy as np
import pygame
pygame.init()

RADIUS = 1080 // 2
FPS = 30
screen = pygame.display.set_mode((RADIUS * 2, RADIUS * 2))
clock = pygame.time.Clock()

circle_size = (RADIUS * 2, RADIUS * 2)
circle = pygame.Surface(circle_size)

background_color = (255, 255, 255)
circle_color = (255, 0, 0)

pygame.draw.circle(circle, circle_color, (RADIUS, RADIUS), RADIUS, RADIUS // 2)


def remove_holes(surface, background=(0, 0, 0)):
    """
    Removes holes caused by aliasing.

    The function locates pixels of color 'background' that are surrounded by pixels of different colors and set them to
    the average of their neighbours. Won't fix pixels with 2 or less adjacent pixels.

    Args:
        surface (pygame.Surface): the pygame.Surface to anti-aliasing.
        background (3 element list or tuple): the color of the holes.

    Returns:
        anti-aliased pygame.Surface.
    """
    width, height = surface.get_size()
    array = pygame.surfarray.array3d(surface)
    contains_background = (array == background).all(axis=2)

    neighbours = (0, 1), (0, -1), (1, 0), (-1, 0)

    for row in range(1, height-1):
        for col in range(1, width-1):
            if contains_background[row, col]:
                average = np.zeros(shape=(1, 3), dtype=np.uint16)
                elements = 0
                for y, x in neighbours:
                    if not contains_background[row+y, col+x]:
                        elements += 1
                        average += array[row+y, col+x]
                if elements > 2:  # Only apply average if more than 2 neighbours is not of background color.
                    array[row, col] = average // elements

    return pygame.surfarray.make_surface(array)


def main():
    running = True
    image = pygame.image.load('test.png').convert()
    # image = circle
    pos = image.get_rect(center=(RADIUS, RADIUS))
    while running:

        clock.tick(FPS)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False
                elif event.key == pygame.K_1:
                    print('Reset circle.')
                    image = circle
                elif event.key == pygame.K_2:
                    print('Starting removing holes.')
                    time = pygame.time.get_ticks()
                    image = remove_holes(image, background=(255, 255, 255))
                    time = pygame.time.get_ticks() - time
                    print('Finished removing holes in {:.4E} s.'.format(time / 1000))

        screen.fill(background_color)
        screen.blit(image, pos)
        pygame.display.update()


if __name__ == '__main__':
    main()

结果

Before

After

时间

正如我之前所说,这不是一个非常快速的操作。以下是基于示例中的圆圈的一些基准测试:

Surface size: (100, 100) | Time: 1.1521E-02 s
Surface size: (200, 200) | Time: 4.3365E-02 s
Surface size: (300, 300) | Time: 9.7489E-02 s
Surface size: (400, 400) | Time: 1.7257E-01 s
Surface size: (500, 500) | Time: 2.6911E-01 s
Surface size: (600, 600) | Time: 3.8759E-01 s
Surface size: (700, 700) | Time: 5.2999E-01 s
Surface size: (800, 800) | Time: 6.9134E-01 s
Surface size: (900, 900) | Time: 9.1454E-01 s

用你的形象:

Time: 1.6557E-01 s