Algorithme pour le mélange de couleurs additives pour les valeurs RVB

Je cherche un algorithme pour faire un mélange de couleurs additif pour les valeurs RVB.

Est-ce aussi simple que d’append les valeurs RVB ensemble à un maximum de 256?

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

Cela dépend de ce que vous voulez, et cela peut aider à voir quels sont les résultats des différentes méthodes.

Si tu veux

 Rouge + noir = rouge
 Rouge + vert = jaune
 Rouge + vert + bleu = blanc
 Rouge + blanc = blanc 
 Noir + blanc = blanc

puis append avec une pince fonctionne (par exemple min(r1 + r2, 255) ) Ceci est plus comme le modèle de lumière que vous avez mentionné.

Si tu veux

 Rouge + noir = rouge foncé
 Rouge + vert = jaune foncé
 Rouge + Vert + Bleu = Gris Foncé
 Rouge + blanc = rose
 Noir + blanc = gris

alors vous devrez faire la moyenne des valeurs (par exemple (r1 + r2) / 2 ) Cela fonctionne mieux pour éclaircir / assombrir les couleurs et créer des dégradés.

Pour fusionner en utilisant des canaux alpha, vous pouvez utiliser ces formules:

 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 est la couleur de la peinture. bg est l'arrière-plan. r est la couleur résultante. 1.0e-6 n'est qu'un très petit nombre, pour compenser les erreurs d'arrondi.

REMARQUE: Toutes les variables utilisées ici sont dans la plage [0.0, 1.0]. Vous devez diviser ou multiplier par 255 si vous souhaitez utiliser des valeurs comsockets entre 0 et 255.

Par exemple, 50% de rouge sur 50% de vert:

 // 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 

La couleur résultante est: (0.67, 0.33, 0.00, 0.75) ou 75% de brun (ou orange foncé).


Vous pouvez également inverser ces formules:

 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; 

Les formules calculeront que la couleur d'arrière-plan ou de peinture devra être pour produire la couleur résultante donnée.


Si votre arrière-plan est opaque, le résultat serait également opaque. La couleur de premier plan peut alors prendre une plage de valeurs avec des valeurs alpha différentes. Pour chaque canal (rouge, vert et bleu), vous devez vérifier quelle plage d'alphas donne des valeurs valides (0 - 1).

Fait amusant: les valeurs RVB de l’ordinateur sont dérivées de la racine carrée du stream de photons. Donc, en règle générale, vos calculs devraient en tenir compte. La fonction générale pour ceci pour un canal donné est:

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

Où a et b sont les couleurs à mélanger, et t est un nombre compris entre 0 et 1 représentant le point du mélange que vous voulez entre a et b.

Le canal alpha est différent. il ne représente pas l’intensité des photons, juste le pourcentage d’arrière-plan qui devrait apparaître à travers; donc en mélangeant les valeurs alpha, la moyenne linéaire est suffisante:

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

Donc, pour gérer le mélange de deux couleurs, en utilisant ces deux fonctions, le pseudocode suivant devrait vous faire du bien:

 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 

Incidemment, je souhaite un langage de programmation et un clavier qui permettent de représenter les mathématiques proprement (ou plus) (le caractère unicode de surlignage combinant ne fonctionne pas pour les exposants, les symboles et un large éventail d’autres caractères) et les interprète correctement. sqrt ((1-t) * pow (a, 2) + t * pow (b, 2)) ne lit pas comme propre.

Quelques points:

  • Je pense que vous voulez utiliser min au lieu de max
  • Je pense que vous voulez utiliser 255 au lieu de 256

Cela donnera:

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

Cependant, la manière “naturelle” de mélanger les couleurs consiste à utiliser la moyenne, et alors vous n’avez pas besoin du min:

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

Fonction Javascript pour mélanger les couleurs rgba

c1, c2 et result – JSON comme 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 } } 

MÉLANGE DE COULEURS DE PYTHON PAR ADDITION DANS UN ESPACE CMJN

Pour ce faire, convertissez d’abord les couleurs au format CMJN , ajoutez-les au préalable et reconvertissez-les en RVB.

Voici un exemple de code 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) 

Le résultat à votre question serait alors (en supposant un demi-mélange de vos deux couleurs:

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

où les 0,5 sont là pour dire que nous mélangeons 50% de la première couleur avec 50% de la deuxième couleur.

Oui, c’est aussi simple que cela. Une autre option consiste à trouver la moyenne (pour créer des dégradés).

Cela dépend vraiment de l’effet que vous souhaitez atteindre.

Cependant, quand Alpha est ajouté, cela devient compliqué. Il existe un certain nombre de méthodes différentes pour mélanger en utilisant un alpha.

Un exemple de mélange alpha simple: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

Voici une classe C ++ autonome hautement optimisée, domaine public, avec virgule flottante et deux mécanismes de mélange 8 bits optimisés différemment dans les formats de fonction et de macro, ainsi qu’une discussion technique du problème et de son importance. de l’optimisation de ce problème:

https://github.com/fyngyrz/colorblending

Avoir écrit / utilisé quelque chose comme la réponse de mélange sRGB @Markus Jarderot (qui n’est pas corrigée du gamma puisque c’est l’inheritance par défaut) en utilisant 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; 

plus d’infos: quel-chaque-codeur-devrait-savoir-sur-gamma