Я пошел с pyvips .Это многопоточная, потоковая библиотека обработки изображений, поэтому она быстрая и не требует большого объема памяти.
import sys
import pyvips
from functools import reduce
# Keep only these colors in the image, otherwise replace with (0,255,0,255)
palette = [[0,0,0,255], [0, 255, 0,255], [255, 0, 0,255], [128, 128, 128,255], [0, 0, 255,255], [255, 0, 255,255], [0, 255, 255,255], [255, 255, 255,255], [128, 128, 0,255], [0, 128, 128,255], [128, 0, 128,255]]
im = pyvips.Image.new_from_file(sys.argv[1], access="sequential")
# test our image against each sample ... bandand() will AND all image bands
# together, ie. we want pixels where they all match
masks = [(im == colour).bandand() for colour in palette]
# OR all the masks together to find pixels which are in the palette
mask = reduce((lambda x, y: x | y), masks)
# pixels not in the mask become [0, 255, 0, 255]
im = mask.ifthenelse(im, [0, 255, 0, 255])
im.write_to_file(sys.argv[2])
С 2500x 2500 пикселями PNG на этом ноутбуке i5 2015 года я вижу:
$ /usr/bin/time -f %M:%e ./replace-pyvips.py ~/pics/x.png y.png
55184:0.92
Таким образом, максимум 55 МБ памяти и 0,92 с истекшего времени.
Я попробовал отличную версию Nangy Quang Hoang для сравнения:
p = np.array(palette).transpose()
# mask
# all(2) force all channels to be equal
# any(-1) matches any color
mask = (a[:,:,:, None] == p).all(2).any(-1)
# replace color
rep_color = np.array([0,255,0,255])
# np.where to the rescue:
a = np.where(mask[:,:,None], a, rep_color[None,None,:])
im = Image.fromarray(a.astype('uint8'))
im.save(sys.argv[2])
Запуск на тех же 2500 x 2500pixel image:
$ /usr/bin/time -f %M:%e ./replace-broadcast.py ~/pics/x.png y.png
413504:3.08
Пик 410 МБ памяти и 3,1 с.
Обе версии можно ускорить, сравнив uint32, как говорит Хоанг.