6

I have 4 colors that I converted from RGB to CIELAB L*a*b* model.

  1. How can I calculate mix of these 4 colors when I have (L,a,b) for each such color?

  2. How can I calculate same mix, if I want to put weights (w1, w2, w3, w4) on such 4 colors, having 1 maximum and 0 minimum (none) weight?

Jeffery To
  • 11,836
  • 1
  • 27
  • 42
Ωmega
  • 42,614
  • 34
  • 134
  • 203
  • 4
    What do you expect this mix to produce? You can average the 4 colors together component-by-component, with weighting, and you'll get valid colors. But whether they'll be the colors you expect or not is hard to say without more information. Have you tried just multiplying each by their respective weights, then adding them together component-wise and dividing by 4? If that doesn't give you what you expect, can you be more precise about what you expect? If you post a picture, it would help. – user1118321 Jun 23 '12 at 19:52
  • 4
    Did you just include a bunch of programming language tags hoping to attract as many potential viewers as possible? – DavidO Jun 24 '12 at 07:26
  • @DavidO - I included those programming language that I work with for 10+ years. – Ωmega Jun 24 '12 at 12:26
  • 3
    Regardless of your experience OP, the current set of tags (php, javascript, c++, c, perl) have nothing to do with your _question_, and they should be removed. – aaaidan Jun 25 '12 at 00:59

2 Answers2

10

Assuming you have a structure like this:

typedef struct LabColor {
    uint8_t L;
    uint8_t a;
    uint8_t b;
} LabColor;

and an array of 4 of them:

LabColor colors[4];
getMeSomeColors (colors);

and weights:

float weights[4];
getMeSomeWeights (weights);

One possible blending method would be this:

float LSum = 0.0;
float aSum = 0.0;
float bSum = 0.0;
float weightSum = 0.0;
for (int i = 0; i < 4; i++)
{
    LSum += ((float)colors [ i ].L * weights [ i ]);
    aSum += ((float)colors [ i ].a * weights [ i ]);
    bSum += ((float)colors [ i ].b * weights [ i ]);
    weightSum += weights[i];
}
LabColor result;
result.L = (uint8_t)(LSum / weightSum);
result.a = (uint8_t)((aSum / weightSum) + 127);
result.b = (uint8_t)((bSum / weightSum) + 127);

This assumes weights are all between 0 and 1. If not, then you'll have to do some clamping. If the weights sum to 1, then you can skip the division step.

There are an infinite number of other ways to blend the colors. If this one doesn't do what you want, you'll need to give more specifics as to what you do want.

user1118321
  • 25,567
  • 4
  • 55
  • 86
  • Fro a [weighted average](https://en.wikipedia.org/wiki/Weighted_mean), you divide by the *sum of the weights*, not the number of things you're averaging. So you would do `result.L = (uint8_t)((float)LSum/wSum)`, where `wSum = weights[0] + weights[1] + weights[2] + weights[3]`. –  Jun 27 '12 at 17:57
  • @RahulNarain Good point! Thanks for catching that. I've fixed it. – user1118321 Jun 27 '12 at 20:09
  • a and b are signed values in L*a*b* space. – ergosys Jun 29 '12 at 03:45
  • @ergosys OK, I've updated it assuming they're between -127 and 127. Obviously, depending on the weights, you might need some clamping. I'll leave that as an exercise for the reader. – user1118321 Jun 29 '12 at 03:53
4

The most realistic results will come from converting your L*a*b* values to a linear (not gamma-corrected) RGB space, summing those values, and converting back. This is how physical light sources interact.

The L*a*b* color space wasn't invented for color manipulation, as it's inherently non-linear.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • RGB mix will not produce realistic colors: http://farm1.static.flickr.com/147/417347616_60b05096f3_o.png – Ωmega Jun 26 '12 at 18:21
  • 2
    @user1215106, who said anything about "mixing mode multiply"? I said "sum". Although in your case you might be looking for a "blend", but that requires specifying the order and opacity of each color. – Mark Ransom Jun 26 '12 at 18:25
  • Unlike the RGB and CMYK color models, Lab color is designed to approximate human vision - http://en.wikipedia.org/wiki/CIE_Lab. CIE L*a*b* (CIELAB) is the most complete color space specified by the International Commission on Illumination (French Commission internationale de l'éclairage, hence its CIE initialism). It describes all the colors visible to the human eye and was created to serve as a device independent model to be used as a reference. – Ωmega Jun 26 '12 at 18:30
  • 2
    @user1215106, I'm perfectly aware of how and why CIELAB came to exist. I still assert that it's meant for color quantification, not color calculation. – Mark Ransom Jun 26 '12 at 18:44
  • 1
    Okay, so what formula should I use to mix color in RGB then to get most human color mix, please? Summing ends with black/gray/white soon or later. – Ωmega Jun 26 '12 at 19:36
  • @Ωmega Can you describe in detail why you want to blend these colors? What are you trying to accomplish with your blend? If we know what you're trying to do, we can give better, more specific answers. – user1118321 Jun 27 '12 at 20:14
  • @user1118321 - I have 4 points that represents any quadrangle. Each corner has light with different color. I want to calculate color of light at some random point of the quadrangle. – Ωmega Jun 27 '12 at 20:33
  • 3
    In that case, Mark is absolutely correct - convert your colors to linear RGB and add them together. If you want realistic lighting, you'll need to take into account fall-off as well - that is the way that lights appear dimmer the farther away you are from them. – user1118321 Jun 27 '12 at 20:50