2017-06-01 55 views
0

我試圖用每種可能的顏色創建圖像。它將從種子像素開始,然後在其周圍放置隨機生成的RGB像素。未來的展示位置將基於哪個開放位置的周圍像素的平均值最接近要放置的新顏色。需要增加圖像創建的速度

from PIL import Image 
import numpy as np 
from random import randint 
import sys 
import random 
import itertools 

sys.setcheckinterval(10000) 

def moddistance3(x1,y1,z1,x2,y2,z2): #get relative distance between two 3D points 
    x = abs(x1 - x2) 
    y = abs(y1 - y2) 
    z = abs(z1 - z2) 
    return (x + y + z) 

def genColor(unused): #generate random color (not used anymore) 
    test = 0 
    while test == 0: 
     red = randint(0,255) 
     green = randint(0,255) 
     blue = randint(0,255) 
     if unused[red,green,blue] == 1: 
      test = 1 
    return (red,green,blue) 

def surroundAvg(points,unfilled): 
    surrounding = {} 
    count = len(points) 
    for inc in xrange(count): 
     neighbors = filledNeighbors(points[inc][0],points[inc][1],unfilled) 
     nearcount = len(neighbors) 
     pixred = 0 
     pixgreen = 0 
     pixblue = 0 
     for num in xrange(nearcount): 
      (temp_red,temp_green,temp_blue) = pixels[neighbors[num][0],neighbors[num][1]] 
      pixred = pixred + temp_red 
      pixgreen = pixgreen + temp_green 
      pixblue = pixblue + temp_blue 
     pixred = pixred/nearcount 
     pixgreen = pixgreen/nearcount 
     pixblue = pixblue/nearcount 
     surrounding[(points[inc][0],points[inc][1])] = (pixred,pixgreen,pixblue) 
    return surrounding 

def genPoint(perim,unfilled,averages,red,green,blue): 
    num_test = len(perim) 
    test = 0 
    least_diff = 9999 
    nearby = [] 
    for point in xrange(num_test): 
     i = perim[point][0] 
     j = perim[point][1] 
     pixred = averages[(i,j)][0] 
     pixgreen = averages[(i,j)][1] 
     pixblue = averages[(i,j)][2] 
     diff = abs(red - pixred) + abs(green - pixgreen) + abs(blue - pixblue) 
     if diff < least_diff or test == 0: 
      least_diff = diff 
      newx = i 
      newy = j 
      test = 1 
    return newx,newy 

def cubegen(): #create the cube of colors with each color having its own number 
    cube = np.zeros(16777216,dtype=np.object) 
    num = 0 
    for red in xrange(0,256): 
     for green in xrange(0,256): 
      for blue in xrange(0,256): 
       cube[num] = [red,green,blue] 
       num += 1 
    return cube 

def getNeighbors(x,y,unfilled): 
    Prod = itertools.product 
    toremove = [] 
    neighbors = list(Prod(range(x-1,x+2),range(y-1,y+2))) 
    for num in xrange(len(neighbors)): 
     i,j = neighbors[num] 
     if j > 4095 or i > 4095 or unfilled[(i,j)] == 0 or j < 0 or i < 0: 
      toremove.append((i,j)) 
    map(neighbors.remove,toremove) 
    return neighbors 

def filledNeighbors(x,y,unfilled): 
    Prod = itertools.product 
    toremove = [] 
    neighbors = list(Prod(range(x-1,x+2),range(y-1,y+2))) 
    #neighbors = filter(lambda i,j: j < 4096 and i < 4096 and unfilled[i,j] == 0 and j > -1 and i > -1,allneighbors) 
    for num in xrange(len(neighbors)): 
     i,j = neighbors[num] 
     if j > 4095 or i > 4095 or unfilled[(i,j)] == 1 or j < 0 or i < 0: 
      toremove.append((i,j)) 
    map(neighbors.remove,toremove) 
    return neighbors 

img = Image.new('RGB', (4096,4096)) # create a new black image 
pixels = img.load() # create the pixel map 

colorList = range(16777216) 
colorCube = cubegen() 
print("Color cube created successfully") 
unfilled = {} 
for x in xrange(4096): 
    for y in xrange(4096): 
     unfilled[(x,y)] = 1 
startx = 2048 
starty = 2048 
random.shuffle(colorList) 
print("Color list shuffled successfully") 
color = colorList[0] 
(red,green,blue) = colorCube[color] 
pixels[startx,starty] = (red,green,blue) 
unfilled[(startx,starty)] = 0 
perim_empty = getNeighbors(startx,starty,unfilled) 
edge = [] 
#edge.append((startx,starty)) 
avg = surroundAvg(perim_empty,unfilled) 
print("First point placed successfully.") 
#appendEdge = edge.append 
#removeEdge = edge.remove 
appendPerim = perim_empty.append 
removePerim = perim_empty.remove 
updateAvg = avg.update 


for iteration in xrange(1,16777216): 
    temp = {} 
    color = colorList[iteration] 
    (red,green,blue) = colorCube[color] 
    (i,j) = genPoint(perim_empty,unfilled,avg,red,green,blue) 
    unfilled[(i,j)] = 0 
    pixels[i,j] = (red,green,blue) 
    new_neighbors = getNeighbors(i,j,unfilled) 
    map(appendPerim,new_neighbors) 
    temp = surroundAvg(new_neighbors,unfilled) 
    updateAvg(temp) 
    removePerim((i,j)) 
    #appendEdge((i,j)) 

    #if iteration % 20 == 0: 
    # toremove = [] 
    # appendToRemove = toremove.append 
    # for num in xrange(len(edge)): 
    #  nearby = getNeighbors(edge[num][0],edge[num][1],unfilled) 
    #  if len(nearby) == 0: 
    #   appendToRemove(edge[num]) 
     #for num in xrange(len(toremove)): 
     # edge.remove(toremove[num]) 
    # map(removeEdge,toremove) 

    if iteration % 500 == 0: 
     print("Iteration %d complete" %iteration) 
    if iteration == 100000 or iteration == 500000 or iteration ==1000000 or iteration == 5000000 or iteration == 10000000 or iteration == 15000000: 
     img.save("Perimeter Averaging -- %d iterations.bmp" %iteration) 
img.save("Perimeter Averaging Final.bmp") 
img.show() 

的問題是,當我嘗試運行此,它需要花費數天甚至經過顏色的1,000,000,減慢顯着,因爲它去。我無法弄清楚如何讓它花費更少的時間,而且我知道必須有一種方法可以做到這一點,但這不會花費數月時間。我是新來的代碼,並且在自學,所以請原諒我完全忽略的任何明顯的修復。

+0

,而不必專門看了到你的代碼中,你有沒有考慮'cython',定義它還是使用像'numba'這樣的JIT編譯器? – salient

+0

你說得對,這樣可以更快運行。我猜想從函數到函數的龐大字典都可能是一個很大的瓶頸。這個程序在進入迭代部分之前會消耗大量內存。肯定有一些地方你可以更有效地處理。如果我有時間的話,我今天晚上會列出一張名單。 – BHawk

+0

無論何時試圖加快代碼速度,最好將其分析以確定其花費的大部分時間在哪裏......因此您在此知道要花費大部分時間來優化它。請參閱[** _如何配置python腳本?_ **](https://stackoverflow.com/questions/582336/how-can-you-profile-a-python-script)這就是說,通常答案是完全使用不同的算法並避免瓶頸,無論它是什麼。 – martineau

回答

0

好的,我花了一些時間看這個,我做了一些改變以加快速度。我非常喜歡你在這個程序中實現的想法,並且輸出很可愛。我可能會做的事情從一開始就有點不同,但事實上,我已經將它放在了您所呈現的結構中,並進行了一些調整。這絕不代表代碼可能是最快或最有效的,但它足以讓我滿意加速。 Note - 在那裏我可能會用到一些錯誤的打印語句來理解代碼中發生了什麼。希望能幫助到你。

  1. 我清理了代碼,刪除了任何未使用的代碼,並更改了通過某些對象循環的方式。這些風格變化比其他任何事情都多。

  2. 我將您的顏色立方體從numpy數組更改爲元組,因爲np數組不是必需的。我認爲立方體現在生成速度更快,但我沒有做任何測試來確認。

  3. 重要的是要理解,鑑於您希望處理顏色的方式,隨着「邊界」候選項數量的增長,此算法將隨着時間的推移而減慢。當我第一次運行代碼時,由於座標沒有被正確刪除,我得到了外圍列表大小的巨大增長。我無法追查爲什麼發生這種情況,所以我在程序中添加了額外的檢查以避免覆蓋已寫入的任何像素。如果發現某個點不應該是邊界列表的成員,我還在一些冗餘點刪除中添加。這對我的系統有很大的幫助,儘管錯誤的來源仍然沒有解決。

  4. 爲了進一步解決與周長成比例增長的處理時間問題,我在genPoint中引入了一個閾值。這允許該功能在「足夠接近」到良好位置時退出。這樣做的質量很高,所以如果您想要更高質量的圖像,您可以將速度設置得更高或更低。這是一個惡魔的討價還價,但另一種選擇是從未完成的程序。當我第一次運行這個腳本時,我的電腦花了大約14秒來完成迭代14000-14500。由於目前正在運行,我正在迭代2,330,000次,每500次迭代需要2-3秒。

  5. 我擺脫了未填充的字典,而是直接從函數引用圖像。不是我怎麼會喜歡這樣做,但它簡化了代碼並擺脫了大內存塊。

  6. 另一個更明顯的方式加快這將是多線程的迭代的genPoint()函數,但我不願意花時間做它的一部分,因爲它將需要對代碼進行重大修改。爲了使其工作起來,你將不得不實施許多線程鎖,但這會使其更快。

-

from PIL import Image 
import sys 
import random 
import itertools 
import datetime as dt 


#sys.setcheckinterval(10000) 

def surroundAvg(points): 
    surrounding = {} 
    for inc in points: 
     neighbors = filledNeighbors(inc[0],inc[1]) 
     nearcount = len(neighbors) 
     pixred = 0 
     pixgreen = 0 
     pixblue = 0 
     for n in neighbors: 
      (temp_red,temp_green,temp_blue) = pixels[n[0],n[1]] 
      pixred += temp_red 
      pixgreen += temp_green 
      pixblue += temp_blue 
     pixred = pixred/nearcount 
     pixgreen = pixgreen/nearcount 
     pixblue = pixblue/nearcount 
     surrounding[(inc[0],inc[1])] = (pixred,pixgreen,pixblue) 
    return surrounding 

def genPoint(perim,averages,red,green,blue): 
    test = 0 
    least_diff = 9999 
    threshold = 35 
    for point in perim: 
     i = point[0] 
     j = point[1] 
     if pixels[i,j] == (0,0,0): 
      pixred = averages[(i,j)][0] 
      pixgreen = averages[(i,j)][1] 
      pixblue = averages[(i,j)][2] 
      diff = abs(red - pixred) + abs(green - pixgreen) + abs(blue - pixblue) 
      if diff < least_diff or test == 0: 
       least_diff = diff 
       newx = i 
       newy = j 
       test = 1 
       if least_diff < threshold: 
        return newx,newy,perim 
     else: 
      perim.pop(perim.index(point)) 
    return newx,newy,perim 

def cubegen(): #create the cube of colors with each color having its own number 
    cube = [] 
    num = 0 
    for red in xrange(0,256): 
     for green in xrange(0,256): 
      for blue in xrange(0,256): 
       cube.append((red,green,blue)) 
       num += 1 
    return tuple(cube) 

def getNeighbors(x,y): 
    Prod = itertools.product 
    toremove = [] 
    neighbors = list(Prod(range(x-1,x+2),range(y-1,y+2))) 
    for num in xrange(len(neighbors)): 
     i,j = neighbors[num] 
     if j > 4095 or i > 4095 or pixels[i,j] != (0,0,0) or j < 0 or i < 0: 
      toremove.append((i,j)) 
    map(neighbors.remove,toremove) 
    return neighbors 

def filledNeighbors(x,y): 
    Prod = itertools.product 
    toremove = [] 
    neighbors = list(Prod(range(x-1,x+2),range(y-1,y+2))) 
    for num in xrange(len(neighbors)): 
     i,j = neighbors[num] 
     if j > 4095 or i > 4095 or pixels[i,j] == (0,0,0) or j < 0 or i < 0: 
      toremove.append((i,j)) 
    map(neighbors.remove,toremove) 
    return neighbors 

img = Image.new('RGB', (4096,4096)) # create a new black image 
pixels = img.load() # create the pixel map 

print("Making list") 
colorList = range(16777216) 
random.shuffle(colorList) 
print("Color list shuffled successfully") 

print("Making cube") 
colorCube = cubegen() 
print("Color cube created successfully") 

startx = 2048 
starty = 2048 
color = colorList[0] 

(red,green,blue) = colorCube[color] 
#start with a random color 
pixels[startx,starty] = (red,green,blue) 

#get it's neighboring pixels 
perim_empty = getNeighbors(startx,starty) 

#calc avg values (original pixel) 
avg = surroundAvg(perim_empty) 
print("First point placed successfully.") 
appendPerim = perim_empty.append 
removePerim = perim_empty.remove 
updateAvg = avg.update 

start = dt.datetime.now() 
for iteration in xrange(1,16777216): 
    temp = {} 
    color = colorList[iteration] 
    (red,green,blue) = colorCube[color] 
    i,j,perim_empty = genPoint(perim_empty,avg,red,green,blue) 
    pixels[i,j] = (red,green,blue) 
    new_neighbors = getNeighbors(i,j) 
    map(appendPerim,new_neighbors) 
    temp = surroundAvg(new_neighbors) 
    updateAvg(temp) 
    removePerim((i,j)) 
    for p in perim_empty: 
     if p[0] == i and p[1] == j: 
      perim_empty.remove(p) 

    if iteration % 1000 == 0: 
     end = dt.datetime.now() 
     print("Iteration %d complete: %f" %(iteration,(end-start).total_seconds())) 
     print("Perimeter size: %d"%len(perim_empty)) 
     print("Averages size: %d"%sys.getsizeof(avg)) 
     start = dt.datetime.now() 
     img.save("%06d.png" % (iteration/1000)) 

img.save("Perimeter Averaging Final.bmp") 
img.show() 

編輯:忘了包括我目前的圖像(還在不斷增加): cropped output image