Map colors in image to closest member of a list of colors, in Python
We can use Cython-powered kd-tree
for quick nearest-neighbor lookup and hence achieve our classification/bucketing -
from scipy.spatial import cKDTree
# Input image : img
out_img = colors[cKDTree(colors).query(img,k=1)[1]]
The question not only asks for finding the nearest neighbor - which the other answers provide - but how to efficiently apply the exchange over 30000 images.
Performance improvement:
Instead of computing the distance per pixel per image (30000*1024*1024 = 31457280000), compute a mapping once for each possible color onto your palette.
Then use that mapping to exchange the pixels.
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)
In addition I recommend the lecture of Creating fast RGB look up tables in Python