2015-05-29 9 views
7

Ich arbeite an einem Projekt, wo ich ein Bild von einem farbigen Gitter als Eingabe (in diesem Beispiel mit Legosteinen gemacht) machen und ein viel kleineres modifiziertes Bild zurückgeben möchte. HierPixelate Bild mit Kissen

ist ein Beispiel Eingabe:

Input

Unten ist ein sehr kleines 8x8 Bild, das das Ergebnis sein würde:

Output

Hier ist eine viel größere Version des erwarteten Ergebnisses::

Big Output

Hier ist mein Code so weit: Es nur mit Schwarz-Weiß-Bilder funktioniert.

from PIL import Image 
import re 

black = [(110,110,110),(0,0,0)] #The highest value and the lowest RGB value for the color black 

img = Image.open("input.jpg") #The input image 
size = (8,8) #The dimensions of the output image 

out = img.resize(size,resample=Image.LANCZOS) #Resize the image 

for y in range(size[0]): #loop through every pixel 
    for x in range(size[1]): 

     if out.getpixel((x,y)) <= black[0] and out.getpixel((x,y)) >= black[1]: #check to see if the pixel is within the accepted black values 

      out.putpixel((x,y), (0,0,0)) #Give the current pixel true color 
     else: 
      #otherwise make the pixel black 
      out.putpixel((x,y), (255,255,255)) #Give the current pixel true color 

"""Save the pixelated image""" 
out.save("output.jpg") 

Und die Ausgabe von meinem Code zurückgegeben:

Actual Output

Mein Programm arbeitet für Schwarzweiß-Bilder in Ordnung, aber ich brauche Hilfe zu ändern es mit verschiedenen Farben (rot zu arbeiten, orange, gelb, hellgrün, dunkelgrün, hellblau, dunkelblau, lila, schwarz und weiß).

Vielen Dank im Voraus!

+0

Nicht Thumbnail tun, was Sie wollen? –

Antwort

7

Sie machen ein paar Dinge falsch.

Zuerst sollten Sie PNG, nicht JPG für Ihre Ausgabe verwenden. JPG führt so viele Artefakte ein, dass kleine Bilder wie deine Ausgabe komplett degeneriert werden.

Dann sollten Sie Ihre Palette reduzieren. Es ist viel einfacher, mit Eingaben zu arbeiten, die kein Rauschen enthalten.

Zunächst einmal langweilig Initialisierung:

from PIL import Image 
import operator 
from collections import defaultdict 
import re 

input_path = 'input.jpg' 
output_path = 'output.png' 
size = (4,4) 

Dann erklären wir die Palette - diese Farben aller möglichen LEGO Steine ​​enthalten sollte. Ich probierte die folgenden Werte aus dem Bild, aber Sie können Schwarz-Weiß verwenden, wie Sie in Ihrem Code zu tun, oder irgendwelche Farben wünschen Sie, solange sie zu Farben im Quellbild ähnlich sind:

palette = [ 
    (45, 50, 50), #black 
    (240, 68, 64), #red 
    (211, 223, 223), #white 
    (160, 161, 67), #green 
    (233, 129, 76), #orange 
] 
while len(palette) < 256: 
    palette.append((0, 0, 0)) 

Die Code unten wird Palette für PIL erklären, da PIL flache Anordnung benötigt, anstatt Array von Tupeln:

flat_palette = reduce(lambda a, b: a+b, palette) 
assert len(flat_palette) == 768 

Jetzt können wir ein Bild erklären, dass die Palette halten wird. Wir verwenden es, um die Farben später vom Originalbild zu reduzieren.

palette_img = Image.new('P', (1, 1), 0) 
palette_img.putpalette(flat_palette) 

Hier öffnen wir das Bild und quantisieren es. Wir skalieren es achtmal größer als nötig, da wir später die durchschnittliche Ausgabe testen werden.

multiplier = 8 
img = Image.open(input_path) 
img = img.resize((size[0] * multiplier, size[1] * multiplier), Image.BICUBIC) 
img = img.quantize(palette=palette_img) #reduce the palette 

Danach sieht unser Bild wie folgt aus:

quantized image

Wir müssen es konvertieren zurück zu RGB, so dass wir Pixel probieren jetzt können:

img = img.convert('RGB') 

wir jetzt Wir werden unser letztes Bild aufbauen. Um dies zu tun, probieren wir aus, wie viele Pixel jeder Palette jedes Quadrat im größeren Bild enthält. Dann wählen wir die Farbe, die am häufigsten vorkommt.

out = Image.new('RGB', size) 
for x in range(size[0]): 
    for y in range(size[1]): 
     #sample at get average color in the corresponding square 
     histogram = defaultdict(int) 
     for x2 in range(x * multiplier, (x + 1) * multiplier): 
      for y2 in range(y * multiplier, (y + 1) * multiplier): 
       histogram[img.getpixel((x2,y2))] += 1 
     color = max(histogram.iteritems(), key=operator.itemgetter(1))[0] 
     out.putpixel((x, y), color) 

Schließlich sparen wir die Ausgabe:

out.save(output_path) 

Das Ergebnis:

small image

von 1600% Gehoben:

big image

+0

Hallo, ich bin gerade dazu gekommen, den Code zu benutzen, und er wirft mir einige Fehler auf, die aussehen, als wären sie mit der Version von Python verwandt. Ich benutze Python 3.4.2. In welcher Version wurde dieser Code geschrieben? –

+0

Python 3.4.3, aber ich benutze Kissen statt PIL (es ist eine aktivere Gabel). Es könnte minimale API-Änderungen geben. –

+1

Danke für die Info. Ich bin froh zu sagen, dass ich jetzt alles funktioniert habe. –

3

Nur zum Spaß, habe ich das mit ImageMagick - die auch von Python aufrufbar ist ...

angepackt, zunächst einmal, ich kreiere eine kleine benutzerdefinierte Palette, um Ihre Farben entsprechen - Ihr Weiß ist nicht sehr weiß und Ihr Grün ist anders von ImageMagick's Idee von Grün, also habe ich hex für sie anstelle von Farbnamen verwendet.

convert xc:black xc:red xc:"rgb(200,200,200)" xc:"rgb(168,228,23)" xc:orange +append palette.png 

Wenn ich, dass Palette nach oben skalieren, sieht es wie folgt aus:

enter image description here

Dann habe ich bis zu 4x4 Ihr Bild die Größe und das Ergebnis an die eigene Palette abbilden und nach oben zurückzufahren, so Sie können es so sehen:

convert lego.jpg -resize 4x4! +dither -remap palette.png -scale 1600 result.png 

und hier ist das Ergebnis

enter image description here

Das Weiß ist aus, um das "weiße" in Ihrem Original zu entsprechen.