Calculate RGB value for a range of values to create heat map

Viewed 44309

I am trying to create a heat map with python. For this I have to assign an RGB value to every value in the range of possible values. I thought of changing the color from blue (minimal value) over green to red (maximal value).

The picture example below explains how I thought of the color composition: We have a range from 1 (pure blue) to 3 (pure red), 2 is in between resembled by green.

color composition RGB in range(1-3)

I read about linear interpolation and wrote a function that (more or less) handles the calculation for a certain value in the range between a minimum and a maximum and returns an RGB tuple. It uses if and elif conditions (which does not make me completely happy):

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)

However I wonder if one could write a function for each color value without using if conditions. Does anybody have an idea? Thank you a lot!

6 Answers

"We sense light intensity on a logarithmic scale – an exponential intensity ramp will be seen as a linear ramp" https://courses.cs.washington.edu/courses/cse455/09wi/Lects/lect11.pdf

From the https://en.wikipedia.org/wiki/RGB_color_model: "an input intensity RGB value of (0.5, 0.5, 0.5) only outputs about 22% of full brightness (1.0, 1.0, 1.0), instead of 50%"

This leads to the brownish smudge at 2.5 in @martineau example, where it should be yellow, and cyan at 1.5 in order to get a proper hue gradient.

So the formula you should use to get the gradient is not necessarily what you will want. (sorry for not answering your question directly)

But it might be handy to convert to the HSV or HLS color space model, and use H (for hue) and use that as input, and convert back to RGB for display purposes. ie:

colorsys.hsv_to_rgb(value, 1, 1)

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

For whoever do not feel like carrying all the code around, the "terminedia" package packs a gradient class which can handle general gradients with an arbitrary number of color stops, at arbitrary positions.

The resulting ColorGradient instance can be then used with an index from 0 to 1 to get the desired color at the given point.

For example, for the colors given as [(4, 4, 4), (226, 75, 20), (4, 162, 221)], one can do:

In [286]: from terminedia import ColorGradient

In [287]: g =  ColorGradient([(0, (4,4,4)), (0.5, (226, 75, 20)), (1, (4, 162, 221))])

In [288]: g[0.2]
Out[288]: <Color (92, 32, 10)>

In [289]: print([tuple(g[i/25]) for i in range(26)])
[(4, 4, 4), (21, 9, 5), (39, 15, 6), (57, 21, 7), (75, 26, 9), (92, 32, 10), (110, 38, 11), (128, 43, 12), (146, 49, 14), (163, 55, 15), (181, 60, 16), (199, 66, 18), (217, 72, 19), (217, 78, 28), (199, 85, 44), (181, 92, 60), (163, 99, 76), (146, 106, 92), (128, 113, 108), (110, 120, 124), (92, 127, 140), (75, 134, 156), (57, 141, 172), (39, 148, 188), (21, 155, 204), (4, 162, 221)]

The current released version of terminedia (0.4.3) can do that - the development code (https://github.com/jsbueno/terminedia/) signature made the stop positions optional when creating the Gradient, and the colors get automatically evenly spaced. That means that in versions after 0.4.3, the same gradient can be created with: g = ColorGradient( [(4, 4, 4), (226, 75, 20), (4, 162, 221)])

After test in blender, you need to limit the value between the minimum and maximum, then the result is right

import numpy as np

def convert_to_rgb(minimum, maximum, value):
    value = np.clip(value, minimum, maximum)
    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/255.0,g/255.0,b/255.0)
Related