Antialiasing an image

Viewed 66

I have an image in pygame. I want to apply antialiasing to it. How can I do this? Ideally, I would be able to do this just with pygame and built-in modules but I'm open to other options if necessary.

More specifically, I've got an image of a square split into 4. Each of the quadrants has a different colour. I want to blur it so it looks more like a gradient, so instead of the colours switching instantly where the quadrants meet, it would fade slowly. I believe anti-aliasing is the best way to accomplish this? Here's an image of what I mean:left: what I've got, right: what I need to have

1 Answers

It's not clear what specific image smoothing algorithm you want to use. SciPy provides some relevant functions, so I've constructed an example using ndimage.gaussian_filter().

Clicking a mouse button will apply the filter, or you can scroll the wheel.

import pygame
from scipy.ndimage import gaussian_filter
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html


def smooooooth(screen):
    """Apply a gaussian filter to each colour plane"""
    # Get reference pixels for each colour plane and then apply filter
    r = pygame.surfarray.pixels_red(screen)
    gaussian_filter(r, sigma=72, mode="nearest", output=r)
    g = pygame.surfarray.pixels_green(screen)
    gaussian_filter(g, sigma=72, mode="nearest", output=g)
    b = pygame.surfarray.pixels_blue(screen)
    gaussian_filter(b, sigma=72, mode="nearest", output=b)

height = 300
width = 300

pygame.init()
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
pygame.display.set_caption("Smooth Image")
clock = pygame.time.Clock()

# draw initial pattern
screen.fill((194, 198, 199))  # fill with gray
# fill top left quadrant with white
screen.fill(pygame.Color("white"), pygame.Rect(0, 0, width // 2, height // 2))
# fill bottom left quadrant with black
screen.fill(pygame.Color("black"), pygame.Rect(width // 2, height // 2, width, height))

count = 0
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONUP:
            smooooooth(screen)
            count += 1
            pygame.display.set_caption(f"Smooth Image {count}")

    pygame.display.update()
    # limit framerate
    clock.tick(30)
pygame.quit()

If you play with the sigma argument to the gaussian_filter function, you might be able to get closer to what you're after. E.g. with sigma 72, two passes looks like this:

Smooooth 2 @ sigma 72

I've modified the example code to properly support screen resizing, which will redraw the base pattern and using the + and - keys to increment the sigma value so you can do it interactively. Hold Shift to adjust by ten instead of one.

import pygame
from scipy.ndimage import gaussian_filter
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html

def smooooooth(screen, sisgma):
    """Apply a gaussian filter to each colour plane"""
    # Get reference pixels for each colour plane and then apply filter
    r = pygame.surfarray.pixels_red(screen)
    gaussian_filter(r, sigma=sigma, mode="nearest", output=r)
    g = pygame.surfarray.pixels_green(screen)
    gaussian_filter(g, sigma=sigma, mode="nearest", output=g)
    b = pygame.surfarray.pixels_blue(screen)
    gaussian_filter(b, sigma=sigma, mode="nearest", output=b)

def draw_grey_squares(screen):
    """Draw the base pattern"""
    screen.fill((194, 198, 199))  # fill with grey
    # fill top left quadrant with white
    screen.fill(pygame.Color("white"), (0, 0, width // 2, height // 2))
    # fill bottom left quadrant with black
    screen.fill(pygame.Color("black"), (width // 2, height // 2, width, height))

height = 300
width = 300

pygame.init()
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
draw_grey_squares(screen)
pygame.display.set_caption("Smooth Image")
clock = pygame.time.Clock()

sigma = 72
count = 0
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.VIDEORESIZE:
            # redraw on window resize
            width, height = event.w, event.h
            draw_grey_squares(screen)
            count = 0
        elif event.type == pygame.KEYUP:
            # support + and - to modify sigma
            if event.key in (pygame.K_PLUS, pygame.K_KP_PLUS):
                if event.mod & pygame.KMOD_SHIFT:
                    sigma += 10
                else:
                    sigma += 1
            elif event.key in (pygame.K_MINUS, pygame.K_KP_MINUS):
                if event.mod & pygame.KMOD_SHIFT:
                    sigma -= 10
                else:
                    sigma -= 1
                sigma = max(sigma, 1)  # sigma below one doesn't make sense
        elif event.type == pygame.MOUSEBUTTONUP:
            smooooooth(screen, sigma)
            count += 1
    pygame.display.set_caption(f"Smooth Image {count} σ {sigma}")
    pygame.display.update()
    clock.tick(30)  # limit framerate
pygame.quit()

Here's an example with sigma 200:

Smooooooth 1 Sigma 200

Related