What is a sampling test image?

I was asked what the purpose is of the test image in my previous post, and why the alpha was set to 0.01 rather than 0 in alternating pixels.
The answer to the second question is simpler: pixel manipulation code often zeros the RGB channels of a pixel when it sees that the alpha is zero, which could interfere with the test.

The answer to the first question is more complicated as it requires some background. Combining pixels by sampling, blurring, or compositing them generally requires the pixels to be premultiplied first. If this isn’t done, then the result can show incorrect colors, halos, and other artifacts.

For example, this test image has alternating pixels of {1.0, 0.0, 0.0, 0.01} and {0.0, 0.0, 0.0, 1.0}, read as {Red, Green, Blue, Alpha}:


When blurred in Adobe After Effects CS3, using the included directional blur filter set to 3 degrees, we get this:


You can easily see that the blur did not premultiply the pixels before sampling them. The red channel has leaked out from the transparent pixels and polluted the image.

We can use a simple average operation to show why this happens. Note that we are starting with straight pixels, where the alpha channel is treated as a mask independent of the RGB channels.

The quick-but-wrong way to average two straight pixels is to simply add their alpha, red, green, and blue channels independently and divide the result values by two. When averaging two adjacent pixels in our test image, we would add a red value of 1 to a red value of 0, resulting in 0.5 after the division. Similarly, the alpha values average to 0.5005. The result is a reddish, half-transparent pixel. Since a blur is just a complicated average, that’s what happened in our test.

The right way to average straight pixels is to first multiply each of the color channels by the alpha value, then do the average. Afterwards, you have to divide the result color channels by the result alpha to produce straight pixels again. In our example, we should average a red value of 1 x 0.01 with one of 0 x 1. The result is a red value of 0.005 and an alpha of 0.5005 (as before). This is a near-black, half-transparent color, and is what we expect visually. The final, straightened red value is 0.005 / 0.5005, or 0.00999.

In general, premultiplying pixels before combining them, and straightening them afterwards, accounts for most of the time spent in these operations. Time that is actually wasted on fully-opaque pixels, where premultiplication has no effect.

This is a surprisingly common problem in practice. I’ve seen a commercial compositing application that failed to premultiply with any pixel-combining operations. The reaction that I got when I explained the need for premultiplication to the developers showed that they really didn’t understand the problem, and that they didn’t see it in practice. I suspect that this would show up mostly on feathered edges of layered images, and would either be mistaken for noise in the original image, or won’t be recognized as a bug since they weren’t looking for it.

It takes extra code and complexity to combine straight pixels properly, and to avoid unnecessary premultiplication and straightening, but it doesn’t matter how fast an implementation is if it produces the wrong result.

For more information on this topic, see the Wikipedia article on Alpha Compositing.



, ,