【问题标题】:How to create colour gradient in Python?如何在 Python 中创建颜色渐变?
【发布时间】:2014-10-29 09:37:11
【问题描述】:

我想创建一个在绿色和蓝色(或任何其他两种颜色)之间插值的新颜色图。我的目标是得到类似的东西:

首先,我真的不确定这是否可以使用蓝色和绿色的线性插值来完成。如果可以,我不知道该怎么做,我找到了一些关于使用 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 坐标,另外两列是颜色值的实际值在那个坐标。

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 空间中插值来创建...

【问题讨论】:

  • Check out this link 关于命名颜色。那里有显示规范方法之间转换的代码。 I also think this link 关于颜色条可能会有所帮助。
  • 你是如何创建示例渐变的?它远非线性。
  • 是的,它只是一个屏幕截图,说明了我想要什么。我没有创建它。我想知道 Python 是否有一些函数可以促进这些渐变......
  • what 的屏幕截图?
  • 我可以尝试找到我从中获取的幻灯片,如果这有帮助的话,但我只记得上面写着“这是一个颜色渐变的示例”

标签: python colors gradient


【解决方案1】:

我还没有看到一个简单的答案就是使用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>]

将输入更改为您想要的任何颜色。正如@zelusp 所指出的,这不会将自己限制为只有两种颜色的平滑组合(例如,红色到蓝色的中间将有黄色+绿色),但根据赞成票很明显是一个数字的人发现这是一个有用的近似值

【讨论】:

  • 这种方法产生的渐变通过其他颜色形成渐变——它不是只有两种颜色的真正渐变——即使用它从红色变为蓝色会产生黄色和绿色。跨度>
  • This page 是关于 Python 中颜色渐变的非凡资源
  • 与示例相比,至少包名拼写正确.... ;p
  • 这是一个 HSV 梯度。作者可以在答案或他的代码中指定这一点。否则,干得好。
【解决方案2】:

如果您只需要在两种颜色之间进行插值,我为此编写了一个简单的函数。 colorFader 用另外两个十六进制颜色代码为您创建一个十六进制颜色代码。

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

def colorFader(c1,c2,mix=0): #fade (linear interpolate) from color c1 (at mix=0) to c2 (mix=1)
    c1=np.array(mpl.colors.to_rgb(c1))
    c2=np.array(mpl.colors.to_rgb(c2))
    return mpl.colors.to_hex((1-mix)*c1 + mix*c2)

c1='#1f77b4' #blue
c2='green' #green
n=500

fig, ax = plt.subplots(figsize=(8, 5))
for x in range(n+1):
    ax.axvline(x, color=colorFader(c1,c2,x/n), linewidth=4) 
plt.show()

结果:

因兴趣高涨而更新:

colorFader 现在适用于 rgb 颜色和颜色字符串,例如“red”甚至“r”。

【讨论】:

  • c1='#ff0000' #red and c2='#001dff' #blue
  • 这是一个很好的答案。如果我用hex(a)[2:4] 替换hex(a)[2:],对我有用。
  • @Ulf Alsak:感谢您告诉我!在执行之前,您是否更改了我的代码中的任何内容?或许你可以举一个hex(a)的内容的例子?
  • 是的!我实际上在上面评论错误,我的意思是hex(a)[2:-1]。在我的环境中,hex 返回一个以“L”结尾的字符串,您不想包含它,因此 -1。
  • @Ulf Alsak:完全更新。也应该在您的系统上工作;)
【解决方案3】:

很明显,您的原始示例渐变不是线性的。看一下图像中红色、绿色和蓝色值的平均值:

尝试使用线性渐变的组合重新创建它会很困难。

对我来说,每种颜色看起来都像是两条高斯曲线的叠加,所以我做了一些最佳拟合并想出了这个:

使用这些计算值让我可以创建一个非常漂亮的渐变,几乎完全匹配你的。

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)

不幸的是,我还不知道如何将其推广到任意颜色。

【讨论】:

  • 谢谢马克,这太棒了。我也一直在尝试不同的曲线,但正如你所说,我很难找到任何方法将其推广到任意颜色。也许看看这里wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps 创建的一些标准python 渐变是如何有所帮助的,尽管我找不到显示它们是如何创建的代码。
【解决方案4】:

每个元组的第一个元素(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()

请注意,红色很常见。它在那里是因为中心区域接近灰色 - 这三个组件是必要的。

这会产生:

【讨论】:

  • +1 用于跳出框框思考,我从未想过实际对组件进行采样。只是出于好奇,如果每个元组中的第一个值是位置,第二个是值,为什么字典中每个组件都需要第三列?第三列代表什么?
  • @Jack:这两个值允许颜色图中的不连续性。如果一个点是(x0, yleft, yright),则颜色图接近yleft,因为x 增加到x0,它接近yright,因为x 减少到x0。
  • 太好了,非常感谢!我不得不承认,这比我之前想象的要复杂。使用你的方法,我可以得到我想要的,只要我有一个梯度可以得到组件值。但是,如果我想创建一些上述内容,但要用于红色和绿色等。我想这不会那么简单。
【解决方案5】:

我也需要这个,但我想输入多个任意色点。考虑一张热图,您需要黑色、蓝色、绿色……一直到“热”色。我借用了上面Mark Ransom 的代码并对其进行了扩展以满足我的需要。我很高兴。感谢所有人,尤其是 Mark。

此代码与图像大小无关(高斯分布中没有常数);您可以使用 width= 参数将其更改为 pixel()。它还允许调整分布的“传播”(-> stddev);您可以通过将 spread= 参数更改为 pixel() 来进一步混淆它们或引入黑带。

#!/usr/bin/env python

width, height = 1000, 200

import math
from PIL import Image
im = Image.new('RGB', (width, height))
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=im.size[0], map=heatmap)
    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')

这是这段代码产生的多点渐变:

【讨论】:

    【解决方案6】:

    这将创建一个由单个参数控制的颜色图,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。green 从 0 上升到 y,然后保持不变。 blue 在y 上加注星号,前半部分保持不变,然后逐渐下降到 0。

    这是y = 0.7的情节:

    您可以通过添加一两个片段来平滑它。

    【讨论】:

    • 感谢这个很好的例子。正如您所指出的,需要进行一些平滑处理。现在我们只有两个分段线性函数,它们在中间点“镜像”。我想理想的情况是让两个非线性函数也镜像在中点附近并且相交接近于零?
    • 是的,类似的。 @jcoppens 有一个更精致的例子。
    【解决方案7】:

    这是一种非常紧凑的创建颜色图的方法。 另请参阅LinearSegmentedColormap 的文档。

    import matplotlib as mpl
    import matplotlib.pylab as plt
    
    cmap0 = mpl.colors.LinearSegmentedColormap.from_list(
            'green2red', ['green', 'orangered'])
    cmap1 = mpl.colors.LinearSegmentedColormap.from_list(
            'unevently divided', [(0, 'b'), (.3, 'gray'), (1, 'green')])
    
    # plot
    fig, axs = plt.subplots(2, 1)
    norm = mpl.colors.Normalize(vmin=0, vmax=1)
    cbar = axs[0].figure.colorbar(
                mpl.cm.ScalarMappable(norm=norm, cmap=cmap0),
                ax=axs[0], fraction=.1)
    cbar = axs[1].figure.colorbar(
                mpl.cm.ScalarMappable(norm=norm, cmap=cmap1),
                ax=axs[1], fraction=.1)
    plt.show()
    

    【讨论】:

      【解决方案8】:

      也许这个link 可以帮助你解决你的问题,感谢这段代码的作者redjack001

      from PIL import Image
      
      #set the size of the new image
      w = 500
      h = 200
      
      #start creating gradient
      pixel_list = []
      pixel_w = []
      
      #colour at 0,0
      start1 = (255,255,255)
      s = list(start1)
      pixel_list.append(start1)
      print('Position zero:', pixel_list[0])
      
      #colour at end
      end1 = (174,15,15)
      e = list(end1)
      
      #in case you want to change the final colour you could use f to adjust otherwise just keep it at 1
      f = 1
      
      #transition per line
      r = (s[0] - e[0])/w*f
      g = (s[1] - e[1])/w*f
      b = (s[2] - e[2])/w*f
      
      t = ()
      
      for j in range (0,w):
          t = pixel_list[j]
      
          #convert pixel tuple to a list and recalculate
          li = list(t)
          li[0] = int(max((li[0] - r*j),0))
          li[1] = int(max((li[1] - g*j),0))
          li[2] = int(max((li[2] - b*j),0))
          z = (li[0],li[1],li[2])
          final_t = tuple(z)
          #print('final_t:', final_t) if you want to show the list of pixel values
          pixel_list[j] = final_t
          for i in range (0,h):
              pixel_w = []
              pixel_w.append(final_t)
              pixel_list.extend(pixel_w)
      
      l = len(pixel_list)
      
      print('Pixel_list:', pixel_list, l)
      
      
      #remove the last item
      del pixel_list[l-1:]
      
      print('Reprint length:', len(pixel_list))
      
      im = Image.new('RGB', (w,h))
      im.putdata(pixel_list)
      im.show()
      

      结果在这里:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-15
        • 2019-05-14
        • 1970-01-01
        • 1970-01-01
        • 2022-07-28
        • 2018-05-02
        • 2016-04-18
        相关资源
        最近更新 更多