将图像中的颜色映射到颜色列表中最接近的成员,在 Python 中

Map colors in image to closest member of a list of colors, in Python

我有一个包含 19 种颜色的列表,它是一个大小为 (19,3):

的 numpy 数组
colors = np.array([[0, 0, 0], 
[0, 0, 255], 
[255, 0, 0], 
[150, 30, 150], 
[255, 65, 255], 
[150, 80, 0], 
[170, 120, 65], 
[125, 125, 125], 
[255, 255, 0], 
[0, 255, 255], 
[255, 150, 0], 
[255, 225, 120], 
[255, 125, 125], 
[200, 100, 100], 
[0, 255, 0], 
[0, 150, 80], 
[215, 175, 125], 
[220, 180, 210], 
[125, 125, 255]
])

现在我有一个图像(大小为 (1024,1024,3) 的 numpy 数组),其颜色有点接近或等于上面定义的所有颜色。但是,我的程序要求图像只能包含上面的颜色,不能包含附近的颜色,所以我需要将数组中每个像素的颜色转换为 19 种颜色中最接近的颜色。

我在这里看到了一个函数,它可以从一组颜色中找到最接近的颜色,它只使用 Numpy(效果很好):

def closest_color(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

使用此功能,我可以从预定义列表中找到最接近单一颜色的颜色,但在我的问题中,我没有想要更改的单一颜色,而是整个图像(1024 x 1024 像素颜色)。利用此函数(或使用任何更好的函数)的最有效方法是什么,它仅使用 numpy 来解决我的问题。我想尽可能减少for循环的数量,因为我总共需要处理30,000张大小为1024 x 1024的图像。

谢谢!

由于您正在计算的是欧几里德距离,因此您可以使用 scipy.spatial.distance.cdist 快速计算每对距离,然后从生成的距离矩阵中选择最接近的颜色:

import numpy as np
import scipy.spatial.distance

np.random.seed(0)
palette = np.array([
    [0, 0, 0],
    [0, 0, 255],
    [255, 0, 0],
    [150, 30, 150],
    [255, 65, 255],
    [150, 80, 0],
    [170, 120, 65],
    [125, 125, 125],
    [255, 255, 0],
    [0, 255, 255],
    [255, 150, 0],
    [255, 225, 120],
    [255, 125, 125],
    [200, 100, 100],
    [0, 255, 0],
    [0, 150, 80],
    [215, 175, 125],
    [220, 180, 210],
    [125, 125, 255]
])
data = np.random.randint(256, size=(10, 3))
closest_idx = scipy.spatial.distance.cdist(data, palette).argmin(1)
data_in_palette = palette[closest_idx]
print(*zip(data, data_in_palette), sep='\n')
# (array([172,  47, 117]), array([150,  30, 150]))
# (array([192,  67, 251]), array([255,  65, 255]))
# (array([195, 103,   9]), array([150,  80,   0]))
# (array([211,  21, 242]), array([255,  65, 255]))
# (array([36, 87, 70]), array([  0, 150,  80]))
# (array([216,  88, 140]), array([200, 100, 100]))
# (array([ 58, 193, 230]), array([  0, 255, 255]))
# (array([ 39,  87, 174]), array([125, 125, 125]))
# (array([ 88,  81, 165]), array([125, 125, 125]))
# (array([25, 77, 72]), array([  0, 150,  80]))

我们可以使用 Cython-powered kd-tree for quick nearest-neighbor lookup 从而实现我们的 classification/bucketing -

from scipy.spatial import cKDTree

# Input image : img
out_img = colors[cKDTree(colors).query(img,k=1)[1]]

该问题不仅要求找到最近的邻居(其他答案提供),而且还要求如何有效地应用超过 30000 张图像的交换。

性能提升:

不是计算每个图像的每个像素的距离 (30000*1024*1024 = 31457280000),而是为调色板上的每种可能颜色计算一次映射。

然后使用该映射交换像素。

import numpy as np
import itertools as it
import scipy.spatial.distance

palette  = np.array([[0, 0, 0], 
[0, 0, 255], 
[255, 0, 0], 
[150, 30, 150], 
[255, 65, 255], 
[150, 80, 0], 
[170, 120, 65], 
[125, 125, 125], 
[255, 255, 0], 
[0, 255, 255], 
[255, 150, 0], 
[255, 225, 120], 
[255, 125, 125], 
[200, 100, 100], 
[0, 255, 0], 
[0, 150, 80], 
[215, 175, 125], 
[220, 180, 210], 
[125, 125, 255]
])

valueRange = np.arange(0,256)
allColors = np.array(list(it.product(valueRange,valueRange,valueRange)))
mapping = scipy.spatial.distance.cdist(allColors, palette).argmin(1)

另外推荐

的讲座