【问题标题】:Remove anti-aliasing from an image从图像中删除抗锯齿
【发布时间】:2020-03-25 00:27:22
【问题描述】:

我想从图像中移除抗锯齿。此代码将从图像中获取 4 种主要颜色,将每个像素与 4 种主要颜色进行比较并分配最接近的颜色。

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

这是结果:

如您所见,颜色之间的边界并不完美。

这就是我想要的结果:

(似乎图片托管网站压缩了图片,并且不会正确显示“别名”图片)

【问题讨论】:

  • @HansHirse 可能是对的;我没有考虑这方面,并检查了后面的大部分内容;-)我想指出两个方面:①您确定四种主要颜色的方法仅适用于没有很多中间色且没有大区域的抖动(JPG 工件和类似的),但也许您的输入符合这些特征。 ② 如果主色 A 接近主色 B 和 C 之间的线,您的算法将选择 A 作为 B 和 C 之间的抗锯齿像素。您可以通过使用相邻像素而不是简单的颜色匹配来解决此问题。跨度>

标签: python numpy image-processing python-imaging-library antialiasing


【解决方案1】:

这里的主要问题在于你的closest方法:

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

colorscolor 都成为 uint8 类型的 NumPy 数组。现在,当减去uint8 值时,您不会得到负值,但会发生整数下溢,导致值接近255。所以,然后计算出来的distances是错误的,最终导致选错颜色。

因此,最快的解决方法是将两个变量都转换为 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))

此外,利用 NumPy 的矢量化能力可能会很有用。为您的 closest 方法考虑以下方法:

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)

所以,不是用

迭代所有像素
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])

你可以简单地传递整个图像:

image_nd = closest(four_major_colors, image_nd)

使用给定的图像,我的机器速度提高了 100 倍。当然,也可以优化查找 RGB 直方图值。 (不幸的是,我对 Python 字典的体验还不是很好……)

无论如何——希望对你有所帮助!

【讨论】:

  • 我不明白为什么在转换 closest() 方法的返回值时会得到 TypeError: Cannot handle this data type
  • 抱歉,我刚刚发现Image.fromarray() 预计uint8
  • @conquistador 这是我的一个实际错误,转换回uint8 应该在那里。我编辑了我的答案,并解决了这个问题。很抱歉。
  • 您可以省略 np.sqrt(),因为 a
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-29
  • 1970-01-01
  • 2016-04-24
  • 1970-01-01
  • 2014-02-18
  • 2021-02-12
  • 2015-05-21
相关资源
最近更新 更多