2016-09-14 14 views
7

Co chcę osiągnąć, to programowo utworzyć dwuwymiarową rampę kolorów reprezentowaną przez matrycę 256x256 wartości kolorów. Oczekiwany wynik można zobaczyć na załączonym obrazie. To, co mam dla punktu początkowego, to 4 kolory narożne macierzy, z których należy interpolować pozostałe 254 kolory między nimi. Chociaż odniosłem sukces w interpolacji kolorów dla jednej osi, dwuwymiarowe obliczenia dają mi pewne złe bóle głowy. Chociaż wydaje się, że obraz ma nieliniowy gradient koloru, byłbym zadowolony z liniowego.Dwuwymiarowa rampa kolorów (matryca 256x256) interpolowana z 4 kolorów narożników

Jeśli mógłbyś dać mi wskazówki, jak to zrobić za pomocą numpy lub innych narzędzi, będę wdzięczny.

enter image description here

+0

Czy masz konkretną formułę do intrepolacji wartości? – jadsq

+0

Nie, niezupełnie. Próbowałem używać scipy.interpolate.interp1d i http://www.alanzucconi.com/2016/01/06/colour-interpolation. – hetsch

+0

[To pytanie] (http://stackoverflow.com/questions/25060379/python-bilinear-image-interpolation) wygląda na trafny. –

Odpowiedz

5

Oto bardzo krótki rozwiązanie używając zoom function z scipy.ndimage. Zdefiniuję obraz 2x2 RGB z kolorami początkowymi (tutaj losowymi) i po prostu powiększam do 256x256, order=1 czyni interpolację liniową. Oto kod:

import numpy as np 
import matplotlib.pyplot as plt 

im=(np.random.rand(2,2,3)*255).astype(np.uint8) 

from scipy.ndimage.interpolation import zoom 
zoomed=zoom(im,(128,128,1),order=1) 

plt.subplot(121) 
plt.imshow(im,interpolation='nearest') 
plt.subplot(122) 
plt.imshow(zoomed,interpolation='nearest') 
plt.show() 

wyjściowa:

output from the script

+0

Nie wiem, gdzie skończyły się moje poprzednie komentarze. Chciałem tylko podziękować za to rozwiązanie. Działa bardzo dobrze, a sztuczka z powiększaniem jest bardzo przydatna! Dziękuję jadsq! – hetsch

+1

Nie ma za co. Twoje komentarze mogły zostać usunięte z powodu polityki stackoverflow w komentarzach "Dzięki", ale nie martw się: przegłosowanie i zaakceptowanie odpowiedzi wystarczy, aby okazać Ci wdzięczność :) – jadsq

+0

Po prostu zostawiając ten komentarz na wypadek, gdyby dezorientowało kogoś innego, dodatkowe 1 w wielkość powiększenia jest tylko dla wartości RGB. Ta odpowiedź była bardzo przydatna dla tego, czego i tak potrzebowałem, dzięki, tylko szkoda, że ​​interpolacja nie działa odwrotnie :) – Peter

2

Oto bardzo krótki sposób to zrobić z ImageMagick który jest zainstalowany na większości dystrybucji Linuksa i jest dostępny dla OS X i Windows. Istnieją również powiązania w Pythonie. W każdym razie, po prostu w linii poleceń, należy utworzyć kwadrat 2x2 z kolorami w 4 rogach obrazu, pozwól ImageMagick poszerzyć i interpolować aż do pełnego rozmiaru:

convert \(xc:"#59605c" xc:"#ebedb3" +append \) \ 
     \(xc:"#69766d" xc:"#b3b3a0" +append \) \ 
     -append -resize 256x256 result.png 

enter image description here

pierwszej linii tworzy 1x1 piksel każdego z twoich górnych lewych i górnych prawej krawędzi i dołącza dwa obok siebie. Druga linia tworzy 1x1 piksel każdego z twoich dolnych lewych i dolnych prawych rogów i dołącza je obok siebie. Ostatnia linia dodaje dolny rząd poniżej górnego wiersza i powiększa się poprzez interpolację do 256x256.

Jeśli chcesz lepiej zrozumieć, co się dzieje, tutaj jest ten sam podstawowy obraz ale skaluje się za pomocą najbliższego sąsiada zamiast interpolacja:

convert \(xc:"#59605c" xc:"#ebedb3" +append \) \ 
     \(xc:"#69766d" xc:"#b3b3a0" +append \) \ 
     -append -scale 20x20 result.png 

enter image description here

+0

Dziękuję za to proste rozwiązanie, które z pewnością przydaje się przy pracy z linią poleceń i skryptami powłoki. Bardzo wygodne! – hetsch

+0

Cieszę się, że to działa dla Ciebie. Możesz również zrobić prostą funkcję "bash", jeśli robisz to często. Zauważ, że możesz także podać kolory w 'rgb' lub' 'rgba' (z przezroczystością) lub' hsl' zamiast hexie z 'xc: 'rgb (0,255,255)' '. Powodzenia w projekcie! –

2

Oto 3 sposoby wykonywania tej dwuliniowy interpolacja. Pierwsza wersja wykonuje całą arytmetykę w czystym Pythonie, druga wykorzystuje kompozycję obrazu PIL, trzecia wykorzystuje Numpy do wykonywania arytmetyki. Zgodnie z oczekiwaniami, czysty Python jest znacznie wolniejszy niż inne podejścia. Wersja Numpy (wywodząca się z kodu napisanego przez Andras Deak) jest prawie tak samo szybka jak wersja PIL dla małych obrazów, ale dla większych obrazów wersja PIL jest zauważalnie szybsza.

Próbowałem również używać techniki skalowania jadsqa w PIL, ale wyniki nie były dobre - podejrzewam, że kod interpolacyjny PIL jest trochę błędny.

Jeśli chcesz utworzyć wiele bilinearnych obrazów tego samego rozmiaru, technika PIL ma jeszcze jedną zaletę: po utworzeniu masek kompozycji nie trzeba ich odbudowywać dla każdego obrazu.

#!/usr/bin/env python3 

''' Simple bilinear interpolation 
    Written by PM 2Ring 2016.09.14 
''' 

from PIL import Image 
from math import floor 
import numpy as np 

def color_square0(colors, size): 
    tl, tr, bl, br = colors 
    m = size - 1 
    r = range(size) 

    def interp_2D(tl, tr, bl, br, x, y): 
     u0, v0 = x/m, y/m 
     u1, v1 = 1 - u0, 1 - v0 
     return floor(0.5 + u1*v1*tl + u0*v1*tr + u1*v0*bl + u0*v0*br) 

    data = bytes(interp_2D(tl[i], tr[i], bl[i], br[i], x, y) 
     for y in r for x in r for i in (0, 1, 2)) 
    return Image.frombytes('RGB', (size, size), data) 

# Fastest 
def color_square1(colors, size): 
    #Make an Image of each corner color 
    tl, tr, bl, br = [Image.new('RGB', (size, size), color=c) for c in colors] 

    #Make the composition mask 
    mask = Image.new('L', (size, size)) 
    m = 255.0/(size - 1) 
    mask.putdata([int(m * x) for x in range(size)] * size) 

    imgt = Image.composite(tr, tl, mask) 
    imgb = Image.composite(br, bl, mask) 
    return Image.composite(imgb, imgt, mask.transpose(Image.TRANSPOSE)) 

# This function was derived from code written by Andras Deak  
def color_square2(colors, size): 
    tl, tr, bl, br = map(np.array, colors) 
    m = size - 1 
    x, y = np.mgrid[0:size, 0:size] 
    x = x[..., None]/m 
    y = y[..., None]/m 
    data = np.floor(x*y*br + (1-x)*y*tr + x*(1-y)*bl + (1-x)*(1-y)*tl + 0.5) 
    return Image.fromarray(np.array(data, dtype = 'uint8'), 'RGB') 

color_square = color_square1 

#tl = (255, 0, 0) 
#tr = (255, 255, 0) 
#bl = (0, 0, 255) 
#br = (0, 255, 0) 

tl = (108, 115, 111) 
tr = (239, 239, 192) 
bl = (124, 137, 129) 
br = (192, 192, 175) 

colors = (tl, tr, bl, br) 
size = 256 
img = color_square(colors, size) 
img.show() 
#img.save('test.png') 

wyjście

bilinear gradient


Tak dla zabawy, oto prosty program GUI używając Tkinter, który może być używany do generowania tych wzniesień.

#!/usr/bin/env python3 

''' Simple bilinear colour interpolation 
    using PIL, in a Tkinter GUI 

    Inspired by https://stackoverflow.com/q/39485178/4014959 

    Written by PM 2Ring 2016.09.15 
''' 

import tkinter as tk 
from tkinter.colorchooser import askcolor 
from tkinter.filedialog import asksaveasfilename 

from PIL import Image, ImageTk 

DEFCOLOR = '#d9d9d9' 
SIZE = 256 

#Make the composition masks 
mask = Image.new('L', (SIZE, SIZE)) 
m = 255.0/(SIZE - 1) 
mask.putdata([int(m * x) for x in range(SIZE)] * SIZE) 
maskt = mask.transpose(Image.TRANSPOSE) 

def do_gradient(): 
    imgt = Image.composite(tr.img, tl.img, mask) 
    imgb = Image.composite(br.img, bl.img, mask) 
    img = Image.composite(imgb, imgt, maskt) 
    ilabel.img = img 
    photo = ImageTk.PhotoImage(img) 
    ilabel.config(image=photo) 
    ilabel.photo = photo 

def set_color(w, c): 
    w.color = c 
    w.config(background=c, activebackground=c) 
    w.img = Image.new('RGB', (SIZE, SIZE), color=c) 

def show_color(w): 
    c = w.color 
    newc = askcolor(c)[1] 
    if newc is not None and newc != c: 
     set_color(w, newc) 
     do_gradient() 

def color_button(row, column, initcolor=DEFCOLOR): 
    b = tk.Button(root) 
    b.config(command=lambda w=b:show_color(w)) 
    set_color(b, initcolor) 
    b.grid(row=row, column=column) 
    return b 

def save_image(): 
    filetypes = [('All files', '.*'), ('PNG files', '.png')] 
    fname = asksaveasfilename(title="Save Image",filetypes=filetypes) 
    if fname: 
     ilabel.img.save(fname) 
     print('Saved image as %r' % fname) 
    else: 
     print('Cancelled') 

root = tk.Tk() 
root.title("Color interpolation") 

coords = ((0, 0), (0, 2), (2, 0), (2, 2)) 
tl, tr, bl, br = [color_button(r, c) for r,c in coords] 

ilabel = tk.Label(root, relief=tk.SUNKEN) 
do_gradient() 
ilabel.grid(row=1, column=1) 

b = tk.Button(root, text="Save", command=save_image) 
b.grid(row=3, column=1) 

root.mainloop() 
+0

Dziękujemy za tę kompleksową i dokładną odpowiedź oraz poręczny kod GUI. Wygląda na to, że masz bardzo głęboką wiedzę na temat numpy/PIL i manipulacji obrazem, chciałem cię zapytać, czy możesz rzucić okiem na moje następne pytanie, w którym Spektre bardzo mi pomógł, ale nie jestem do końca pewien, czy to możliwe. nie należy wykonywać operacji macierzowych/numpy (http://stackoverflow.com/questions/39489089/interpolating-elements-of-a-color-matrix-on-the-basis-of-some-given-reference-el/ 39506280 # 39506280). W każdym razie, dziękuję za poświęcony czas i rozwiązanie! – hetsch

+0

@hetsch: Jestem dość zadowolony z PIL, ale nie wiem zbyt wiele o Numpy. Powyższy kod Numpy został napisany przez Andrasa Deaka, ale zapomniałem o tym wspomnieć wcześniej (przepraszam Andras!). Zerknąłem na plik PDF, do którego linkowałeś w swoim nowym pytaniu, i zrozumiałem, że rozumiem ich algorytm interpolacji, ale nie wiem, jak skutecznie go zaimplementować za pomocą Numpy. Ale następnym razem, gdy go zobaczę, wymienię twoje nowe pytanie do Andrasa. –

+0

To byłoby naprawdę niesamowite! Dziękujemy za spędzenie czasu i przeczytanie artykułu. – hetsch