Algoritmo para mistura de colors aditivas para valores RGB

Eu estou procurando um algoritmo para fazer mistura de colors aditivas para valores RGB.

É tão simples quanto adicionar os valores RGB juntos a um máximo de 256?

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

Depende do que você quer e pode ajudar a ver quais são os resultados de diferentes methods.

Se você quiser

 Vermelho + preto = vermelho
 Vermelho + Verde = Amarelo
 Vermelho + Verde + Azul = Branco
 Vermelho + Branco = Branco 
 Preto + branco = branco

em seguida, adicionando com um grampo funciona (por exemplo, min(r1 + r2, 255) ) Este é mais parecido com o modelo de luz que você se referiu.

Se você quiser

 Vermelho + preto = vermelho escuro
 Vermelho + Verde = Amarelo Escuro
 Vermelho + Verde + Azul = Cinza Escuro
 Vermelho + branco = rosa
 Preto + branco = cinza

então você precisará calcular a média dos valores (por exemplo, (r1 + r2) / 2 ). Isso funciona melhor para clarear / escurecer colors e criar gradientes.

Para misturar usando canais alfa, você pode usar essas 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 é a cor da tinta. bg é o fundo. r é a cor resultante. 1.0e-6 é apenas um número muito pequeno, para compensar erros de arredondamento.

NOTA: Todas as variables ​​usadas aqui estão no intervalo [0.0, 1.0]. Você tem que dividir ou multiplicar por 255 se quiser usar valores no intervalo [0, 255].

Por exemplo, 50% vermelho em cima de 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 

A cor resultante é: (0.67, 0.33, 0.00, 0.75) ou 75% marrom (ou laranja escuro).


Você também pode inverter essas 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)); 

ou

 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; 

As fórmulas calcularão que o fundo ou a cor da pintura teriam que ser para produzir a cor resultante resultante.


Se o seu fundo for opaco, o resultado também será opaco. A cor do primeiro plano pode então ter um intervalo de valores com diferentes valores alfa. Para cada canal (vermelho, verde e azul), você precisa verificar qual intervalo de alfas resulta em valores válidos (0 - 1).

Curiosidade: Os valores RGB do computador são derivados da raiz quadrada do stream de fótons. Então, como uma function geral, sua matemática deve levar isso em conta. A function geral para isso para um determinado canal é:

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

Onde a e b são as colors a serem mescladas, e t é um número de 0 a 1 representando o ponto na mescla que você deseja entre a e b.

O canal alfa é diferente; não representa a intensidade do fóton, apenas a porcentagem de fundo que deve aparecer; então, ao misturar valores alfa, a média linear é suficiente:

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

Então, para lidar com a mistura de duas colors, usando essas duas funções, o seguinte pseudocódigo deve ser bom para você:

 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, eu anseio por uma linguagem de programação e teclado que permita representar a matemática (ou mais) de forma limpa (o caractere unicode da linha de combinação não funciona para sobrescritos, símbolos e uma vasta gama de outros caracteres) e interpretá-lo corretamente. sqrt ((1-t) * pow (a, 2) + t * pow (b, 2)) simplesmente não lê como limpa.

Alguns pontos:

  • Eu acho que você quer usar min em vez de max
  • Eu acho que você quer usar 255 em vez de 256

Isso vai dar:

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

No entanto, a maneira “natural” de misturar colors é usar a média, e então você não precisa do min:

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

Função Javascript para misturar colors rgba

c1, c2 e resultado – JSon é 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 } } 

PYTHON COLOR MISTURA ATRAVÉS DE ADIÇÃO EM ESPAÇO CMYK

Uma maneira possível de fazer isso é primeiro converter as colors para o formato CMYK , adicioná-las e depois reconverter para RGB.

Aqui está um exemplo de código em 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) 

O resultado para a sua pergunta seria então (assumindo uma meia metade das suas duas colors:

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

onde os 0.5’s estão lá para dizer que nós misturamos 50% da primeira cor com 50% da segunda cor.

Sim, é tão simples quanto isso. Outra opção é encontrar a média (para criar gradientes).

Isso realmente depende apenas do efeito que você deseja alcançar.

No entanto, quando Alpha é adicionado, fica complicado. Existem vários methods diferentes para mesclar usando um alfa.

Um exemplo de mistura alfa simples: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

Aqui está uma class c ++ autônoma altamente otimizada, domínio público, com ponto flutuante e dois mecanismos de mistura de 8 bits otimizados de maneira diferente nos formatos de function e macro, assim como uma discussão técnica do problema em questão e como e a importância de otimização deste problema:

https://github.com/fyngyrz/colorblending

Escreveu / usou algo como a resposta de mistura de sRGB @Markus Jarderot (que não é corrigida por gama, já que é o legado padrão) 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; 

mais informações: what-every-coder-should-know-about-gama