计算一系列值的RGB值以创建热图

时间:2013-12-26 23:07:37

标签: python colors rgb heatmap linear-interpolation

我正在尝试使用python创建热图。为此,我必须为可能值范围内的每个值分配RGB值。我想把颜色从蓝色(最小值)变为绿色到红色(最大值)。

下面的图片示例解释了我如何看待颜色组成:我们的范围从1(纯蓝色)到3(纯红色),2之间的颜色与绿色相似。

color composition RGB in range(1-3)

我读到了线性插值并编写了一个函数(或多或少)处理最小值和最大值之间范围内某个值的计算并返回RGB元组。它使用ifelif条件(这并不能让我完全满意):

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条件为每个颜色值编写一个函数而不使用。有人有想法吗?非常感谢你!

4 个答案:

答案 0 :(得分:31)

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

答案 1 :(得分:15)

这是另一种方法,虽然不尽可能短,但更为通用,因为它没有针对您的特定颜色进行硬编码。这意味着它还可以用于在任意颜色的可变大小调色板上线性插值指定范围的值。

另请注意,颜色可以在其他颜色空间中进行插值,从而产生比其他颜色空间更令人愉悦的结果。从我提交给标题为Range values to pseudocolor的相关问题的两个单独答案中获得的不同结果中说明了这一点。

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

答案 2 :(得分:2)

您通常可以使用索引将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_1blue_curve_2只是0
  • green_curve_1255*(value-minimum)/(halfmax-minimum)

答案 3 :(得分:1)

“我们以对数标度感知光强度 - 指数强度斜坡将被视为线性 “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%”

这导致@martineau示例中的褐色污迹为2.5,其应为黄色,青色为1.5,以获得适当的色调梯度。

所以你应该用来获得渐变的公式不一定是你想要的。 (抱歉没有直接回答你的问题)

但转换为HSV或HLS色彩空间模型可能很方便,并使用H(用于色调)并将其用作输入,并转换回RGB用于显示目的。 即:

colorsys.hsv_to_rgb(value, 1, 1)

https://docs.python.org/2/library/colorsys.html