coolwarm color cmap python colors rgb heatmap linear-interpolation

python - color - Calcule el valor RGB para un rango de valores para crear un mapa de calor



plt color python (4)

Estoy tratando de crear un mapa de calor con python. Para esto tengo que asignar un valor RGB a cada valor en el rango de valores posibles. Pensé en cambiar el color de azul (valor mínimo) sobre verde a rojo (valor máximo).

El ejemplo de la imagen a continuación explica cómo pensé en la composición del color: Tenemos un rango de 1 (azul puro) a 3 (rojo puro), 2 entre el verde y el parecido.

Leí sobre interpolación lineal y escribí una función que (más o menos) maneja el cálculo para un cierto valor en el rango entre un mínimo y un máximo y devuelve una tupla RGB. Utiliza las condiciones if y elif (lo que no me hace completamente feliz):

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)

Sin embargo, me pregunto si se podría escribir una función para cada valor de color sin usar las condiciones. Alguien tiene una idea? ¡Muchas gracias!


"Sentimos la intensidad de la luz en una escala logarítmica: una rampa de intensidad exponencial se verá como una rampa lineal" https://courses.cs.washington.edu/courses/cse455/09wi/Lects/lect11.pdf

De la https://en.wikipedia.org/wiki/RGB_color_model : "un valor RGB de intensidad de entrada de (0.5, 0.5, 0.5) solo genera aproximadamente el 22% del brillo total (1.0, 1.0, 1.0), en lugar del 50% "

Esto lleva a la mancha marrón a 2.5 en el ejemplo de @martineau, donde debería ser amarillo, y cian a 1.5 para obtener un gradiente de matiz adecuado.

Por lo tanto, la fórmula que debe usar para obtener el gradiente no es necesariamente lo que deseará. (perdon por no responder a tu pregunta directamente)

Pero podría ser útil convertirlo al modelo de espacio de color HSV o HLS, usar H (para tono) y usarlo como entrada, y volver a convertir a RGB para fines de visualización. es decir:

colorsys.hsv_to_rgb(value, 1, 1)

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


A menudo puede eliminar un if con un índice en una matriz de dos valores. Python carece de un operador condicional ternario, pero esto funciona:

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]

Reemplace las *_curve_1 y *_curve_2 con las constantes o pendientes o curvas a la izquierda o derecha del punto medio, respectivamente.

Te dejaré esas sustituciones, pero por ejemplo:

  • red_curve_1 y blue_curve_2 son simplemente 0
  • green_curve_1 es 255*(value-minimum)/(halfmax-minimum)
  • etc.

Esta es otra forma de hacerlo que, si bien no es lo más breve posible, es mucho más general ya que no se ha codificado para su conjunto específico de colores. Esto significa que también se puede utilizar para interpolar linealmente un rango específico de valores sobre una paleta de colores arbitrarios de tamaño variable.

También tenga en cuenta que los colores podrían haber sido interpolados en otros espacios de color dando resultados que pueden ser más agradables que en otros. Esto se ilustra en los diferentes resultados obtenidos de las dos respuestas separadas que envié a una pregunta relacionada titulada 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))

Salida numérica:

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)

Aquí está la salida visualizada como un degradado horizontal:


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