2017-01-25 23 views
4

我正在研究將圖像轉換爲NES調色板的一些代碼。我目前的代碼有點成功,但非常慢。查找Python中圖像像素和調色板顏色差異的最快方法

我正在使用畢達哥拉斯定理。我將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下,它可能是一個痛苦的安裝,並希望轉向明確。

+0

這將是很難測試不完整的代碼。這是否過長?我看到許多東西會在這裏,三個嵌套for循環,追加被稱爲操作過過,分選和最後它不是C –

+0

@ MS-DDOS這幾乎是完整的代碼。我認爲可能唯一需要引用但沒有提供的是'NES'列表。我現在補充一下。 –

+0

謝謝,應該有所幫助。但我真的很好奇成員數據,比如'self.i'和'self.pix',它們的類型是什麼?你正在使用任何庫,或者這只是純粹的Python? –

回答

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") 

樣品輸入,使用輸出GI VEN調色板NES -

enter image description here

enter image description here

+0

你能想到的任何方法那不涉及scipy?我知道這是一個痛苦的安裝。 –

+0

關於方法2 - 尋找廣播,這似乎是一個很好的計劃。我需要以什麼方式輸入圖像才能使用此方法?目前我將它作爲PIL RGB圖像進行存儲。我將在圖像中添加當前用於加載的代碼以供您查看。 –

+0

@ WORD_559猜你可以這樣做:'np.asarray(PIL.Image.open('pic1.jpg'))'把'pix'作爲NumPy數組,其中'pic1.jpg'是輸入文件名和'顏色':colors = np.asarray(NES)? – Divakar