2017-06-01 5 views
0

Ich versuche, ein Bild mit jeder möglichen Farbe zu erstellen. Es würde mit einem Startpixel beginnen und dann zufällig erzeugte RGB-Pixel um es herum platzieren. Zukünftige Placements würden auf dem offenen Punkt basieren, bei dem der Durchschnitt der Pixel, die ihn am nächsten bei der neuen einzufügenden Farbe umgaben.Notwendigkeit, die Geschwindigkeit der Bilderstellung zu erhöhen

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

Das Problem ist, dass wenn ich versuche, dies zu laufen, es Tage dauert sogar bis 1.000.000 der Farben zu gehen, und verlangsamt sich deutlich, wie es geht. Ich kann mir nicht vorstellen, wie ich dafür sorgen kann, dass es weniger Zeit braucht, und ich weiß, dass es einen Weg geben muss, dies zu tun, der nicht Monate dauert. Ich bin neu im Code und unterrichte mich selbst, also bitte vergib mir alle offensichtlichen Fehler, die ich völlig übersehen habe.

+0

ohne in Ihren Code speziell sah zu haben, haben Sie darüber nachgedacht, 'cython'-izing oder mit einem JIT-Compiler wie 'numba'? – salient

+0

Sie haben Recht, das könnte viel schneller laufen. Ich denke, dass das Werfen des massiven Wörterbuchs von Funktion zu Funktion wahrscheinlich ein ziemlich großer Flaschenhals ist. Dieses Programm verbraucht jede Menge Speicher, bevor es überhaupt in den iterativen Bereich gelangt. Es gibt definitiv einige Bereiche, die Sie effizienter bewältigen könnten. Ich werde versuchen, heute Abend eine Liste zu machen, wenn ich Zeit habe. – BHawk

+0

Immer wenn Sie versuchen, Ihren Code zu beschleunigen, ist es eine gute Idee, ihn zu profilieren, um festzustellen, wo er die meiste Zeit verbringt ... also wissen Sie, dass er die meisten von Ihnen für die Optimierung ausgeben kann. Siehe [** _ Wie können Sie ein Python-Skript profilieren? _ **] (https://stackoverflow.com/questions/582336/how-can-you-profile-a-python-script) Das heißt, oft ist die Antwort Verwenden Sie einen ganz anderen Algorithmus und vermeiden Sie den Engpass, was auch immer es ist. – martineau

Antwort

0

Okay, ich habe mich einige Zeit damit beschäftigt und einige Änderungen vorgenommen, um die Dinge zu beschleunigen. Ich mag die Idee, die Sie in diesem Programm implementieren, und die Ergebnisse sind sehr schön. Ich hätte die Dinge wahrscheinlich etwas anders gemacht als am Anfang, aber so wie ich es gemacht habe, habe ich es in der Struktur belassen, in der du es präsentiert hast, mit ein paar Verbesserungen. Dies stellt keineswegs den schnellsten oder effizientesten Code dar, aber es war genug, um mich von der Geschwindigkeit zu überzeugen. Note - Möglicherweise gibt es einige fehlerhafte Druckanweisungen, die ich verwendet habe, um zu verstehen, was im Code vor sich ging. Ich hoffe es hilft.

  1. Ich bereinigte den Code, um alles zu entfernen, was nicht verwendet wurde, und um die Art und Weise zu ändern, in der Sie einige Objekte durchlaufen. Das waren Stiländerungen mehr als alles andere.

  2. Ich habe Ihren Farbwürfel von einem numpy-Array in ein Tupel geändert, weil das np-Array nicht notwendig war. Ich denke, der Würfel wird jetzt schneller generiert, aber ich habe noch keine Tests durchgeführt, um dies zu bestätigen.

  3. Es ist wichtig zu verstehen, dass angesichts der Art, wie die Farben verarbeitet werden sollen, dieser Algorithmus im Laufe der Zeit langsamer wird, wenn die Anzahl der "Perimeter" -Kandidaten wächst. Als ich den Code zum ersten Mal ausführte, wurde die Größe der Umkreisliste massiv vergrößert, da die Koordinaten nicht richtig entfernt wurden. Ich konnte nicht herausfinden, warum es so war, also fügte ich zusätzliche Überprüfungen in das Programm ein, um zu vermeiden, dass bereits geschriebene Pixel überschrieben wurden. Ich fügte auch eine überflüssige Punktentfernung hinzu, wenn ein Punkt gefunden wurde, der kein Mitglied der Umkreisliste sein sollte. Das hat die Dinge auf meinem System ziemlich beschleunigt, obwohl mir die Quelle des Bugs noch immer entgeht.

  4. Um das Problem der Bearbeitungszeiten, die proportional zur Perimetergröße wachsen, weiter zu bekämpfen, habe ich einen Grenzwert in genPoint eingeführt. Dadurch kann die Funktion beendet werden, wenn sie einem guten Standort "nahe genug" ist. Dies ist ein Qualitätskriterium, so dass Sie den Wert höher einstellen können, wenn Sie eine höhere Geschwindigkeit wünschen, oder niedriger, wenn Sie ein Bild mit höherer Qualität wünschen. Es ist ein Schnäppchen für den Teufel, aber die Alternative ist ein Programm, das niemals abgeschlossen wird. Als ich das Skript zum ersten Mal ausgeführt habe, brauchte mein Computer etwa 14 Sekunden, um die Iterationen 14000-14500 zu beenden. Wie es derzeit läuft, bin ich bei Iteration 2.330.000 und es dauert etwa 2-3 Sekunden pro 500 Iterationen.

  5. Ich habe das ungefüllte Wörterbuch losgeworden und stattdessen das Bild direkt aus den Funktionen referenziert. Nicht, wie ich es vorgezogen hätte, aber es hat den Code vereinfacht und den großen Speicherblock losgeworden.

  6. Ein weiterer, offensichtlicher Weg, dies die Iterationen oder die GenPoint() Funktion Multi-Thread wäre zu beschleunigen, aber ich bin nicht bereit, in der Zeit zu setzen, dass ein Teil davon, wie es zu tun würde eine wesentliche Überarbeitung des Codes erfordern. Es würde viele Thread-Sperren geben, die Sie implementieren müssten, damit es funktioniert, aber es würde es schneller machen.

-

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

Edit: vergaß mein aktuelles Bild aufzunehmen (immer noch wachsende): cropped output image

Verwandte Themen