2014-09-04 68 views
8

我想創建一個在綠色和藍色(或其他任何兩種顏色之間)之間插入的新色彩映射。我的目標是得到這樣的東西:gradient如何在Python中創建顏色漸變?

首先,我真的不知道這是否可以使用藍色和綠色的線性插值完成。如果可以的話,我不知道該怎麼做,我發現了一些使用matplotlib方法插入指定的RGB值的文檔。here

真正的麻煩在於瞭解「cdict2」如何在下面工作。例如,文檔中提到:

「例如:假設你想讓紅色從下半部分的0增加到1,在中間部分用綠色做同樣的事情,在上半部分用藍色,然後你會用:」

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

編輯:我現在明白是怎麼插的作品,例如這會給一個紅白色插值:

由白變紅:沿着走的列‘矩陣’爲每種顏色,在第一列中我們有我們希望插值開始和結束的地方的x座標,另外兩列是顏色val的實際值在那個座標上。

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

很明顯,我想會是非常困難的梯度由RGB空間插值創造... ...

+0

[查看此鏈接](http://matplotlib.org/examples/color/named_colors.html)有關指定的顏色。那裏的代碼顯示了規範方法之間的轉換。 [我也認爲這個鏈接](http://matplotlib.org/examples/api/colorbar_only.html)關於顏色條可能會有所幫助。 – mauve 2014-09-04 15:15:33

+0

您是如何創建該示例漸變的?這遠非線性。 – 2014-09-04 18:04:25

+0

是的,它只是一個屏幕截圖,說明我想要什麼。我沒有創建它。我想知道如果Python有一些功能,這些功能,以促進這些類型的梯度...... – Jack 2014-09-04 18:10:15

回答

3

這將創建由單一的參數控制的顏色表,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從0上升至y,然後回降至0.​​從0上升至y然後恆定。 blue星星y,是上半年常數,接着減速到0

下面是與y = 0.7情節:

bluegreen color map

你可以通過增加另一個段或兩個光滑出來。

+0

感謝這個偉大的例子。正如你指出的那樣,一些平滑是必需的。現在我們只有兩個分段線性函數,它們在中間點處被「鏡像」。我想最理想的情況是讓兩個非線性函數也反映在中途點並相交於零點附近? – Jack 2014-09-04 16:02:02

+0

是的,類似的東西。 @jcoppens有一個更精緻的例子。 – 2014-09-04 16:21:36

5

每個元組的第一個元素(0,0.25,0.5等)是顏色應該是某個值的地方。我抽取了5個樣本來查看RGB組件(在GIMP中),並將它們放在表格中。 RGB分量從0到1,因此我必須將它們除以255.0來縮放正常的0-255值。

5點是一個相當粗略的近似值 - 如果你想要一個'平滑'的外觀,使用更多的值。

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

請注意,紅色是相當存在。它在那裏,因爲中心區域接近灰色 - 這三個組件是必要的。

這將產生:result from the above table

+0

+1以外思考,我從來沒有想過實際取樣組件。出於好奇,爲什麼需要在每個組件的字典中有第3列,如果每個元組中的第一個值是位置,第二個值是值 - 第三個代表什麼? – Jack 2014-09-04 16:09:37

+0

@Jack:這兩個值允許顏色映射中的不連續性。如果一個點是'(x0,yleft,yright)',當x增加到x0時,顏色圖接近yleft,當x減少到x0時,顏色圖接近yright。 – 2014-09-04 16:19:34

+0

太好了,謝謝你!我不得不承認,這比我以前想象的更復雜。使用你的方法,我可以得到我想要的,只要我有一個可用的梯度來獲取組件值。但是,如果我想創建一些上述但紅色和綠色等,我想這不會那麼簡單。 – Jack 2014-09-04 16:43:04

16

很明顯,你原來的例子梯度線性的。看一看整個圖像的平均紅色,綠色和藍色值的圖表:

example gradient graph

試圖與線性漸變的組合來創建此將是困難的。

對我來說,每一種顏色看起來像另外兩個高斯曲線,所以我做了一些最好的配合和與此想出了:

simulated

利用這些計算值讓我創造一個真正漂亮的梯度幾乎完全匹配你的。

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

不幸的是我還不知道如何將它推廣到任意的顏色。

+0

謝謝Mark,這太好了。我也一直在嘗試不同的曲線,但正如你所說,我很難找到任何方法來推廣這種任意顏色。也許看看如何創建一些標準的python漸變http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps會有所幫助,雖然我找不到可以顯示它們是如何創建的代碼。 – Jack 2014-09-05 11:52:19

30

我還沒有看到一個簡單的答案是隻使用colour package

通過PIP安裝

pip install colour 

使用像這樣:

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

更改輸入您要

+0

這是如何與matplotlib一起工作的? – SriK 2017-11-09 20:14:44

3

我需要這個問題,以及任何顏色,但我想輸入多個任意色點。考慮一個熱圖,你需要黑色,藍色,綠色......一直到「熱」的顏色。我借用了上面的Mark Ransom的代碼,並將其擴展以滿足我的需求。我對此很滿意。我要感謝所有人,特別是馬克。

此代碼對圖像的大小是中性的(高斯分佈中沒有常量);你可以用width =參數改變它爲pixel()。它還允許調整分佈的「傳播」( - > stddev);你可以將它們進一步混合起來,或者通過將spread =參數改爲pixel()來引入黑帶。

#!/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') 
相關問題