Skip to content Skip to sidebar Skip to footer

White Spots When Drawing Mutliple Lines Close To Each Other

How can I get rid of white spots when drawing multiple circles close to each other in pygame? Here is my code: import pygame from pygame import gfxdraw from math import pow, atan2

Solution 1:

This phenomenon is called aliasing and happens when you take a continuous signal and samples it. In your case, gfx.draw() uses continuous functions (the trigonometric functions) to calculate which pixel to draw the color onto. Since theses calculations are in floats and have to be rounded to integers, it may happen that some pixels are missed.

To fix this you need an anti-aliasing filter. There are many different types such as low pass (blurring), oversampling etc.


Since these holes almost always are one pixel I'd create a function that identifies these holes and fills them with the average of it's neighbours colors. The problem is that Pygame is not very good at manually manipulating pixels, so it can be slow depending on the size of the image. Although, Pygame has a module called surfarray that's built on numpy which allows you to access pixels easier and faster, so that will speed it up some. Of course, it'll require you to install numpy.

I couldn't get your program to work, so next time make sure you really have a Minimal, Complete, and Verifiable example. The following code is just based on the image you provided.

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)


defremove_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 inrange(1, height-1):
        for col inrange(1, width-1):
            if contains_background[row, col]:
                average = np.zeros(shape=(1, 3), dtype=np.uint16)
                elements = 0for y, x in neighbours:
                    ifnot 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)


defmain():
    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 = Falseelif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = Falseelif 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()

Result

Before Before

After After

Time

As I said before, it's not a very fast operation. Here are some benchmarks based on the circle in the example:

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

And with your image:

Time: 1.6557E-01 s

Post a Comment for "White Spots When Drawing Mutliple Lines Close To Each Other"