2014-09-04 404 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')