我尝试了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])
在这台 2015 i5 笔记本电脑上使用 2500x 2500 像素的 PNG,我看到:
$ /usr/bin/time -f %M:%e ./replace-pyvips.py ~/pics/x.png y.png
55184:0.92
因此,最大内存为 55mb,经过时间为 0.92 秒。
我尝试了 Quang Hoang 的优秀 numpy 版本进行比较:
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 2500 像素图像上运行:
$ /usr/bin/time -f %M:%e ./replace-broadcast.py ~/pics/x.png y.png
413504:3.08
410MB内存的峰值,3.1s过去了。
正如 Hoang 所说,通过比较 uint32 可以进一步加快这两个版本的速度。