2
$\begingroup$

I want to calculate the average color+opacity of an image. First I thought about just averaging all ARGB values component-wise, but this got really dark on images with a lot of transparency. I also don't think mixing RGB colors is a good idea in general since they are nonlinear due to Gamma or something. I also had a look at HSV/HSB but this seems a pretty stupid idea.

So, what is a reasonably physically correct way to average the color of an ARGB image? I prefer solutions in Java and using 8 bit per color channel. The solution should also take care that the image may have an arbitrary amount of transparent or semi-transparent pixels.

$\endgroup$
3
$\begingroup$

Averaging RGB should be correct assuming you do it in linear RGB. If your images are in sRGB, you can remove the gamma correction by doing something like the following for each of the R, G, and B components:

float sRGBToLinear(UInt8 component)
{
    float tempComponent = (float)component / 255.0;
    if (tempComponent <= 0.04045)
    {
        tempComponent = tempComponent / 12.92;
    }
    else
    {
        tempComponent = pow((tempComponent + 0.055) / (1.055), 2.4);
    }
    return tempComponent;
}

You can then average together all of the red values in the image, all of the green values in the image, and all of the blue values in the image after they've been run through the above conversion. You can then do the opposite conversion to get back to sRGB:

UInt8 linearRGBTosRGB(float component)
{
    float tempComponent =  0.0;
    if (component <= 0.00318308)
    {
        tempComponent = 12.92 * component;
    }
    else
    {
        tempComponent = 1.055 * pow(component, 1.0 / 2.4) - 0.055;
    }
    return (UInt8)(tempComponent * 255.0);
}

(My apologies if I've messed up the syntax. I'm a C/C++ programmer and don't know the intricacies of Java.)

Note that alpha makes things slightly more complicated. If you're using premultiplied alpha, you can simply apply the above calculations for the average. If you're using straight alpha, you'll want to multiply each of the R, G, and B components by the alpha before doing the averaging.

$\endgroup$
  • 1
    $\begingroup$ Also, you probably want to weight by alpha during the averaging, so that the (invisible) color in transparent areas doesn't contaminate the average. $\endgroup$ – Nathan Reed Aug 22 '18 at 6:06
  • $\begingroup$ @NathanReed Not just "probably", Jim Blinn says to be correct you should use premultiplied alpha in filtering calcs . :-) $\endgroup$ – Simon F Aug 22 '18 at 8:53
  • $\begingroup$ Does this mean that ideally, I convert back to sRGB after all processing steps have been done (not only the downscaling)? Is alpha gamma corrected too? My images are not premultiplied: pixels with zero alpha may have any RGB color. How do I take in the alpha when averaging? $\endgroup$ – piegames Aug 22 '18 at 9:39
  • $\begingroup$ @NathanReed Interesting. I would have thought there would be cases where you want to and cases where you don't. But I will certainly defer to Mr. Blinn. $\endgroup$ – user1118321 Aug 22 '18 at 16:00
  • $\begingroup$ @piegames - Yes, you probably want to convent back to sRGB after you've done all the averaging. Otherwise the color you're left with is in a different color space than the ones you started with. Alpha does not get gamma corrected. As mentioned in the comment above, you probably want to make sure that your values are premultiplied, so you can skip the un-pre-multiply step I mentioned. I'll update the post to remove that. $\endgroup$ – user1118321 Aug 22 '18 at 16:01
1
$\begingroup$

Sounds like you could be using the sum of all pixels disregarding the transparency for each pixel, and that the image has black pixels where it is transparent which leads to the issue. Instead add the pixels with their alpha value as weight, and divide the result with the total weight. Premultiplied alpha you should handle also as suggested above.

$\endgroup$
  • $\begingroup$ Hi Beyond, FWIW, Jim Blinn's article "Compositing, Part I: Theory" (graphics.stanford.edu/courses/cs248-02/blinn_theory.pdf) has a discussion on how to do filtering/averaging for colours with alpha. This states the correct way is to use premultiplied alpha (AKA associative alpha). $\endgroup$ – Simon F Aug 22 '18 at 8:49
  • $\begingroup$ Sure, the trick is that not all files are saved that way. So in order to do it correctly you need to know which type of file you're dealing with :) $\endgroup$ – beyond Aug 22 '18 at 10:52
  • $\begingroup$ Sorry, looks like I misread your post! Wasn't entirely awake at the time. $\endgroup$ – Simon F Aug 22 '18 at 12:59

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.