2014-09-04 12 views
8

Chcę utworzyć nową mapę kolorów, która interpoluje między zielonym i niebieskim (lub dwoma innymi kolorami). Moim celem jest uzyskanie czegoś w rodzaju: gradientJak utworzyć gradient koloru w Pythonie?

Przede wszystkim nie jestem pewien, czy można to zrobić za pomocą liniowej interpolacji niebieskiego i zielonego. Jeśli to możliwe, nie jestem pewien, jak to zrobić, znalazłem dokumentację dotyczącą użycia metody matplotlib, która interpoluje określone wartości RGB. Na przykład dokumentacja mówi:

"Przykład: załóżmy, że chcesz, aby czerwony wzrósł od 0 do 1 w dolnej połowie, zielony, aby zrobić to samo w połowie i niebieski w górnej połowie. ”

from matplotlib import pyplot as plt 
import matplotlib 
import numpy as np 

plt.figure() 
a=np.outer(np.arange(0,1,0.01),np.ones(10)) 
cdict2 = {'red': [(0.0, 0.0, 0.0), 
        (0.5, 1.0, 1.0), 
        (1.0, 1.0, 1.0)], 
     'green': [(0.0, 0.0, 0.0), 
        (0.25, 0.0, 0.0), 
        (0.75, 1.0, 1.0), 
        (1.0, 1.0, 1.0)], 
     'blue': [(0.0, 0.0, 0.0), 
        (0.5, 0.0, 0.0), 
        (1.0, 1.0, 1.0)]} 
my_cmap2 = matplotlib.colors.LinearSegmentedColormap('my_colormap2',cdict2,256) 
plt.imshow(a,aspect='auto', cmap =my_cmap2)     
plt.show() 

EDIT: I teraz zrozumieć, jak działa interpolacji, na przykład ten da się czerwony na białym interpolacji:

białego na czerwony: Schodząc kolumny z«matrycą»dla każdego koloru , w kolumnie pierwszej mamy współrzędną x, w której chcemy, aby interpolacja zaczęła się i kończyła, a dwie pozostałe kolumny są rzeczywistymi wartościami dla koloru val na tej współrzędnej.

cdict2 = {'red': [(0.0, 1.0, 1.0), 
        (1.0, 1.0, 1.0), 
        (1.0, 1.0, 1.0)], 
     'green': [(0.0, 1.0, 1.0), 
        (1.0, 0.0, 0.0), 
        (1.0, 0.0, 0.0)], 
    'blue': [(0.0, 1.0, 1.0), 
       (1.0, 0.0, 0.0), 
       (1.0, 0.0, 0.0)]} 

Jest oczywiste, że gradient Chcę będzie bardzo trudno stworzyć przez interpolację w przestrzeni RGB ...

+0

[Sprawdź ten link] (http://matplotlib.org/examples/color/named_colors.html) o nazwanych kolorach. Jest tam kod, który pokazuje konwersję między metodami specyfikacji. [Też myślę, że ten link] (http://matplotlib.org/examples/api/colorbar_only.html) o kolorach może pomóc. – mauve

+0

Jak stworzyłeś przykładowy gradient? To daleki od linearności. –

+0

Tak, oczywiście, to tylko zrzut ekranu ilustrujący to, co chcę. Nie stworzyłem tego. Zastanawiam się, czy Python ma jakieś funkcje, które ułatwiają tego rodzaju gradienty ... – Jack

Odpowiedz

3

Stwarza to colormap kontrolowany przez jednego parametru, y:

from matplotlib.colors import LinearSegmentedColormap 


def bluegreen(y): 
    red = [(0.0, 0.0, 0.0), (0.5, y, y), (1.0, 0.0, 0.0)] 
    green = [(0.0, 0.0, 0.0), (0.5, y, y), (1.0, y, y)] 
    blue = [(0.0, y, y), (0.5, y, y),(1.0,0.0,0.0)] 
    colordict = dict(red=red, green=green, blue=blue) 
    bluegreenmap = LinearSegmentedColormap('bluegreen', colordict, 256) 
    return bluegreenmap 

red Przyspieszenie od 0 do y, a następnie z powrotem do 0. green zwiększa się od 0 do y, a następnie jest stała. blue gwiazdy w y i jest stała w pierwszej połowie, a potem ramp down 0.

Oto wykres z y = 0.7:

bluegreen color map

Można wygładzić ją za pomocą dodawania innego segmentu lub dwa.

+0

Dzięki za ten świetny przykład. Jak zauważysz, wymagane będzie pewne wygładzenie. W tej chwili mamy tylko dwie liniowe funkcje, które są "odbijane" w połowie drogi. Sądzę, że idealnym scenariuszem byłoby posiadanie dwóch nieliniowych funkcji również odbijanych w lustrze o połowie drogi i przecinających się blisko zera? – Jack

+0

Tak, coś w tym stylu. @jcoppens ma bardziej wyrafinowany przykład. –

5

Pierwszym elementem każdej krotki (0, 0,25, 0,5 itd.) Jest miejsce, w którym kolor powinien mieć określoną wartość. Pobrałem 5 próbek, aby zobaczyć komponenty RGB (w GIMP) i umieściłem je w tabelach. Komponenty RGB przechodzą od 0 do 1, więc musiałem podzielić je przez 255.0, aby skalować normalne wartości 0-255.

5 punktów to raczej przybliżone przybliżenie - jeśli chcesz uzyskać "gładszy" wygląd, użyj więcej wartości.

from matplotlib import pyplot as plt 
import matplotlib 
import numpy as np 

plt.figure() 
a=np.outer(np.arange(0,1,0.01),np.ones(10)) 
fact = 1.0/255.0 
cdict2 = {'red': [(0.0, 22*fact, 22*fact), 
        (0.25, 133*fact, 133*fact), 
        (0.5, 191*fact, 191*fact), 
        (0.75, 151*fact, 151*fact), 
        (1.0, 25*fact, 25*fact)], 
     'green': [(0.0, 65*fact, 65*fact), 
        (0.25, 182*fact, 182*fact), 
        (0.5, 217*fact, 217*fact), 
        (0.75, 203*fact, 203*fact), 
        (1.0, 88*fact, 88*fact)], 
     'blue': [(0.0, 153*fact, 153*fact), 
        (0.25, 222*fact, 222*fact), 
        (0.5, 214*fact, 214*fact), 
        (0.75, 143*fact, 143*fact), 
        (1.0, 40*fact, 40*fact)]} 
my_cmap2 = matplotlib.colors.LinearSegmentedColormap('my_colormap2',cdict2,256) 
plt.imshow(a,aspect='auto', cmap =my_cmap2)     
plt.show() 

Należy zauważyć, że czerwony jest dość obecny. Jest tam, ponieważ obszar środkowy zbliża się do szarego - tam, gdzie potrzebne są trzy komponenty.

ta produkuje: result from the above table

+0

+1 za myślenie nieszablonowe, nigdy nie myślałem o próbowaniu komponentów. Po prostu z ciekawości, dlaczego w każdym komponencie musi być trzecia kolumna w słowniku, jeśli pierwsza wartość w każdej krotce jest pozycją, a druga jest wartością - co reprezentuje trzecia kolumna? – Jack

+0

@Jack: Dwie wartości pozwalają na brak ciągłości na kolorowej mapie. Jeśli punkt to '(x0, yleft, yright)', to colormap zbliża się 'yleft', gdy' x' rośnie do 'x0', i zbliża się do' yright', ponieważ 'x' zmniejsza się do' x0'. –

+0

Świetnie, dziękuję! Muszę przyznać, że było to bardziej skomplikowane, niż wcześniej sądziłem. Korzystając z Twojej metody, mogę uzyskać to, co chcę, pod warunkiem, że dysponuję gradientem, aby uzyskać wartości składowe. Ale co jeśli chcę stworzyć niektóre powyższe, ale na czerwono i zielono itp. Myślę, że to nie będzie takie proste. – Jack

16

To oczywiste, że oryginalny przykład gradient nie liniowy.Spojrzeć na wykres z czerwonego, zielonego i niebieskiego wartościach uśrednionych po obrazie:

example gradient graph

próbujące odtworzyć ten z kombinacją gradienty liniowe będzie trudne.

Dla mnie każdy kolor wygląda dodanie dwóch gaussowskich krzywe, więc zrobiłem kilka najlepiej pasuje i podszedł z tym:

simulated

Stosując te obliczone wartości pozwala mi stworzyć naprawdę ładny gradient, który pasuje do twojego prawie dokładnie.

import math 
from PIL import Image 
im = Image.new('RGB', (604, 62)) 
ld = im.load() 

def gaussian(x, a, b, c, d=0): 
    return a * math.exp(-(x - b)**2/(2 * c**2)) + d 

for x in range(im.size[0]): 
    r = int(gaussian(x, 158.8242, 201, 87.0739) + gaussian(x, 158.8242, 402, 87.0739)) 
    g = int(gaussian(x, 129.9851, 157.7571, 108.0298) + gaussian(x, 200.6831, 399.4535, 143.6828)) 
    b = int(gaussian(x, 231.3135, 206.4774, 201.5447) + gaussian(x, 17.1017, 395.8819, 39.3148)) 
    for y in range(im.size[1]): 
     ld[x, y] = (r, g, b) 

recreated gradient

Niestety nie wiem jeszcze jak to uogólnić na dowolne kolory.

+0

Dzięki Mark, to jest świetne. Eksperymentowałem również z różnymi krzywymi, ale jak mówisz, staram się znaleźć jakiś sposób uogólnienia tego dla arbitralnych kolorów. Być może warto przyjrzeć się, jak przydatne mogą być niektóre ze standardowych gradientów pythonowych http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps, chociaż nie mogę znaleźć kodu, który pokazuje, w jaki sposób zostały utworzone. – Jack

30

Prostą odpowiedzią, której jeszcze nie widziałem, jest skorzystanie z colour package.

zainstalować poprzez pip

pip install colour 

Użyj jako tak:

from colour import Color 
red = Color("red") 
colors = list(red.range_to(Color("green"),10)) 

# colors is now a list of length 10 
# Containing: 
# [<Color red>, <Color #f13600>, <Color #e36500>, <Color #d58e00>, <Color #c7b000>, <Color #a4b800>, <Color #72aa00>, <Color #459c00>, <Color #208e00>, <Color green>] 

zmienić wejść do dowolnych kolorach chcesz

+0

Jak to działa z matplotlib? – SriK

3

Musiałem to za dobrze, ale chciałem wprowadzić wiele arbitralny punkty koloru. Rozważ mapę ciepła, w której potrzebujesz czarnego, niebieskiego, zielonego ... aż do "gorących" kolorów. Pożyczyłem kod Mark Ransom powyżej i rozszerzyłem go, aby spełnić moje potrzeby. Jestem z tego bardzo szczęśliwy. Dziękuję wszystkim, zwłaszcza Markowi.

Ten kod jest neutralny do rozmiaru obrazu (brak stałych w rozkładzie gaussowskim); możesz go zmienić za pomocą parametru width = do piksela(). Umożliwia także dostrojenie "spreadu" (-> stddev) rozkładu; możesz je zmulić dalej lub wprowadzić czarne pasy, zmieniając parametr spread = na piksel().

#!/usr/bin/env python 

import math 
from PIL import Image 
im = Image.new('RGB', (3000, 2000)) 
ld = im.load() 

# A map of rgb points in your distribution 
# [distance, (r, g, b)] 
# distance is percentage from left edge 
heatmap = [ 
    [0.0, (0, 0, 0)], 
    [0.20, (0, 0, .5)], 
    [0.40, (0, .5, 0)], 
    [0.60, (.5, 0, 0)], 
    [0.80, (.75, .75, 0)], 
    [0.90, (1.0, .75, 0)], 
    [1.00, (1.0, 1.0, 1.0)], 
] 

def gaussian(x, a, b, c, d=0): 
    return a * math.exp(-(x - b)**2/(2 * c**2)) + d 

def pixel(x, width=100, map=[], spread=1): 
    width = float(width) 
    r = sum([gaussian(x, p[1][0], p[0] * width, width/(spread*len(map))) for p in map]) 
    g = sum([gaussian(x, p[1][1], p[0] * width, width/(spread*len(map))) for p in map]) 
    b = sum([gaussian(x, p[1][2], p[0] * width, width/(spread*len(map))) for p in map]) 
    return min(1.0, r), min(1.0, g), min(1.0, b) 

for x in range(im.size[0]): 
    r, g, b = pixel(x, width=3000, map=points) 
    r, g, b = [int(256*v) for v in (r, g, b)] 
    for y in range(im.size[1]): 
     ld[x, y] = r, g, b 

im.save('grad.png')