绘制多条线彼此接近时的白点 [英] White spots when drawing mutliple lines close to each other

查看:147
本文介绍了绘制多条线彼此接近时的白点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在 pygame 中绘制多个圆圈时,摆脱白点?





这是我的代码:

 从pygame导入pygame 
导入gfxdraw
来自math import pow,atan2

def getColor(r,col1,col2,fun =lin ):
如果有趣==pol2:
r = pow(r,2)

col1_r =元组([r * x代表col1中的x])
col2_r =元组([(1-r)* x代表col2中的x])
final_col =元组(我在zip中的sum(i)(col1_r,col2_r))
返回final_col

def draw(sizeX,sizeY):
#初始化游戏引擎
pygame.init()

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

#Loop直到用户点击关闭按钮。
done = False
clock = pygame.time.Clock()

未完成时:

#这将while循环限制为最大值每秒10次。
#将其保留,我们将尽可能使用所有CPU。
clock.tick(10)

pygame.event.get()中的事件:#用户做了
,如果event.type == pygame.QUIT:#If if user点击关闭
done = True#标记我们已完成,因此我们退出此循环

screen.fill(WHITE)

y范围内(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()


解决方案

这种现象被称为



$ b $之后b



时间



正如我之前所说,它不是很快速操作。以下是基于示例中的圆圈的一些基准:

 表面尺寸:(100,100)|时间:1.1521E-02 s 
表面尺寸:(200,200)|时间:4.3365E-02 s
表面尺寸:(300,300)|时间:9.7489E-02 s
表面尺寸:(400,400)|时间:1.7257E-01 s
表面尺寸:(500,500)|时间:2.6911E-01 s
表面尺寸:(600,600)|时间:3.8759E-01 s
表面尺寸:(700,700)|时间:5.2999E-01 s
表面尺寸:(800,800)|时间:6.9134E-01 s
表面尺寸:(900,900)|时间:9.1454E-01 s

并附上您的图片:

 时间:1.6557E-01 s 


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

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()

解决方案

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)


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()

Result

Before

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-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

And with your image:

Time: 1.6557E-01 s

这篇关于绘制多条线彼此接近时的白点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆