White Spots When Drawing Mutliple Lines Close To Each Other
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
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"