Question

If I have two colors defined by their RGB values, can I average the Red, Green and Blue values and then combine to define a third color that looks like a visual average of the two?

ie NewColor = (R1+R2)/2,(G1+G2)/2,(B1+B2)/2

EDIT1: Thanks for all the responses. For my current needs, I am only dealing with color pairs that are shades of the same color so I think that averaging them will work. However, I will try converting to Lab Space to make sure that assumption is true and the technique will be useful in the future.

EDIT2: Here are my results FWIW. Color1 and Color2 are my two colors and the two middle columns are the results of averaging in Lab space and averaging RGB respectively. In this case there is not a lot of difference between the two color and so the differences in the output from the averaging techniques is subtle.

visual comparison of color averaging techniques

Was it helpful?

Solution

Take a look at the answers to this question.

Basically, you want to convert the colors into something called Lab space, and find their average in that space.

Lab space is a way of representing colours where points that are close to each other are those that look similar to each other to humans.

OTHER TIPS

Several answers suggest converting to Lab color space - which is probably a good approach for more complex color manipulation.

But if you simply need a quick way to take the average of two colors, this can be done in the RGB space. You just have to mind a caveat: You must square the RGB values before averaging them, and then take the root of the result. (If you simply take the average, the result will tend to be too dark.)

Like this:

NewColor = sqrt((R1^2+R2^2)/2),sqrt((G1^2+G2^2)/2),sqrt((B1^2+B2^2)/2)

Here's a great vid which explains why this method is efficient: https://www.youtube.com/watch?v=LKnqECcg6Gw

Averaging in HSL color space might produce better results.

I don't know whether taking a simple average of the components is the "best" from a perceptual point of view (that sounds like a question for a psychologist), but here are a couple of examples using simple component averaging.

alt text

The red-mustard-green one is ugly but the interpolation seems reasonable enough.

Yes. You can average two colors together like that. It's the approach used by OpenGL to blend colors together (e.g., in creating mip maps for rendering distant objects, or rendering a 50% transparent texture). It is fast, simple, and "good enough" for many situations. It isn't completely realistic, however, and probably wouldn't be used on photograph-quality images.

This is hard. First, a set of RGB values doesn't define a color. They need to be interpreted in light of the color primaries to which they refer (the color space), such as sRGB, Rec.709, Rec.2020, Adobe RGB (1998), etc.

Further, RGB values as we normally encounter them are not proportional to linear light: they are "encoded" using a non-linear function (gamma). And sometimes (in video applications mostly) the value of "black" is not zero, but is offset from zero, usually 16 for 8-bit values. And "white" is not 255 but 235. sRGB and Rec.709 share RGB primaries, but their gamma functions are different.

The color space conversion starts with removing any black offset so that black is zero. If the gamma function has a breakpoint in it (like sRGB and Rec.709 do), you will need to carefully scale the RGB values so that "white" is 1.0.

Then, "decode" the gamma by doing the inverse of the original gamma function. (One answer suggested squaring the values, which is an approximation of gamma decoding.) Now you have linear-light RGB values in some color space. At this point you can convert from that color space to Lab space. Most conversions from RGB to Lab go through an intermediate color space called XYZ.

The steps as nested function calls:

Lab = XYZ2Lab( RGB2XYZ( gamma_decode( offset_and_scale( RGB ), gammaFunction ), RGB color space ) )

(Lab space was developed in the 1976 as an attempt to create a perceptually-uniform warping of the standard CIE XYZ space. (Luv was another attempt.) The idea is that the Euclidean (straight-line) distance between two colors that were just-noticeably different (1 "JND") would be the same distance for any two colors. The distance between two colors in Lab is known as 'delta-E'. The simple delta Euclidean distance formula is now called dE76. See https://en.wikipedia.org/wiki/Color_difference)

In your case, you could average the two Lab colors to get a new Lab color, then reverse all the conversions to get back to RGB in your chosen color space.

This will get you close, but is not guaranteed, simply because "color" is a human perception, not a physical quantity, and has been notoriously difficult to characterize reliably. Lab didn't actually work so well at being perceptually uniform. So rather than fix Lab, they proposed a new, more-complex delta-E function with another warp built-in: DE94. That was better, but not perfect, so another proposal emerged in 2000: DE2000. Also better but not perfect. See that Wiki page above for more info.

If DE2000 is not good enough (or too complex!) you might have a look at an alternative to Lab called ICtCp that is claimed to be more perceptually uniform than Lab.

I think, the answer from arntjw goes in the right direction, and recognizes the logarithmic underlay, as mentioned by Dan W. However, the proper geometric mean is not sqrt((C1^2+C2^2)/2), but sqrt(C1*C2). So the average color would be:

NewColor = sqrt(R1*R2),sqrt(G1*G2),sqrt(B1*B2)

The resulting colors are closer to what we expect. You can generalize to more colors using higher order roots, and weight each color by adding an exponent to its components.

There's actually a much simpler way.

  • Scale the image down to 1px by 1px.

    Color of the 1px is the average color of whatever you scaled

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top