Resize and resample in alpha-multiplied light
Posted: 2012-11-16T14:30:45-07:00
In this post, I will give a simple example that justifies the use of alpha-multiplied light when resampling images (resizing, for example).
-----
When resizing and resampling images, the issue arises as to whether one should resample the channels independently, or not.
In ImageMagick (and GEGL and Image Worsener and, I am sure, many other libraries and tools), the channels are not resampled independently.
Only one channel is resampled independently of the others: the alpha (transparency) channel.
The pixel values of the color channels are first multiplied by the alpha (transparency) channel's pixel value, then linear resampling is performed, and finally the final color channel values are obtained by "unmultiplying" the results of resampling the alpha-multiplied color channels by dividing them by the alpha value at the same location, with some exception handling when alpha is close to zero. That is, the color (non transparency) channels are resampled taking transparency into account.
Of course, when the image is opaque, that is, when alpha is 1 throughout the input image, the color channels end up being resampled independently. Actually, the same goes for any image with a constant transparency channel (with alpha=.5 throughout, for example, which corresponds to a half-transparent image). This post, really, only concerns images with an active transparency channel, meaning an image with varying transparency.
In order to explain why one should resample using alpha-multiplied light, I will use a very simple "1D" greyscale image, with only two channels (grey, and alpha) and two pixels (pixel "0" and pixel "1").
So, the left input pixel has greyscale value g0 and transparency a0, and the right input pixel has color g1 and transparency a1.
Let's see what happens if we enlarge this 2x1 image to 3x1, inserting a pixel halfway between the two with (bi)linear interpolation.
If I resample the two channels independently, this gives the following values for the new pixel:since (bi)linear interpolation is the same as averaging in the current situation.
On the other hand, if I use alpha-multiplied light, the new pixel values arewhich is more complicated, and clearly needs love if a happens to be 0.
Why in the world would this be better?
Here is why:
Suppose that we know ahead of time that we actually want to use a constant background color of value b. Then, there is no reason to resample two channels: We may as well flatten before resampling, and then resample a one channel image. What this means is that the final, "flat", result we actually want isDo we get the same result if we flatten after resampling?
Let's see:
With the image enlarged keeping the channels separate, the result of flattening after resampling isthat is, there is an error proportional to both the local variation in color value, and the local variation in transparency value. (Basically, the deviation from what we want is proportional to the product of two slopes: the alpha channel slope, and the greyscale channel slope. In the language of numerical analysis, this means that resampling channels independently is only first order accurate at halfway points, even though (bi)linear interpolation is a second order accurate resampling method at any location.)
On the other hand, with the image enlarged using alpha-multiplied light, the result of flattening after resampling isas desired.
-----
The above discussion used a simple filter (bilinear) and sampling location for the sake of keeping the algebra simple. The general situation, with a more complex linear filter and other sampling locations, leads to the same conclusion:
Technically: If a linear filter returns a white image when fed a white image, flattening to a constant background after applying the filter is the same as flattening before, provided alpha-multiplied light is used.
Note: For best results, don't clamp intermediate results: Carry negative and "whiter than white" color and transparency pixel values around until the image is flattened. This, of course, is not possible without HDRI with ImageMagick, or when storing images in unsigned integer formats. But it's something to keep in mind: Don't clamp unless (or until) you have to.
-----
When resizing and resampling images, the issue arises as to whether one should resample the channels independently, or not.
In ImageMagick (and GEGL and Image Worsener and, I am sure, many other libraries and tools), the channels are not resampled independently.
Only one channel is resampled independently of the others: the alpha (transparency) channel.
The pixel values of the color channels are first multiplied by the alpha (transparency) channel's pixel value, then linear resampling is performed, and finally the final color channel values are obtained by "unmultiplying" the results of resampling the alpha-multiplied color channels by dividing them by the alpha value at the same location, with some exception handling when alpha is close to zero. That is, the color (non transparency) channels are resampled taking transparency into account.
Of course, when the image is opaque, that is, when alpha is 1 throughout the input image, the color channels end up being resampled independently. Actually, the same goes for any image with a constant transparency channel (with alpha=.5 throughout, for example, which corresponds to a half-transparent image). This post, really, only concerns images with an active transparency channel, meaning an image with varying transparency.
In order to explain why one should resample using alpha-multiplied light, I will use a very simple "1D" greyscale image, with only two channels (grey, and alpha) and two pixels (pixel "0" and pixel "1").
So, the left input pixel has greyscale value g0 and transparency a0, and the right input pixel has color g1 and transparency a1.
Let's see what happens if we enlarge this 2x1 image to 3x1, inserting a pixel halfway between the two with (bi)linear interpolation.
If I resample the two channels independently, this gives the following values for the new pixel:
Code: Select all
a = (a_0+a_1)/2 and g = (g0+g1)/2
On the other hand, if I use alpha-multiplied light, the new pixel values are
Code: Select all
a = (a_0+a_1)/2 and g = ((a0*g0+a1*g1)/2)/a
Why in the world would this be better?
Here is why:
Suppose that we know ahead of time that we actually want to use a constant background color of value b. Then, there is no reason to resample two channels: We may as well flatten before resampling, and then resample a one channel image. What this means is that the final, "flat", result we actually want is
Code: Select all
G = (a0*g0+(1-a0)*b+a1*g1+(1-a1)*b)/2 = (a0*g0+a1*g1)/2+(1-(a0+a1)/2)*b
Let's see:
With the image enlarged keeping the channels separate, the result of flattening after resampling is
Code: Select all
a*(g0+g1)/2+(1-a)*b = G - (g1-g0)*(a1-a0)/4
On the other hand, with the image enlarged using alpha-multiplied light, the result of flattening after resampling is
Code: Select all
a*(((a0*g0+a1*g1)/2)/a)+(1-a)*b = G
-----
The above discussion used a simple filter (bilinear) and sampling location for the sake of keeping the algebra simple. The general situation, with a more complex linear filter and other sampling locations, leads to the same conclusion:
Which is what ImageMagick does.Resample using alpha-multiplied light
Technically: If a linear filter returns a white image when fed a white image, flattening to a constant background after applying the filter is the same as flattening before, provided alpha-multiplied light is used.
Note: For best results, don't clamp intermediate results: Carry negative and "whiter than white" color and transparency pixel values around until the image is flattened. This, of course, is not possible without HDRI with ImageMagick, or when storing images in unsigned integer formats. But it's something to keep in mind: Don't clamp unless (or until) you have to.