Algoritmo per miscelazione colore additiva per valori RGB

Sto cercando un algoritmo per fare il mixaggio additivo dei colors per i valori RGB.

È semplice come aggiungere i valori RGB insieme a un massimo di 256?

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

Dipende da ciò che vuoi e può aiutare a vedere quali sono i risultati di diversi metodi.

Se vuoi

 Rosso + Nero = Rosso
 Rosso + Verde = Giallo
 Rosso + Verde + Blu = Bianco
 Rosso + Bianco = Bianco 
 Nero + Bianco = Bianco

poi aggiungendo con un morsetto funziona (es. min(r1 + r2, 255) ) Questo è più simile al modello di luce a cui ti sei riferito.

Se vuoi

 Rosso + Nero = Rosso scuro
 Rosso + Verde = Giallo scuro
 Rosso + Verde + Blu = Grigio scuro
 Rosso + Bianco = Rosa
 Nero + Bianco = Grigio

quindi dovrai calcolare la media dei valori (ad esempio (r1 + r2) / 2 ) Funziona meglio per schiarire / scurire i colors e creare sfumature.

Per fondere usando i canali alfa, puoi usare queste formule:

 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 è il colore della vernice. bg è lo sfondo. r è il colore risultante. 1.0e-6 è solo un numero molto piccolo, per compensare gli errori di arrotondamento.

NOTA: tutte le variabili utilizzate qui sono comprese nell'intervallo [0.0, 1.0]. Devi dividere o moltiplicare per 255 se vuoi usare valori nell'intervallo [0, 255].

Ad esempio, il 50% di rosso sopra il 50% di 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 

Il colore risultante è: (0.67, 0.33, 0.00, 0.75) o marrone al 75% (o arancione scuro).


È anche ansible invertire queste formule:

 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; 

Le formule calcoleranno che lo sfondo o il colore della vernice dovrebbero essere per produrre il dato colore risultante.


Se il tuo sfondo è opaco, il risultato sarebbe anche opaco. Il colore di primo piano potrebbe quindi assumere un intervallo di valori con diversi valori alfa. Per ciascun canale (rosso, verde e blu), è necessario verificare quale intervallo di alfa risulta in valori validi (0 - 1).

Fatto divertente: i valori RGB del computer derivano dalla radice quadrata del stream di fotoni. Quindi, come una funzione generale, la tua matematica dovrebbe tenerne conto. La funzione generale per questo per un dato canale è:

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

Dove aeb sono i colors da sfumare, e t è un numero compreso tra 0 e 1 che rappresenta il punto nella fusione che si desidera tra a e b.

Il canale alfa è diverso; non rappresenta l’intensità del fotone, ma solo la percentuale di sfondo che dovrebbe mostrare; quindi quando si combinano i valori alfa, la media lineare è sufficiente:

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

Quindi, per gestire la fusione di due colors, usando queste due funzioni, il seguente pseudocodice dovrebbe farti del bene:

 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 

Incidentalmente, desidero un linguaggio di programmazione e una tastiera che entrambi consentano di rappresentare la matematica (o più) in modo pulito (il carattere unicode combinato non funziona per superscript, simboli e una vasta gamma di altri caratteri) e di interpretarlo correttamente. sqrt ((1-t) * pow (a, 2) + t * pow (b, 2)) non viene letto come pulito.

Pochi punti

  • Penso che tu voglia usare min anziché max
  • Penso che tu voglia usare 255 invece di 256

Questo darà:

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

Tuttavia, il modo “naturale” di mescolare i colors è usare la media, e quindi non è necessario il minimo:

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

Funzione Javascript per unire i colors rgba

c1, c2 e result – JSON è come 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 } } 

MISCELAZIONE DEL COLORE DI PYTHON ATTRAVERSO L’ AGGIUNTA IN SPAZIO CMYK

Un modo ansible per farlo è prima convertire i colors in formato CMYK , aggiungerli lì e poi riconvertire in RGB.

Ecco un codice di esempio in 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) 

Il risultato per la tua domanda sarebbe quindi (assumendo una miscela metà-metà dei tuoi due colors:

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

dove gli 0.5 sono lì per dire che mescoliamo il 50% del primo colore con il 50% del secondo colore.

Sì, è così semplice. Un’altra opzione è trovare la media (per la creazione di gradienti).

Dipende davvero solo dall’effetto che vuoi ottenere.

Tuttavia, quando Alpha viene aggiunto, diventa complicato. Esistono diversi metodi per fondersi usando un alfa.

Un esempio di semplice miscelazione alfa: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

Ecco una class c ++ standalone altamente ottimizzata, di dominio pubblico, con virgola mobile e due meccanismi di fusione a 8 bit ottimizzati in modo diverso nei formati funzione e macro, oltre a una discussione tecnica sia sul problema in questione che su come e sull’importanza di, ottimizzazione di questo problema:

https://github.com/fyngyrz/colorblending

Ho scritto / usato qualcosa come @Markus Jarderot ‘s sRGB blending answer (che non è corretto per la gamma dato che è l’eredità predefinita) 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; 

maggiori informazioni: what-every-coder-should-know-about-gamma