По сути для каждой точки изображения надо найти ближайший цвет из палитры. Вот решение "в лоб":
import numpy as np
a, b, c = np.random.randint(0, 256, 3, dtype=np.uint8)
palette = [ [a, b, c], [a, a, b], [c, a, b], [b, a, c], [c, c, a] ]
rgb_img = [
[ (a+3, b-2, c-1), (a, a+1, b-2), (c+2, a, b-3) ],
[ (b, a-2, c+1), (c, c+2, a-1), (b-1, a, c-3) ],
[ (a, a+1, b-2), (a+2, b-2, c-1), (c+1, c+1, a-2) ]
]
palette = np.array(palette).reshape(1, 1, 5, 3)
rgb_img = np.array(rgb_img).reshape(3, 3, 1, 3)
indices = np.linalg.norm(palette - rgb_img, axis=-1).argmin(axis=-1).ravel()
print(indices)
Результат:
[0 1 2 3 4 3 1 0 4]
Ещё вариант:
from sklearn.cluster import KMeans
a, b, c = np.random.randint(0, 256, 3, dtype=np.uint8)
palette = [ [a, b, c], [a, a, b], [c, a, b], [b, a, c], [c, c, a] ]
rgb_img = [
[ (a+3, b-2, c-1), (a, a+1, b-2), (c+2, a, b-3) ],
[ (b, a-2, c+1), (c, c+2, a-1), (b-1, a, c-3) ],
[ (a, a+1, b-2), (a+2, b-2, c-1), (c+1, c+1, a-2) ]
]
result = (
KMeans(n_clusters=len(palette), init=np.array(palette), n_init=1)
.fit_transform(np.array(rgb_img).reshape(-1, 3))
.argmin(axis=-1)
)
UPD Пример оригинальной картинки и преобразованной в значения [0, 32, 64, 96, 128, 160, 192, 224, 255]: