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