web-dev-qa-db-ja.com

Pythonで色のグラデーションを作成する方法は?

私は緑と青の間で補間する新しいカラーマップを作成したいです(または、他の2色)。私の目標は次のようなものを取得することです。 gradient

まず第一に、これが青と緑の線形補間を使用して実行できるかどうかは本当にわかりません。可能であれば、その方法がわかりません。指定されたRGB値を補間するmatplotlibメソッドの使用に関するドキュメントを見つけました 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()

編集:私は今、補間がどのように機能するかを理解しています、たとえばこれは赤から白への補間を与えます:

白から赤:各色の「マトリックス」の列を下に移動し、列1には補間を開始および終了する場所のx座標があり、他の2つの列はその座標の色の値の実際の値です。

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空間で補間することによって作成するのが非常に難しいことは明らかです...

23
Dipole

私がまだ見たことのない単純な答えは、単に 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で述べたように、これはnot自身を2色のみの滑らかな組み合わせに制限します(たとえば、赤から青は中央に黄+緑があります) )、しかし、賛成票に基づいて、多くの人々がこれが有用な近似であるとわかることは明らかです

56
Hamy

元の例の勾配がnot linearであることは明らかです。画像全体で平均化された赤、緑、青の値のグラフを見てください。

example gradient graph

線形グラデーションの組み合わせでこれを再現しようとするのは難しいでしょう。

私にとって、各色は2つのガウス曲線を追加したように見えるので、いくつかの最適な方法を試し、これを思いつきました。

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

残念ながら、私はそれを任意の色に一般化する方法をまだ知りません。

27
Mark Ransom

2色の間で補間するだけの場合、そのための簡単な関数を書きました。 colorFaderは、他の2つの16進カラーコードから16進カラーコードを作成します。

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

result:

simple color mixing in python

高い関心による更新:

colorFaderは、rgb-colorsおよび 'red'または 'r'のようなcolor-stringsでも機能するようになりました。

10
Markus Dutschke

各タプルの最初の要素(0、0.25、0.5など)は、色が特定の値になる場所です。 RGBコンポーネント(GIMP内)を確認するために5つのサンプルを取得し、それらをテーブルに配置しました。 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()

赤がかなり存在することに注意してください。中央の領域が灰色に近づいているためです-3つのコンポーネントが必要です。

これにより、以下が生成されます。 result from the above table

6
jcoppens

これにより、単一のパラメーター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に上昇し、その後は一定です。 blueyに星を付け、前半は一定で、その後0に減少します。

y = 0.7のプロットは次のとおりです。

bluegreen color map

別の1つまたは2つのセグメントを追加することで、スムーズにできます。

5

これも必要でしたが、複数の任意のカラーポイントを入力したかったのです。黒、青、緑が必要なヒートマップを考えてみてください...「ホット」カラーまで。上記の 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=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')
4
dgc