Algoritmo de mezcla de colores aditiva para valores RGB

Estoy buscando un algoritmo para hacer mezclas de colores aditivos para valores RGB.

¿Es tan simple como agregar los valores RGB juntos a un máximo de 256?

(r1, g1, b1) + (r2, g2, b2) = (min(r1+r2, 256), min(g1+g2, 256), min(b1+b2, 256)) 

Depende de lo que desee, y puede ayudar a ver cuáles son los resultados de diferentes métodos.

Si tu quieres

 Rojo + negro = rojo
 Rojo + Verde = Amarillo
 Rojo + Verde + Azul = Blanco
 Rojo + blanco = blanco 
 Negro + blanco = blanco

a continuación, agregue con una abrazadera funciona (por ejemplo, min(r1 + r2, 255) ) Esto es más como el modelo de luz que ha mencionado.

Si tu quieres

 Rojo + negro = rojo oscuro
 Rojo + verde = amarillo oscuro
 Rojo + Verde + Azul = Gris Oscuro
 Rojo + blanco = rosa
 Negro + blanco = gris

entonces necesitarás promediar los valores (p. ej., (r1 + r2) / 2 ). Esto funciona mejor para aclarar / oscurecer colores y crear degradados.

Para mezclar usando canales alfa, puede usar estas fórmulas:

 r = new Color(); rA = 1 - (1 - fg.A) * (1 - bg.A); if (rA < 1.0e-6) return r; // Fully transparent -- R,G,B not important rR = fg.R * fg.A / rA + bg.R * bg.A * (1 - fg.A) / rA; rG = fg.G * fg.A / rA + bg.G * bg.A * (1 - fg.A) / rA; rB = fg.B * fg.A / rA + bg.B * bg.A * (1 - fg.A) / rA; 

fg es el color de la pintura. bg es el fondo. r es el color resultante. 1.0e-6 es solo un número realmente pequeño, para compensar los errores de redondeo.

NOTA: Todas las variables utilizadas aquí están en el rango [0.0, 1.0]. Debe dividir o multiplicar por 255 si desea usar valores en el rango [0, 255].

Por ejemplo, 50% rojo encima 50% verde:

 // background, 50% green var bg = new Color { R = 0.00, G = 1.00, B = 0.00, A = 0.50 }; // paint, 50% red var fg = new Color { R = 1.00, G = 0.00, B = 0.00, A = 0.50 }; // The result var r = new Color(); rA = 1 - (1 - fg.A) * (1 - bg.A); // 0.75 rR = fg.R * fg.A / rA + bg.R * bg.A * (1 - fg.A) / rA; // 0.67 rG = fg.G * fg.A / rA + bg.G * bg.A * (1 - fg.A) / rA; // 0.33 rB = fg.B * fg.A / rA + bg.B * bg.A * (1 - fg.A) / rA; // 0.00 

El color resultante es: (0.67, 0.33, 0.00, 0.75) o 75% marrón (o naranja oscuro).


También puedes invertir estas fórmulas:

 var bg = new Color(); if (1 - fg.A <= 1.0e-6) return null; // No result -- 'fg' is fully opaque if (rA - fg.A < -1.0e-6) return null; // No result -- 'fg' can't make the result more transparent if (rA - fg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important bg.A = 1 - (1 - rA) / (1 - fg.A); bg.R = (rR * rA - fg.R * fg.A) / (bg.A * (1 - fg.A)); bg.G = (rG * rA - fg.G * fg.A) / (bg.A * (1 - fg.A)); bg.B = (rB * rA - fg.B * fg.A) / (bg.A * (1 - fg.A)); 

o

 var fg = new Color(); if (1 - bg.A <= 1.0e-6) return null; // No result -- 'bg' is fully opaque if (rA - bg.A < -1.0e-6) return null; // No result -- 'bg' can't make the result more transparent if (rA - bg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important fg.A = 1 - (1 - rA) / (1 - bg.A); fg.R = (rR * rA - bg.R * bg.A * (1 - fg.A)) / fg.A; fg.G = (rG * rA - bg.G * bg.A * (1 - fg.A)) / fg.A; fg.B = (rB * rA - bg.B * bg.A * (1 - fg.A)) / fg.A; 

Las fórmulas calcularán que el fondo o el color de la pintura tendrían que ser para producir el color resultante dado.


Si su fondo es opaco, el resultado también sería opaco. El color de primer plano podría tomar un rango de valores con diferentes valores alfa. Para cada canal (rojo, verde y azul), debe verificar qué rango de alfas arroja valores válidos (0 - 1).

Dato curioso: los valores RGB de la computadora se derivan de la raíz cuadrada del flujo de fotones. Entonces, como una función general, tus cálculos deberían tener eso en cuenta. La función general para esto para un canal dado es:

 blendColorValue(a, b, t) return sqrt((1 - t) * a^2 + t * b^2) 

Donde a y b son los colores para mezclar, y t es un número de 0-1 que representa el punto en la mezcla que desea entre a y b.

El canal alfa es diferente; no representa la intensidad de los fotones, solo el porcentaje de fondo que debería mostrarse; entonces cuando se mezclan valores alfa, el promedio lineal es suficiente:

 blendAlphaValue(a, b, t) return (1-t)*a + t*b; 

Por lo tanto, para manejar la mezcla de dos colores, usando esas dos funciones, el siguiente pseudocódigo debería serle útil:

 blendColors(c1, c2, t) ret [r, g, b].each n -> ret[n] = blendColorValue(c1[n], c2[n], t) ret.alpha = blendAlphaValue(c1.alpha, c2.alpha, t) return ret 

A propósito, anhelo un lenguaje de progtwigción y teclado que permita representar matemáticamente (o más) matemáticamente (el carácter unicode superpuesto de combinación no funciona para superíndices, símbolos y una gran variedad de otros caracteres) e interpretarlo correctamente. sqrt ((1-t) * pow (a, 2) + t * pow (b, 2)) simplemente no se lee como limpio.

Pocos puntos:

  • Creo que quieres usar min en lugar de max
  • Creo que quieres usar 255 en lugar de 256

Esto dará:

(r1, g1, b1) + (r2, g2, b2) = (min (r1 + r2, 255), min (g1 + g2, 255), min (b1 + b2, 255))

Sin embargo, la forma “natural” de mezclar colores es usar el promedio, y luego no necesitas el mínimo:

(r1, g1, b1) + (r2, g2, b2) = ((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2)

Función Javascript para combinar colores rgba

c1, c2 y resultado – JSON es como c1 = {r: 0.5, g: 1, b: 0, a: 0.33}

  var rgbaSum = function(c1, c2){ var a = c1.a + c2.a*(1-c1.a); return { r: (c1.r * c1.a + c2.r * c2.a * (1 - c1.a)) / a, g: (c1.g * c1.a + c2.g * c2.a * (1 - c1.a)) / a, b: (c1.b * c1.a + c2.b * c2.a * (1 - c1.a)) / a, a: a } } 

MEZCLA DE COLOR PYTHON A TRAVÉS DE LA ADICIÓN EN EL ESPACIO CMYK

Una forma posible de hacerlo es convertir primero los colores al formato CMYK , agregarlos allí y luego reconvertirlos a RGB.

Aquí hay un código de ejemplo en Python:

 rgb_scale = 255 cmyk_scale = 100 def rgb_to_cmyk(self,r,g,b): if (r == 0) and (g == 0) and (b == 0): # black return 0, 0, 0, cmyk_scale # rgb [0,255] -> cmy [0,1] c = 1 - r / float(rgb_scale) m = 1 - g / float(rgb_scale) y = 1 - b / float(rgb_scale) # extract out k [0,1] min_cmy = min(c, m, y) c = (c - min_cmy) m = (m - min_cmy) y = (y - min_cmy) k = min_cmy # rescale to the range [0,cmyk_scale] return c*cmyk_scale, m*cmyk_scale, y*cmyk_scale, k*cmyk_scale def cmyk_to_rgb(self,c,m,y,k): """ """ r = rgb_scale*(1.0-(c+k)/float(cmyk_scale)) g = rgb_scale*(1.0-(m+k)/float(cmyk_scale)) b = rgb_scale*(1.0-(y+k)/float(cmyk_scale)) return r,g,b def ink_add_for_rgb(self,list_of_colours): """input: list of rgb, opacity (r,g,b,o) colours to be added, o acts as weights. output (r,g,b) """ C = 0 M = 0 Y = 0 K = 0 for (r,g,b,o) in list_of_colours: c,m,y,k = rgb_to_cmyk(r, g, b) C+= o*c M+=o*m Y+=o*y K+=o*k return cmyk_to_rgb(C, M, Y, K) 

El resultado para su pregunta sería (suponiendo una mezcla a la mitad de sus dos colores:

 r_mix, g_mix, b_mix = ink_add_for_rgb([(r1,g1,b1,0.5),(r2,g2,b2,0.5)]) 

donde los 0.5 están ahí para decir que mezclamos el 50% del primer color con el 50% del segundo color.

Si, es tan simple como eso. Otra opción es encontrar el promedio (para crear degradados).

Realmente solo depende del efecto que quieres lograr.

Sin embargo, cuando se agrega Alpha, se vuelve complicado. Hay una serie de métodos diferentes para combinar usando un alfa.

Un ejemplo de mezcla simple alfa: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

Aquí hay un dominio público de clase c ++ altamente optimizado e independiente, con punto flotante y dos mecanismos de fusión de 8 bits optimizados de forma diferente en formatos de función y macro, así como una discusión técnica tanto del problema en cuestión y cómo, como de la importancia de, la optimización de este problema:

https://github.com/fyngyrz/colorblending

Ha escrito / usado algo como la respuesta de fusión sRGB @Markus Jarderot (que no está corregida con rayos gamma ya que es el legado predeterminado) usando C ++

 //same as Markus Jarderot's answer float red, green, blue; alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha)); red = (front.red * front.alpha / result.alpha + back.red * back.alpha * (1.0 - front.alpha)); green = (front.green * front.alpha / result.alpha + back.green * back.alpha * (1.0 - front.alpha)); blue = (front.blue * front.alpha / result.alpha + back.blue * back.alpha * (1.0 - front.alpha)); //faster but equal output alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha)); red = (back.red * (1.0 - front.alpha) + front.red * frontAlpha); green = (back.green * (1.0 - front.alpha) + front.green * frontAlpha); blue = (back.blue * (1.0 - front.alpha) + front.blue * frontAlpha); //even faster but only works when all values are in range 0 to 255 int red, green, blue; alpha = (255 - (255 - back.alpha)*(255 - front.alpha)); red = (back.red * (255 - front.alpha) + front.red * frontAlpha) / 255; green = (back.green * (255 - front.alpha) + front.green * frontAlpha) / 255; blue = (back.blue * (255 - front.alpha) + front.blue * frontAlpha) / 255; 

más información: what-every-coder-should-know-about-gamma