在Python中找到图像像素和调色板颜色之间差异的最快方法

时间:2017-01-25 21:34:04

标签: python python-2.7 numpy image-processing optimization

我正在研究一些用于将图像转换为NES调色板的代码。我目前的代码有些成功,但非常慢。

我是通过使用毕达哥拉斯'定理。我使用RGB颜色值作为3D空间中的坐标并以此方式进行。调色板中与像素的RGB距离最小的颜色是使用的颜色。

class image_filter():
    def load(self,path):
        self.i = Image.open(path)
        self.i = self.i.convert("RGB")
        self.pix = self.i.load()

    def colour_filter(self,colours=NES):
        start = time.time()
        for y in range(self.i.size[1]):
            for x in range(self.i.size[0]):
                pixel = list(self.pix[x,y])
                distances = []
                for colour in colours:
                    distance = ((colour[0]-pixel[0])**2)+((colour[1]-pixel[1])**2)+((colour[2]-pixel[2])**2)
                    distances.append(distance)
                pixel = colours[distances.index(sorted(distances,key=lambda x:x)[0])]
                self.pix[x,y] = tuple(pixel)
        print "Took "+str(time.time()-start)+" seconds."

f = image_filter()
f.load("C:\\path\\to\\image.png")
f.colour_filter()
f.i.save("C:\\path\\to\\new\\image.png")

使用清单:

NES = [(124,124,124),(0,0,252),
           (0,0,188),(68,40,188),
           (148,0,132),(168,0,32),
           (168,16,0),(136,20,0),
           (80,48,0),(0,120,0),
           (0,104,0),(0,88,0),
           (0,64,88),(0,0,0),
           (188,188,188),(0,120,248),
           (0,88,248),(104,68,252),
           (216,0,204),(228,0,88),
           (248,56,0),(228,92,16),
           (172,124,0),(0,184,0),
           (0,168,0),(0,168,68),
           (0,136,136),(248,248,248),
           (60,188,252),(104,136,252),
           (152,120,248),
           (248,120,248),(248,88,152),
           (248,120,88),(252,160,68),
           (184,248,24),(88,216,84),
           (88,248,152),(0,232,216),
           (120,120,120),(252,252,252),(164,228,252),
           (184,184,248),(216,184,248),
           (248,184,248),(248,164,192),
           (240,208,176),(252,224,168),
           (248,216,120),(216,248,120),
           (184,248,184),(184,248,216),
           (0,252,252),(216,216,216)]

这会产生以下输入:

img

和输出:

img

这需要14到20秒,这对于预期的应用来说太长了。有谁知道有什么方法可以大大加快这个速度?

作为一个想法,我认为有可能为此使用numpy数组;但是我对numpy数组一点都不熟悉,无法将它拉下来。

如果可能的话,我也想尝试避免使用scipy - 我知道,至少在Windows下,安装起来很麻烦,而且更愿意避开。

1 个答案:

答案 0 :(得分:3)

<强>方法#1:我们可以使用Scipy's cdist来获得欧几里德距离,然后寻找最小距离Arg和由此选择适当的颜色

因此,以NumPy数组作为输入,我们将有一个类似的实现 -

from scipy.spatial.distance import cdist

out = colours[cdist(pix.reshape(-1,3),colours).argmin(1)].reshape(pix.shape)

方法#2:这是broadcastingnp.einsum的另一种方法 -

subs = pix - colours[:,None,None]
out = colours[np.einsum('ijkl,ijkl->ijk',subs,subs).argmin(0)]

PIL /列表和NumPy阵列之间的接口

要接受通过PIL读取的图像,请使用:

pix = np.asarray(Image.open('input_filename'))

使用colours作为数组:

colours = np.asarray(NES)

# .... Use one of the listed approaches and get out as output array

输出图像:

i = Image.fromarray(out.astype('uint8'),'RGB')
i.save("output_filename")

使用给定调色板NES -

输出样本

enter image description here

enter image description here