Remove anti-aliasing from an image

青春壹個敷衍的年華 提交于 2021-01-28 01:44:46

问题


I want to remove the antialiasing from an image. This code will get the 4 major colors from an image, compare each pixel to the 4 major colors and assign the closest color.

import numpy as np
from PIL import Image

image = Image.open('pattern_2.png')
image_nd = np.array(image)
image_colors = {}

for row in image_nd:
    for pxl in row:
        pxl = tuple(pxl)
        if not image_colors.get(pxl):
            image_colors[pxl] = 1
        else:
            image_colors[pxl] += 1

sorted_image_colors = sorted(image_colors, key=image_colors.get, reverse=True)
four_major_colors = sorted_image_colors[:4]


def closest(colors, color):
    colors = np.array(colors)
    color = np.array(color)
    distances = np.sqrt(np.sum((colors - color) ** 2, axis=1))
    index_of_smallest = np.where(distances == np.amin(distances))
    smallest_distance = colors[index_of_smallest]
    return smallest_distance[0]


for y, row in enumerate(image_nd):
    for x, pxl in enumerate(row):
        image_nd[y, x] = closest(four_major_colors, image_nd[y, x])

aliased = Image.fromarray(image_nd)
aliased.save("pattern_2_al.png")

This is the result:

As you can see, the borders between colors aren't perfect.

And this is the result I'm after:

(it seems the image hosting site compresses the image, and won't show "aliased" image properly)


回答1:


The main problem here is located in your closest method:

def closest(colors, color):
    colors = np.array(colors)
    color = np.array(color)
    distances = np.sqrt(np.sum((colors - color) ** 2, axis=1))

Both colors and color become NumPy arrays of type uint8. Now, when subtracting uint8 values, you won't get negative values, but integer underflow will happen, resulting in values near 255. Therefore, the then calculated distances are wrong, which finally leads to the wrong color picking.

So, the fastest fix would be to cast both variables to int32:

def closest(colors, color):
    colors = np.array(colors).astype(np.int32)
    color = np.array(color).astype(np.int32)
    distances = np.sqrt(np.sum((colors - color) ** 2, axis=1))

Also, it might be useful to make use of NumPy's vectorization power. Consider the following approach for your closest method:

def closest(colors, image):
    colors = np.array(colors).astype(np.int32)
    image = image.astype(np.int32)
    distances = np.argmin(np.array([np.sqrt(np.sum((color - image) ** 2, axis=2)) for color in colors]), axis=0)
    return colors[distances].astype(np.uint8)

So, instead of iterating all the pixels with

for y in np.arange(image_nd.shape[0]):
    for x in np.arange(image_nd.shape[1]):
        image_nd[y, x] = closest(four_major_colors, image_nd[y, x])

you can simply pass the whole image:

image_nd = closest(four_major_colors, image_nd)

Using the given image, I get a speed-up of 100x on my machine. Surely, finding the RGB histogram values can also be optimized. (Unfortunately, my experience with Python dictionaries isn't yet that great...)

Anyway – hope that helps!



来源:https://stackoverflow.com/questions/59104242/remove-anti-aliasing-from-an-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!