Dr. Dobb's Journal November 1999
Unsharp masking is a photographic technique that increases the sharpness of a photographic image. It's heavily used by astronomers for improving blurry telescope images. More recently, it's become a standard tool among graphic artists for enhancing blurry scanned images.
In the darkroom, you create what is called an "unsharp mask" from a large-format negative by exposing another piece of film using a spacer. The result is an underexposed, blurry positive. When you sandwich this with the original negative, some of the fuzziness is canceled out, resulting in a sharper image.
The digital version of this process is actually much simpler in many ways. To demonstrate the technique, pretend for a moment that you're an astronomer trying to photograph a distant star. Figure 1 is a cross-section of the image you would like to get -- a sharp-edged light disc against a dark background. But there are many kinds of interference to contend with, so your actual result is more like Figure 2.
Just as in the photographic technique, the key is to take the actual image and subtract a blurred version of it from itself. Figure 3 shows the result in this particular case. This unsharp mask is very faint; in this particular case, the maximum value is only about a hundredth of the peak magnitude of the original. This is an important observation; for example, the "Unsharp Mask" script included with version 1 of GIMP (http://www.gimp.org/) builds an unsharp mask using 8-bit graphics operations, which severely limits the enhancement you can obtain.
The enhanced image is created by adding a multiple of the unsharp mask back to the original. In the case of Figure 4, I've multiplied the unsharp mask by 100 before adding it back to the original. The enhanced version has a much narrower peak than the original data. The sides of this peak are much steeper, which corresponds to a sharper image.
You should note a few interesting facts about Figure 4. First, I created the unsharp mask using a blur radius much smaller than the blurring in the original; you can play with the parameters, but I think you'll agree that a relatively small radius gives the best improvement. Also, notice how the enhanced data here goes out of range (the red line goes below the x-axis). This is a problem with too-aggressive unsharp masking. You lose all detail in areas where this happens. One way to address this problem is to reduce the weighting when you combine the unsharp mask with the actual data. Of course, real images don't have a single feature you're trying to enhance, so it may be very difficult to find a single weight that simultaneously provides the needed enhancement and does not destroy faint details.
The key to creating an adaptive unsharp-masking algorithm is that all of the parameters apply to single pixels. It's therefore quite reasonable to vary these parameters from pixel to pixel to provide different levels of enhancement. To do this automatically, you first identify the problem areas. Figure 5 shows only the parts of the enhanced image that went out of range.
To build Figure 4, I added 100 times the unsharp mask at every point. In the adaptive version, I start with an array that contains 100 for every pixel. I then wanted to reduce the weight around problem areas. This is not an entirely trivial operation; I wanted to reduce the weight more in areas that were more troublesome. I also wanted the weighting to vary smoothly across the image so that my adaptive weighting wouldn't alter any details. (After all, the point of having an adaptive algorithm is to preserve details.) One simple solution was to first add a multiple of Figure 5, then apply a Gaussian Blur to the resulting array. Figure 5 shows more negative in areas that are more of a problem. By blurring the weighting array, I ensured that the weighting would vary smoothly across the image. The resulting array of weights is shown in Figure 6.
Once I had a suitable weighting array, I could multiply each pixel of the unsharp mask by a different weight and add the result to the original data. Figure 7 is the result. This final enhanced version does not go out of range.
The basic approach here was simply to replace a single fixed parameter (the weighting used to combine the unsharp mask with the original image) with a separate parameter for every pixel. Of course, this required juggling other parameters; I used a single multiplier and a blur radius to construct the weighting array. To choose that multiplier automatically, you can simply increase it just to the point that none of your original image is out of range. I don't know of a good way to automatically determine the blur radius for this operation.
The basic unsharp-mask algorithm does have one other fixed parameter that could become adaptive. A Gaussian Blur is a pointwise operation; each pixel is replaced with a weighted average of neighboring pixels, with the weighting determined by a Gaussian distribution with a selected radius. There is no technical reason the Gaussian radius cannot be varied at each pixel. To do so, you would need an array of Gaussian Blur radiuses that you could manipulate. The final blurred version would require, at every pixel, computing fresh Gaussian weights and applying those weights at that pixel. This is, unfortunately, extremely computation intensive, and probably not practical for most purposes. I've provided a Mathematica notebook (available electronically; see "Resource Center," page 5) that lets you experiment with the different parameters. The notebook uses a Fourier transform to compute the Gaussian Blur, which is a fast method that unfortunately does not lend itself well to an adaptive approach. (You can view the notebook using MathReader 3.0, which is available at no charge from Wolfram Research; see http://www.wolfram.com/.)
The unsharp-mask weighting layer doesn't necessarily have to be constructed automatically. An alternative approach would be to initialize it to all zeros, thus allowing users to paint on it. If carefully done, this would allow users to selectively sharpen the image only where needed.
DDJ