Aligerar programáticamente un color

Motivación

Me gustaría encontrar una manera de tomar un color arbitrario y aclararlo en algunos tonos, para que pueda crear programáticamente un degradado agradable desde un color hasta una versión más ligera. El degradado se usará como fondo en una UI.

Posibilidad 1

Obviamente, puedo dividir los valores RGB y boostlos individualmente en una cierta cantidad. ¿Es esto realmente lo que quiero?

Posibilidad 2

Mi segundo pensamiento fue convertir el RGB a HSV / HSB / HSL (Matiz, Saturación, Valor / Brillo / Luminosidad), boost un poco el brillo, disminuir la saturación un poco, y luego convertirlo nuevamente a RGB. ¿Esto tendrá el efecto deseado en general?

Yo iría por la segunda opción. En términos generales, el espacio RGB no es realmente bueno para hacer la manipulación del color (crear transición de un color a otro, aclarar / oscurecer un color, etc.). A continuación se muestran dos sitios que he encontrado con una búsqueda rápida para convertir de / a RGB a / desde HSL:

  • de los “Fundamentos de gráficos por computadora”
  • algún código fuente en C # – debería ser fácil de adaptar a otros lenguajes de progtwigción.

Como dijo Wedge , quieres multiplicar para hacer las cosas más shinys, pero eso solo funciona hasta que uno de los colores se sature (es decir, llegue a 255 o más). En ese punto, puede fijar los valores a 255, pero estará sutilmente cambiando el tono a medida que se aclare. Para mantener el tono, desea mantener la relación de (medio-bajo) / (más alto-más bajo).

Aquí hay dos funciones en Python. El primero implementa el enfoque ingenuo que simplemente fija los valores RGB en 255 si pasan de largo. El segundo redistribuye los valores en exceso para mantener el matiz intacto.

def clamp_rgb(r, g, b): return min(255, int(r)), min(255, int(g)), min(255, int(b)) def redistribute_rgb(r, g, b): threshold = 255.999 m = max(r, g, b) if m <= threshold: return int(r), int(g), int(b) total = r + g + b if total >= 3 * threshold: return int(threshold), int(threshold), int(threshold) x = (3 * threshold - total) / (3 * m - total) gray = threshold - x * m return int(gray + x * r), int(gray + x * g), int(gray + x * b) 

Creé un degradado comenzando con el valor RGB (224,128,0) y multiplicándolo por 1.0, 1.1, 1.2, etc., hasta 2.0. La mitad superior es el resultado usando clamp_rgb y la mitad inferior es el resultado con redistribute_rgb . Creo que es fácil ver que la redistribución de los desbordamientos da un resultado mucho mejor, sin tener que abandonar el espacio de color RGB.

Gradiente de luminosidad con sujeción (arriba) y redistribución (abajo)

A modo de comparación, este es el mismo degradado en los espacios de color HLS y HSV, según lo implementado por el módulo colorsys de Python. Solo se modificó el componente L y se realizó la fijación en los valores RGB resultantes. Los resultados son similares, pero requieren conversiones de espacio de color para cada píxel.

Gradiente de luminosidad con HLS (arriba) y HSV (abajo)

Cª#:

 public static Color Lighten(Color inColor, double inAmount) { return Color.FromArgb( inColor.A, (int) Math.Min(255, inColor.R + 255 * inAmount), (int) Math.Min(255, inColor.G + 255 * inAmount), (int) Math.Min(255, inColor.B + 255 * inAmount) ); } 

Lo he usado por todos lados.

La clase ControlPaint en el espacio de nombres System.Windows.Forms tiene métodos estáticos Light and Dark:

 public static Color Dark(Color baseColor, float percOfDarkDark); 

Estos métodos usan la implementación privada de HLSColor. Ojalá esta estructura fuera pública y en System.Drawing.

Alternativamente, puede utilizar GetHue, GetSaturation, GetBrightness en Color struct para obtener componentes HSB. Lamentablemente, no encontré la conversión inversa.

Convierta a RGB e interpole linealmente entre el color original y el color del blanco (a menudo blanco). Entonces, si quieres 16 tonos entre dos colores, haces:

 for(i = 0; i < 16; i++) { colors[i].R = start.R + (i * (end.R - start.R)) / 15; colors[i].G = start.G + (i * (end.G - start.G)) / 15; colors[i].B = start.B + (i * (end.B - start.B)) / 15; } 

Para obtener una versión más clara o más oscura de un color determinado, debe modificar su brillo. Puede hacerlo fácilmente incluso sin convertir su color a color HSL o HSB. Por ejemplo, para hacer un color más claro, puedes usar el siguiente código:

 float correctionFactor = 0.5f; float red = (255 - color.R) * correctionFactor + color.R; float green = (255 - color.G) * correctionFactor + color.G; float blue = (255 - color.B) * correctionFactor + color.B; Color lighterColor = Color.FromArgb(color.A, (int)red, (int)green, (int)blue); 

Si necesita más detalles, lea la historia completa en mi blog .

Una pregunta muy similar, con respuestas útiles, se preguntó previamente: ¿Cómo puedo determinar la variante de color más oscura o más clara de un color dado?

Respuesta corta: multiplique los valores RGB por una constante si solo necesita “lo suficientemente bueno”, traduzca a HSV si necesita precisión.

Usé la respuesta de Andrew y la respuesta de Mark para hacer esto (a partir de 1/2013 no hay entrada de rango para ff).

 function calcLightness(l, r, g, b) { var tmp_r = r; var tmp_g = g; var tmp_b = b; tmp_r = (255 - r) * l + r; tmp_g = (255 - g) * l + g; tmp_b = (255 - b) * l + b; if (tmp_r > 255 || tmp_g > 255 || tmp_b > 255) return { r: r, g: g, b: b }; else return { r:parseInt(tmp_r), g:parseInt(tmp_g), b:parseInt(tmp_b) } } 

enter image description here

Convirtiendo a HS (LVB), aumentando el brillo y luego volviendo a RGB es la única forma de aligerar el color de manera confiable sin afectar los valores de tono y saturación (es decir, solo aclarar el color sin cambiarlo de ninguna otra manera).

He hecho esto en ambos sentidos: obtienes resultados mucho mejores con Possibility 2.

Cualquier algoritmo simple que construyas para la Posibilidad 1 probablemente funcionará bien solo para un rango limitado de saturaciones iniciales.

Debería examinar Poss 1 si (1) puede restringir los colores y los brillos utilizados, y (2) está realizando el cálculo mucho en una representación.

Generar el fondo para una interfaz de usuario no necesitará muchos cálculos de sombreado, por lo que sugiero Poss 2.

-Alabama.

SI desea producir un fundido de gradiente, sugeriría la siguiente optimización: en lugar de hacer RGB-> HSB-> RGB para cada color individual, solo debe calcular el color de destino. Una vez que conoces el objective RGB, puedes simplemente calcular los valores intermedios en el espacio RGB sin tener que convertir de ida y vuelta. Ya sea que usted calcule una transición de uso lineal algún tipo de curva depende de usted.

Método 1: Convierta RGB a HSL, ajuste HSL, convierta de nuevo a RGB.

Método 2: actualice los valores de color RGB – http://en.wikipedia.org/wiki/Lerp_(computing)

Vea mi respuesta a esta pregunta similar para una implementación de C # del método 2.

Imagina que tu alfa se mezcló con blanco:

 oneMinus = 1.0 - amount r = amount + oneMinus * r g = amount + oneMinus * g b = amount + oneMinus * b 

donde la amount es de 0 a 1, donde 0 devuelve el color original y 1 vuelve blanco.

Es posible que desee combinar con cualquier color de fondo si se ilumina para mostrar algo deshabilitado:

 oneMinus = 1.0 - amount r = amount * dest_r + oneMinus * r g = amount * dest_g + oneMinus * g b = amount * dest_b + oneMinus * b 

donde (dest_r, dest_g, dest_b) es el color que se combina y la amount es de 0 a 1, con cero regresando (r, g, b) y 1 regresando (dest.r, dest.g, dest.b)

No encontré esta pregunta hasta que se convirtió en una pregunta relacionada con mi pregunta original.

Sin embargo, utilizando el conocimiento de estas excelentes respuestas. Arreglé una bonita función de dos líneas para esto:

Programmatically Aclarar u oscurecer un color hexadecimal (o rgb, y mezclar colores)

Es una versión del método 1. Pero teniendo en cuenta la saturación excesiva. Como dijo Keith en su respuesta anterior; use Lerp para resolver el mismo problema que Mark mencionó, pero sin redistribución. Los resultados de shadeColor2 deberían estar mucho más cerca de hacerlo de la manera correcta con HSL, pero sin la sobrecarga.

Hubiera intentado con el número 1 primero, pero el # 2 parece bastante bueno. Intente hacerlo usted mismo y vea si está satisfecho con los resultados, parece que le tomará tal vez 10 minutos para preparar una prueba.

Técnicamente, no creo que ninguno sea correcto, pero creo que quieres una variante de la opción n. ° 2. El problema es que con RGB 990000 y “aligerar”, realmente solo se agregaría al canal Rojo (Valor, Brillo, Luminosidad) hasta llegar a FF. Después de eso (rojo fijo), estaría reduciendo la saturación hasta llegar al blanco sólido.

Las conversiones se vuelven molestas, especialmente porque no se puede ir directamente a RGB y Lab, pero creo que realmente se desea separar los valores de crominancia y luminancia, y simplemente modificar la luminancia para lograr realmente lo que se desea.

Encontrarás el código para convertir entre espacios de color en la biblioteca de color-tools ruby

Tengo una publicación de blog que muestra cómo hacer esto en Delphi. Es bastante simple porque las funciones ColorRGBToHSL y ColorHLSToRGB son parte de la biblioteca estándar.

Aquí hay un ejemplo de aligerar un color RGB en Python:

 def lighten(hex, amount): """ Lighten an RGB color by an amount (between 0 and 1), eg lighten('#4290e5', .5) = #C1FFFF """ hex = hex.replace('#','') red = min(255, int(hex[0:2], 16) + 255 * amount) green = min(255, int(hex[2:4], 16) + 255 * amount) blue = min(255, int(hex[4:6], 16) + 255 * amount) return "#%X%X%X" % (int(red), int(green), int(blue)) 

Un poco tarde para la fiesta, pero si usa javascript o nodejs , puede usar la biblioteca tinycolor y manipular el color de la forma que desee:

 tinycolor("red").lighten().desaturate().toHexString() // "#f53d3d" 
Intereting Posts