Title: Use unsharp masking to sharpen images in Python
This example adds new filters to the previous example Use image filters to blur and sharpen images, and to detect edges in Python. See that example for basic information about applying filters to images.
This post explains how you can use unsharp asking to sharpen an image.
Unsharp Masking
Unsharp masking is a remarkable technique where you use a blurred image to make the original image sharper. First, you make a blurred image by using any of the techniques described in the previous post. Then you subtract the result from the original image. Because you're removing blurriness, what's left contains the "sharpness." You add the "sharpness" to the original image and it becomes sharper.
Because you're adding the "sharpness" to the original image, you must be making it sharper, right?
As crazy as that sounds, it actually works! Take a look at the picture on the right. On the left is the original image. The middle image has been blurred by a 3×3 box filter. The image on the right is what you get if subtract the blurred image from the original.
You can make the effect stronger by using a larger blurring filter or by multiplying the blurred image's pixel values by a scale factor. In the picture on the right, I scaled the blurred picture by a factor of 1, but if you use larger scale factors you get a sharper result.
The example program's Unsharp menu provides three commands that apply masking with:
- A 3×3 box filter and a scale factor of 1
- A 3×3 box filter and a scale factor of 3
- A 5×5 box filter and a scale factor of 1
The picture on the right shows the results.
Python Code
The Unsharp menu commands all follow the same pattern. Here's the code for the first command.
def mnu_unsharp_mask1(self):
'''Apply unsharp masking.'''
blurrer = Filter.box_filter(3)
unsharp_filter = UnsharpMaskingFilter(blurrer, 1)
self.pil_image = unsharp_filter.apply(self.pil_image)
self.show_result()
This code first creates a 3×3 box filter to blur the original image. It then creates a new UnsharpMaskingFilter (described shortly) to make am unsharp masking filter. It calls the filter's apply method, saves the result, and then calls show_result to display the sharpened picture.
The other Unsharp Masking men commands use a similar technique, just with different kernels and scale factors.
Here's the UnsharpMaskingFilter class.
class UnsharpMaskingFilter(Filter):
def __init__(self, blurring_filter, scale):
# Use the filter to initialize our kernel, weight, and offset.
super().__init__(
blurring_filter.kernel,
blurring_filter.weight,
blurring_filter.offset)
self.scale = scale
def apply(self, image):
'''Apply the filter to a PIL image.'''
# Apply the blurring filter.
blurred = super().apply(image)
# Subtract the blurred result from the image.
subtracted = ImageChops.subtract(image, blurred)
# Scale the result.
enhancer = ImageEnhance.Brightness(subtracted)
brightened = enhancer.enhance(self.scale)
# Add the scaled difference to the original image.
return ImageChops.add(image, brightened)
This class inherits from the Filter class described in my earlier image processing posts. Its constructor calls the parent class constructor to save the blurring kernel, weight, and offset. It then saves the unsharp masking scale factor.
The apply method uses super.apply to apply the blurring filter to the image. It then calls PIL's ImageChops.subtract function to subtract the blurred image from the original. (The name ImageChops comes from "image channel operations," operations that work on an image's separate red, green, blue, and alpha channels.)
The result of the subtraction contains sharpening components that represent areas of the image that will be sharpened. Because the original and blurred images have mostly similar pixel values, though, the result is quite dark. The code calls ImageEnhance.Brightness to scale the sharpened pieces and then calls ImageChops.add to the scale brightener to the original image to sharpen it.
Conclusion
It's not obvious that subtracting a blurred image from an original image can make the original sharper, but it does.
Download the example to see additional details and to experiment with it. For example, you might want to try creating some filters of your own.
|