web-dev-qa-db-fra.com

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

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

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

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

Cela dépend de ce que vous voulez et il peut être utile de 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 ajouter avec une pince fonctionne (par exemple, min(r1 + r2, 255)). Cela ressemble plus au modèle léger 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 

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

55

Pour mélanger à l'aide de canaux alpha, vous pouvez utiliser ces formules:

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

fg est la couleur de la peinture. bg est l’arrière-plan. r est la couleur résultante. 1.0e-6 est juste un nombre très petit, pour compenser les erreurs d'arrondi.

NOTE: Toutes les variables utilisées ici sont dans l'intervalle [0.0, 1.0]. Vous devez diviser ou multiplier par 255 si vous souhaitez utiliser des valeurs comprises dans la plage [0, 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();
r.A = 1 - (1 - fg.A) * (1 - bg.A); // 0.75
r.R = fg.R * fg.A / r.A + bg.R * bg.A * (1 - fg.A) / r.A; // 0.67
r.G = fg.G * fg.A / r.A + bg.G * bg.A * (1 - fg.A) / r.A; // 0.33
r.B = fg.B * fg.A / r.A + bg.B * bg.A * (1 - fg.A) / r.A; // 0.00

La couleur résultante est: (0.67, 0.33, 0.00, 0.75) ou 75% 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 (r.A - fg.A < -1.0e-6) return null; // No result -- 'fg' can't make the result more transparent
if (r.A - fg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important
bg.A = 1 - (1 - r.A) / (1 - fg.A);
bg.R = (r.R * r.A - fg.R * fg.A) / (bg.A * (1 - fg.A));
bg.G = (r.G * r.A - fg.G * fg.A) / (bg.A * (1 - fg.A));
bg.B = (r.B * r.A - 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 (r.A - bg.A < -1.0e-6) return null; // No result -- 'bg' can't make the result more transparent
if (r.A - bg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important
fg.A = 1 - (1 - r.A) / (1 - bg.A);
fg.R = (r.R * r.A - bg.R * bg.A * (1 - fg.A)) / fg.A;
fg.G = (r.G * r.A - bg.G * bg.A * (1 - fg.A)) / fg.A;
fg.B = (r.B * r.A - bg.B * bg.A * (1 - fg.A)) / fg.A;

Les formules vont calculer que la couleur de fond ou de peinture doit être obtenue pour produire la couleur résultante donnée.


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

77
Markus Jarderot

Fait amusant: les valeurs RVB d'ordinateur sont dérivées de la racine carrée du flux de photons. Donc, en règle générale, votre calcul devrait en tenir compte. La fonction générale pour cela 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 souhaité entre a et b.

Le canal alpha est différent. cela ne représente pas l'intensité des photons, mais seulement le pourcentage d'arrière-plan qui devrait apparaître; Ainsi, lorsque vous mélangez des valeurs alpha, la moyenne linéaire suffit:

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 à la fois de représenter les mathématiques de manière propre (ou plus) (le caractère unicode superposé combiné ne fonctionne pas pour les exposants, les symboles et une vaste gamme de caractères) et pour une interprétation correcte. sqrt ((1-t) * pow (a, 2) + t * pow (b, 2)) ne lit tout simplement pas aussi propre.

45
Fordi

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 est d'utiliser la moyenne, et vous n'avez pas besoin du min:

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

7

Fonction Javascript pour mélanger les couleurs rgba

c1, c2 et résultat - JSON est 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
       }
     } 
6
Nedudi

PYTHONCOULEURM&EACUTE;LANGE&AGRAVE;ADDITIONDANS _ {CMYK _ {ESPACE

Une méthode possible consiste à convertir d'abord les couleurs au format CMYK , à les ajouter puis à les reconvertir au format 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 de votre question serait alors (en supposant un mélange moitié-moitié 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 seconde couleur. 

4
patapouf_ai

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

Cela dépend vraiment de l'effet que vous souhaitez obtenir.

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

Un exemple de fusion alpha simple: http://fr.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

3
John Gietzen

Avoir écrit/utilisé quelque chose comme @Markus Jarderot la réponse de mélange sRGB (qui n'est pas corrigée du gamma puisqu'il s'agit de l'héritage par défaut) à l'aide de 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 / alpha + back.red   * back.alpha * (1.0 - front.alpha));
green = (front.green * front.alpha / alpha + back.green * back.alpha * (1.0 - front.alpha));
blue  = (front.blue  * front.alpha / 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   * front.alpha);
green = (back.green * (1.0 - front.alpha) + front.green * front.alpha);
blue  = (back.blue  * (1.0 - front.alpha) + front.blue  * front.alpha);

//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   * front.alpha) / 255;
green = (back.green * (255 - front.alpha) + front.green * front.alpha) / 255;
blue  = (back.blue  * (255 - front.alpha) + front.blue  * front.alpha) / 255;

plus d'infos: tout-chaque-codeur-devrait-savoir-sur-gamma

1
Top-Master

Quand je suis arrivé ici, je n’ai pas trouvé l’algorithme de "mélange de couleurs additif" que je cherchais, qui est également disponible dans Photoshop et décrit comme "Écran" sur Wikipedia . (Aka "éclaire" ou "inverser multiplie".) Il en résulte un résultat similaire à la combinaison de deux sources lumineuses.

En mode de fusion Écran, les valeurs des pixels des deux couches sont inversées, multipliées, puis inversées à nouveau. Cela donne l'effet inverse de se multiplier. Le résultat est une image plus lumineuse. 

C'est ici:

// (rgb values are 0-255)
function screen(color1, color2) {
    var r = Math.round((1 - (1 - color1.R / 255) * (1 - color2.R / 255)) * 255);
    var g = Math.round((1 - (1 - color1.G / 255) * (1 - color2.G / 255)) * 255);
    var b = Math.round((1 - (1 - color1.B / 255) * (1 - color2.B / 255)) * 255);
    return new Color(r, g, b);
}
0
marsze

Voici une classe c ++ autonome hautement optimisée, un domaine public, avec une virgule flottante et deux mécanismes de fusion 8 bits optimisés différemment dans les formats de fonction et de macro, ainsi qu'une discussion technique sur le problème rencontré, son mode et l'importance de, optimisation de ce problème:

https://github.com/fyngyrz/colorblending

0
fyngyrz

Merci Markus Jarderot, Andras Zoltan et Hkurabko; Voici le code Python permettant de fondre une liste d’images RVB.

En utilisant le code de Markus Jarderot, nous pouvons générer une couleur RGBA. J'utilise ensuite la méthode d'Andras Zoltan et de Hkurabko pour convertir RGBA en RVB.

Je vous remercie!

import numpy as np
def Blend2Color(C1,C2):
    c1,c1a=C1
    c2,c2a=C2
    A = 1 - (1 - c1a) * (1 - c2a);
    if (A < 1.0e-6): 
        return (0,0,0) #Fully transparent -- R,G,B not important
    Result=(np.array(c1)*c1a+np.array(c2)*c2a*(1-c1a))/A
    return Result,A
def RGBA2RGB(RGBA,BackGround=(1,1,1)):# whilt background
    A=RGBA[-1]
    RGB=np.add(np.multiply(np.array(RGBA[:-1]),A),
               np.multiply(np.array(BackGround),1-A))
    return RGB

def BlendRGBList(Clist,AlphaList=None,NFloat=2,ReturnRGB=True,
                 RGB_BackGround=(1,1,1)):
    N=len(Clist)
    if AlphaList==None:
        ClistUse=Clist.copy()
    else:
        if len(AlphaList)==N:
            AlphaListUse=np.multiply(AlphaList,10**NFloat).astype(int)
            ClistUse=np.repeat(np.array(Clist), AlphaListUse, axis=0)
        else:
            raise('len of AlphaList must equal to len of Clist!')
    while N!=1:
        temp=ClistUse.copy()
        ClistUse=[]
        for C in temp[:-1]:
            c1,a1=C
            c2,a2=temp[-1]
            ClistUse.append(Blend2Color(C1=(c1,a1*(1-1/N)),C2=(c2,a2*1/N)))
        N=len(ClistUse)
    Result=np.append(ClistUse[0][0],ClistUse[0][1])
    if ReturnRGB:
        Result=RGBA2RGB(Result,BackGround=RGB_BackGround)

    return Result

Tester

BlendRGBList([[(1,0,0),1],[(0,1,0),1]],ReturnRGB=True)
#array([0.75, 0.5 , 0.25])
BlendRGBList([[(1,0,0),1],[(0,1,0),1]],ReturnRGB=False)
#array([0.66666667, 0.33333333, 0.        , 0.75      ])
0
Ningrong Ye