I'm trying to find an efficient way of creating a "glow" effect around a monochrome image such that the gradient is smooth. I know this sounds vague, just bear with me ... The standard approach to this is to blur the image and add the original image to the blurred result, but then small and/or thin structures in the image disappear - this is explicitly what I'm trying to avoid. I want to use this effect for stylistic image postprocessing, not the traditional HDR/tonemapping/bloom type of stuff (where blur+add is more appropriate).
For simplicity, let's say the input pixels are either 0 or 1, where 0 represents the background (exterior) and 1 represents the interior of the shape. What I want is a glow effect that brightens the 0 pixels near the shape's boundary and leaves the 1 pixels unaffected. This should be a "smooth distance field" type of effect, where the output value of each pixel is proportional to the minimum distance to an interior pixel. However, the actual Euclidean Distance Transform (EDT) is generally not smooth, at least not C1-smooth - there are "creases" in the gradient that are very significant visually.
Here's a paper on the Euclidean Distance Transform:
2D Euclidean Distance Transform Algorithms
Currently I have a solution for that works fairly well, but it requires hundreds or even thousands of passes over the image. Basically, I save a copy of the input image, then repeatedly perform a small (3x3) box blur followed by a MAX with the saved input image. Here is a 1D example:
void Glow1D(float *dst, const float *src, int w, int numPasses)
{
memcpy(dst, src, w*sizeof(float));
for (int pass = 0; pass < numPasses; ++pass)
{
float v0 = dst[0];
float v1 = v0;
for (int x = 0; x < w; ++x)
{
float v2 = dst[(x + 1) < w ? (x + 1) : (w - 1)];
dst[x] = Max(src[x], (v0 + v2)/2.0f);
v0 = v1;
v1 = v2;
}
}
}
If you set w=512 and initialize the array with {1,0,0,0,0,....,0} and set numPasses=2000 and then graph the result, you'll get what looks like almost straight linear falloff from 1 down to 0.5 over the first 32 elements, followed by a smooth tapering off that reaches near zero (visually at least) at around the 150th element.
The standard ways of accelerating large-radius Gaussian blur don't seem to apply to this repeated "blur+MAX" method, unfortunately. I've implemented this algorithm using 8x16-bit fixed-point vectors in SSE but it's still orders of magnitude too slow.
Does anyone have any suggestions for how I can make this (or something visually similar) much much faster? Maybe there's some way to start with an EDT and somehow "relax" or smooth the image so that the pixels near the interior don't change and the gradient only changes near the creases in the EDT's gradient?















