Я хочу создать новую цветочную карту, которая интерполирует между зеленым и синим (или любые другие два цвета, если на то пошло). Моя цель - получить что-то вроде:
Прежде всего, я действительно не уверен, что это можно сделать, используя линейную интерполяцию синего и зеленого. Если это возможно, я не уверен, как это сделать, я нашел документацию по использованию метода matplotlib, который интерполирует указанные значения RGB здесь
Настоящая проблема заключается в понимании того, как работает "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()
EDIT: теперь я понимаю, как работает интерполяция, например, это даст интерполяцию от красного до белого:
От белого до красного: переходя по столбцам "матрицы" для каждого цвета, в первом столбце мы имеем 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...
Простой ответ, который я еще не видел, - это просто использовать цветной пакет .
Установить через 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>]
Измените входы на любые цвета, которые вы хотите
Очевидно, что ваш оригинальный примерный градиент не является линейным. Посмотрите на график значений красного, зеленого и синего, усредненных по изображению:
Попытка воссоздать это с помощью комбинации линейных градиентов будет сложной.
Для меня каждый цвет выглядит как добавление двух гауссовских кривых, поэтому я сделал несколько лучших приемов и придумал это:
Используя эти рассчитанные значения, я могу создать действительно красивый градиент, который точно соответствует вашим.
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)
К сожалению, я еще не знаю, как обобщить его на произвольные цвета.
Первый элемент каждого кортежа (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()
Обратите внимание, что красный цвет присутствует. Это там, потому что центральная область приближается к серой - где нужны три компонента.
Это дает:
(x0, yleft, yright)
, yleft
приближается к yleft
при увеличении x
до x0
и приближается к yright
при уменьшении x
до x0
.
Мне тоже это нужно, но я хотел ввести несколько произвольных цветовых точек. Рассмотрим карту тепла, где вам нужны черные, синие, зеленые... вплоть до "горячих" цветов. Я позаимствовал код Марка Рэнсом выше и расширил его, чтобы удовлетворить мои потребности. Я очень доволен этим. Спасибо всем, особенно Марк.
Этот код нейтрален по размеру изображения (без констант в гауссовском распределении); вы можете изменить его с помощью параметра 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')
Это создает цветовой код, управляемый одним параметром, 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
:
Вы можете сгладить его, добавив еще один сегмент или два.
Если вам просто нужно интерполировать между двумя цветами, я написал для этого простую функцию. fadeColor
создает вам шестнадцатеричный код цвета из двух других шестнадцатеричных цветовых кодов.
import matplotlib.pyplot as plt
import numpy as np
def fadeColor(c1,c2,mix=0): #fade (linear interpolate) from color c1 (at mix=0) to c2 (mix=1)
assert len(c1)==len(c2)
assert mix>=0 and mix<=1, 'mix='+str(mix)
rgb1=np.array([int(c1[ii:ii+2],16) for ii in range(1,len(c1),2)])
rgb2=np.array([int(c2[ii:ii+2],16) for ii in range(1,len(c2),2)])
rgb=((1-mix)*rgb1+mix*rgb2).astype(int)
#cOld='#'+''.join([hex(a)[2:] for a in rgb])
#print(11,[hex(a)[2:].zfill(2) for a in rgb])
c='#'+('{:}'*3).format(*[hex(a)[2:].zfill(2) for a in rgb])
#print(rgb1, rgb2, rgb, cOld, c)
return c
fig, ax = plt.subplots(figsize=(8, 5))
c1='#1f77b4' #blue
c2='#2ca02c' #green
# another set of colors
#c1='#ff0000' #red
#c2='#001dff' #blue
n=500
for x in range(n+1):
ax.axvline(x, color=fadeColor(c1,c2,x/n), linewidth=4)
plt.show()
результат: