5
votes

I'm trying to find an algorithm that will mix colors, based on a % amount. I'm working on an election project, and want to be able to assign each candidate in a race a different color, and then create a "resulting" color based on how much of the vote that candidate has gotten.

This question:

Is there an algorithm for color mixing that works like mixing real colors?

is close the question I'm asking - but potentially I need to be able to mix 3 or 4 or 5 colors together. I understand that this ultimately an incredibly complicated question - but I'm curious what the suggested method is for doing this on more than 2 colors. So, I might have:

Red (40%), Green(10%), Blue (50%)

OR

Red (40%), Yellow (5%), Blue (10%), Orange (45%)

Once again, I know that the last example would probably produce some sort of Gray or Brown or Black - and that's fine. My problem is that I don't even have an algorithm that attempts to solve this problem.


Edit: So - after posting this message, I realized that what I was really trying to accomplish was basically PAINT color mixing. IN other words, I don't want LIGHT color mixing, I want to simulate what would happen with PAINT mixing - as that is the "predictable" result that I kept 'expecting' - and was having trouble getting.

I found this link: http://painting.about.com/library/blpaint/blcolormixingpalette1.htm

which behaves VERY closely to what I'm trying to accomplish - and exposes a small "flaw" in my original idea. What is demonstrates is that even though I can mix up to 6 colors using this algorithm, the actual "data" is always broken down into the 3 primary colors.

Even so - this is very close.

Thank you to everyone who has contributed to this thread - all of these suggestions have been very helpful in the exploration of this surprising complex question/field.

3
One simple way to implement this could be to simply multiply the components of each color by their percentage weight. For example, 40% Red, 60% Green would be formed by multiplying the red components by 0.4 and the green component by 0.6, then adding as normal. This guarantees no clipping of any component as long as your percentages add up to 100% or less.Kenogu Labz

3 Answers

7
votes

Have you tried simply taking a weighted sum?

d.R = a.R*0.25 + b.R*0.25 + c.R*0.5
d.G = a.G*0.25 + b.G*0.25 + c.G*0.5
...
where a, b, c are the colors you're mixing

From my research, it seems there isn't any one correct answer, but whatever seems to produce the most appropriate effect. Moreover, I don't think simply blending the colors would really indicate anything on an infograph. You'd be better off trying something like a really granular dither http://en.wikipedia.org/wiki/Dither#Digital_photography_and_image_processing

2
votes

The RGB colors system is bad for such calculation, because it does not work like we humans perceive colors. For example we just do not perceive yellow as a mixture of green and red. The first step is to convert your colors in a color space where you have a lightness coordinate and two coordinates for the color. This can be either HLS or CIELAB. HLS conversion is more simply, but coordinates in CIELAB represent better the human color experience. Equal distances in this color space are at least roughly equal distances in human perception.

In this color space you have coordidates L, A and B. L should be the same for all your colors. Your base colors should be on a circle around the gray-point, so they have the same distance, and no colors is stronger than the others. so you basically have a few points in the plane and need to calculate their weighted middle point of these points. The you convert this point back to RGB.

1
votes

It all depends on the primary colors in your color model. It looks like you're using the RGB color model which means that all colors can be represented by a mix of red, green, and blue.

That said, any colors that are not red, green, or blue you would decompose those down into their red, green and blue counterparts, and then multiply those weights by the weight overall.

In your second example:

Red (40%), Yellow (5%), Blue (10%), Orange (45%)

Yellow and Orange need to be decomposed. Assuming you have the Color instance for orange, you can do this:

orangeRed = o.R * .05;
orangeBlue = o.B * .05;
orangeGreen = o.G * .05;

This can actually be generalized across all Color instances (for primary colors, only one of R, G and B will have a value) and then sums and weighted averages can be taken to get your mixed color.

An easy extension method that does this would be:

static Color Mix(this IEnumerable<Tuple<Color, double>> colors)
{
    // The red, green, and blue values.
    double red = 0.0, green = 0.0, blue = 0.0;

    // The denominator for the weighted average for a color.
    double denominator = byte.MaxValue * 3.0;

    // Cycle through the colors.
    foreach (Tuple<Color, double> color in colors)
    {
        // Get the weighted average value for each component, and then
        // multiply that by the weight for the color.
        red   += ((color.Item1.R / denominator) * color.Item2);
        green += ((color.Item1.G / denominator) * color.Item2);
        blue  += ((color.Item1.B / denominator) * color.Item2);
    }

    // Create the color and return.
    return Color.FromArgb((int) red, (int) green, (int) blue);
}