Вычислить значение RGB для диапазона значений для создания карты тепла
Я пытаюсь создать тепловую карту с питоном. Для этого мне нужно присвоить значение RGB каждому значению в диапазоне возможных значений. Я думал об изменении цвета от синего (минимальное значение) от зеленого до красного (максимальное значение).
В приведенном ниже примере рисунка объясняется, как я думал о цветовой композиции: у нас есть диапазон от 1 (чистый синий) до 3 (чистый красный), 2 находится между ними наподобие зеленого.
![color composition RGB in range(1-3)]()
Я читал о линейной интерполяции и написал функцию, которая (более или менее) обрабатывает вычисление для определенного значения в диапазоне между минимумом и максимумом и возвращает кортеж RGB. Он использует условия if
и elif
(что не делает меня полностью счастливым):
def convert_to_rgb(minimum, maximum, value):
minimum, maximum = float(minimum), float(maximum)
halfmax = (minimum + maximum) / 2
if minimum <= value <= halfmax:
r = 0
g = int( 255./(halfmax - minimum) * (value - minimum))
b = int( 255. + -255./(halfmax - minimum) * (value - minimum))
return (r,g,b)
elif halfmax < value <= maximum:
r = int( 255./(maximum - halfmax) * (value - halfmax))
g = int( 255. + -255./(maximum - halfmax) * (value - halfmax))
b = 0
return (r,g,b)
Однако мне интересно, можно ли написать функцию для каждого значения цвета без, используя условия if
. У кого-нибудь есть идея? Большое вам спасибо!
Ответы
Ответ 1
def rgb(minimum, maximum, value):
minimum, maximum = float(minimum), float(maximum)
ratio = 2 * (value-minimum) / (maximum - minimum)
b = int(max(0, 255*(1 - ratio)))
r = int(max(0, 255*(ratio - 1)))
g = 255 - b - r
return r, g, b
Ответ 2
Вот еще один способ сделать это, хотя и не настолько короткий, насколько это возможно, но гораздо более общий, поскольку он не был жестко задан для вашего определенного набора цветов. Это означает, что его также можно использовать для линейной интерполяции заданного диапазона значений по палитре произвольного цвета с переменным размером.
Также обратите внимание, что цвета могли быть интерполированы в других цветовых пространствах, давая результаты, которые могут быть более приятными, чем в других. Это иллюстрируется различными результатами, полученными из двух отдельных ответов, которые я представил на связанный вопрос, озаглавленный Диапазон значений для псевдоцвета.
import sys
EPSILON = sys.float_info.epsilon # Smallest possible difference.
def convert_to_rgb(minval, maxval, val, colors):
# "colors" is a series of RGB colors delineating a series of
# adjacent linear color gradients between each pair.
# Determine where the given value falls proportionality within
# the range from minval->maxval and scale that fractional value
# by the total number in the "colors" pallette.
i_f = float(val-minval) / float(maxval-minval) * (len(colors)-1)
# Determine the lower index of the pair of color indices this
# value corresponds and its fractional distance between the lower
# and the upper colors.
i, f = int(i_f // 1), i_f % 1 # Split into whole & fractional parts.
# Does it fall exactly on one of the color points?
if f < EPSILON:
return colors[i]
else: # Otherwise return a color within the range between them.
(r1, g1, b1), (r2, g2, b2) = colors[i], colors[i+1]
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
if __name__ == '__main__':
minval, maxval = 1, 3
steps = 10
delta = float(maxval-minval) / steps
colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0)] # [BLUE, GREEN, RED]
print(' Val R G B')
for i in range(steps+1):
val = minval + i*delta
r, g, b = convert_to_rgb(minval, maxval, val, colors)
print('{:.3f} -> ({:3d}, {:3d}, {:3d})'.format(val, r, g, b))
Числовой вывод:
Val R G B
1.000 -> ( 0, 0, 255)
1.200 -> ( 0, 50, 204)
1.400 -> ( 0, 101, 153)
1.600 -> ( 0, 153, 101)
1.800 -> ( 0, 204, 50)
2.000 -> ( 0, 255, 0)
2.200 -> ( 51, 203, 0)
2.400 -> (102, 152, 0)
2.600 -> (153, 101, 0)
2.800 -> (203, 51, 0)
3.000 -> (255, 0, 0)
Здесь вывод визуализируется как горизонтальный градиент:
![horizontal gradient generated with function in answer]()
Ответ 3
Вы можете часто исключать if
с индексом в массив из двух значений. Python не имеет тернарного условного оператора, но это работает:
r = [red_curve_1, red_curve_2][value>=halfmax]
g = [green_curve_1, green_curve_2][value>=halfmax]
b = [blue_curve_1, blue_curve_2][value>=halfmax]
Замените выражения *_curve_1
и *_curve_2
на константы или наклоны или кривые слева или справа от средней точки соответственно.
Я оставлю эти замены вам, но, например:
-
red_curve_1
и blue_curve_2
просто 0
-
green_curve_1
255*(value-minimum)/(halfmax-minimum)
- и др.
Ответ 4
"Мы ощущаем интенсивность света в логарифмическом масштабе - экспоненциальное линейное изменение интенсивности будет рассматриваться как линейное линейное изменение" https://courses.cs.washington.edu/courses/cse455/09wi/Lects/lect11.pdf
Из https://en.wikipedia.org/wiki/RGB_color_model: "RGB-значение интенсивности входного сигнала (0,5, 0,5, 0,5) выдает только около 22% полной яркости (1,0, 1,0, 1,0) вместо 50% "
Это приводит к коричневатому пятну в 2,5 в примере @martineau, где он должен быть желтым, а голубой - в 1,5, чтобы получить надлежащий градиент оттенка.
Таким образом, формула, которую вы должны использовать, чтобы получить градиент, не обязательно соответствует желаемой. (извините, что не ответил на ваш вопрос напрямую)
Но может быть удобно преобразовать в модель цветового пространства HSV или HLS, использовать H (для оттенка) и использовать его в качестве входных данных, а затем преобразовать обратно в RGB для отображения. то есть:
colorsys.hsv_to_rgb(value, 1, 1)
https://docs.python.org/2/library/colorsys.html