Title: Make elliptical images with blended edges in Python
My post Make elliptical images in Python shows how to crop an image so it has an elliptical shape and parts that are outside of the elliptical center are transparent. That works, but if you paste that image on top of another image, there's a sharp edge between the two and that may not be what you want. You may prefer a gentler transition.
This example shows how you can adjust the edges of the elliptical image to make it blend in more smoothly.
Blending the Mask
The trick is to change the mask so its edges are translucent. The center of the mask is opaque and the edges are progressively more transparent.
The following function ellipsifies an image with this kind of blended mask. The modified mask code is highlighted in blue.
def elliptical_image_blended(image, blur_width=50):
'''Return an elliptical copy of the image with blended edges.'''
# Make sure the blur_width is less than half the width and height.
blur_width = min(blur_width, image.width // 2, image.height // 2)
# Make an elliptical mask image with transparent background.
mask = Image.new('L', (image.width, image.height), 0)
dr = ImageDraw.Draw(mask)
# Draw nested ellipses gradually becoming more opaque.
gray = d_gray = 255 / blur_width
for i in range(blur_width - 1):
rect = (i, i, image.width - 1 - i, image.height - 1 - i)
dr.ellipse(rect, fill=int(gray), outline=None)
gray += d_gray
# Make the final ellipse opaque.
rect = (i, i, image.width - 1 - i, image.height - 1 - i)
dr.ellipse(rect, fill='white', outline=None)
# Save the mask (mostly for debugging).
#mask.save('mask.png')
# Copy the image and set its transparency.
image = image.copy()
image.putalpha(mask)
return image
This function creates a new image with a transparent background to hold the result as before.
The code then sets blur_width to the width of the mask's translucent area along its edges. That width is the smaller of the desired width and half the image's width and height. That prevents a crash if the width is so thick that the code that comes next tries to make an ellipse with negative width or height.
The function creates the grayscale mask image, initially making it all black (transparent). It then draws a set of ellipses with different transparencies. It starts with the largest ellipse, giving it the size of the original image. That ellipse would create the sharply defined elliptical image given by my previous post except this time the ellipse is mostly transparent.
The program's loop makes progressively smaller ellipses, increasing the gray value each time so the smaller ellipses become less transparent. After it finishes making the smaller, less transparent ellipses, the code fills one final ellipse that's completely opaque so the central part of the image has no transparency.
The code finishes by copying the original image (so we don't mess it up) and using putalpha to copy the mask's alpha channel onto the copy.
Conclusion
Loading and saving images takes a while (at least on my system; perhaps Windows is just being annoying), but creating the blended image is fast enough that you can adjust the blurred area with only a slight lag.
In my next post, I'll show how you can reduce a slightly annoying artifact caused by the process. Meanwhile, download the example to experiment with it and to see additional details.
|