2015-05-29 14 views
7

Pracuję nad projektem, w którym chcę zrobić zdjęcie kolorowej siatki jako dane wejściowe (wykonane z klocków Lego w tym przykładzie) i zwrócić znacznie mniejszy zmodyfikowany obraz.Pixelate Image With Pillow

Oto przykład wejście:

Input

Poniżej jest bardzo mały 8x8 obraz, który byłby wynikiem:

Output

Tutaj jest znacznie większa wersja oczekiwanego wyniku::

Big Output

Oto mój kod do tej pory: Działa tylko z czarno-białych obrazów.

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

a wyjście zwrócony przez mojego kodu:

Actual Output

Mój program działa poprawnie dla obrazów czarno-białych, ale potrzebuję pomocy zmieniając go do pracy z kilku kolorów (czerwony, pomarańczowy, żółty, jasnozielony, ciemnozielony, jasnoniebieski, ciemnoniebieski, purpurowy, czarny i biały).

Z góry dziękuję!

+0

Czy miniatury nie robią tego, co chcesz? –

Odpowiedz

7

Robisz kilka błędów.

Przede wszystkim należy użyć PNG, a nie JPG dla danych wyjściowych. JPG wprowadza tak wiele artefaktów, że małe obrazy, takie jak twoje wyniki, stają się całkowicie zdegenerowane.

Następnie należy zmniejszyć paletę. Znacznie łatwiej jest pracować z danymi wejściowymi bez szumów.

Przede wszystkim nudny inicjalizacji:

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

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

Następnie deklarujemy palety - to powinno zawierać wszystkie możliwe kolory klocków LEGO. I próbki poniżej wartości z obrazu, ale można użyć czarno-białe, jak to zrobić w kodzie, lub dowolnych kolorach chcesz tak długo, jak są one zbliżone do kolorów w obrazie źródłowym:

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

The Kod poniżej uzna paletę dla PIL, ponieważ PIL potrzebuje płaską tablicę zamiast tablicy krotek:

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

teraz możemy zadeklarować obraz, który będzie trzymać palety. Użyjemy go, aby później zmniejszyć kolory z oryginalnego obrazu.

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

Tutaj otwieramy obraz i kwantyzujemy go. Skalowujemy go do rozmiaru osiem razy większego niż potrzeba, ponieważ będziemy próbkować średnią wydajność później.

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 

Po tym, nasz obraz wygląda tak:

quantized image

Musimy przekształcić go z powrotem na RGB, dzięki czemu możemy spróbować pikseli teraz:

img = img.convert('RGB') 

Teraz "Zamierzamy skonstruować nasz ostateczny obraz. Aby to zrobić, przetestujemy, ile pikseli w każdej palecie kolorów zawiera każdy kwadrat w większym obrazie. Następnie wybierzemy najczęściej występujący kolor.

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) 

Wreszcie możemy zapisać dane wyjściowe:

out.save(output_path) 

Rezultat:

small image

powiększone o 1600%:

big image

+0

Witam, Właśnie dostałem się do używania kodu, i to rzuca mi kilka błędów, które wyglądają jak są związane z wersją pythona. Używam Python 3.4.2. W jakiej wersji napisano ten kod? –

+0

Python 3.4.3, ale używam raczej Pillow niż PIL (jest to bardziej aktywny fork). Mogą wystąpić niewielkie zmiany interfejsu API. –

+1

Dzięki za informacje. Cieszę się, że mogę powiedzieć, że wszystko działa teraz. –

3

Tylko dla zabawy, rozwiązać ten z ImageMagick - co jest również wywoływać z Pythona ...

Po pierwsze, tworzę trochę palety pasujące kolory - Twój biały nie jest biały i bardzo różni się swoją zielony z zielonego pomysłu ImageMagick, więc zamiast imion koloru użyłem dla nich języka heksadecymalnego.

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

Gdybym skalę, że palety w górę, wygląda to tak:

enter image description here

Potem zmienić rozmiar obrazu w dół do 4x4 i map wynik do palety niestandardowej i skalować go z powrotem tak widać to tak:

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

i oto wynik

enter image description here

Biały jest wyłączony, aby pasował do "biały" w oryginale.