[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky] [Facebook]
[Build Your Own Python Action Arcade!]

[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Make an elliptical image from an image that already has transparency in Python

[Making an elliptical image from one that already has transparent areas in Pythony]

I thought I was done with making elliptical images, but I found a new problem while working on Apollonian circle packings. (I'll talk more about that in my next post.) Previously I made these posts:

The method I used in previous posts to add transparency to parts of an image doesn't work if the image already has some transparent parts. Take a look at the picture at the top of this post.

The cat on the left shows the original image. You can see that it has transparent pieces.

The cat in the middle shows the result of the methods I used in earlier posts. The outside of the ellipse is transparent but the parts of the image that were originally transparent are now black.

The cat on the right was made with the method described in this post. It has an elliptical outline and preserves the original image's transparency.

Understanding the Problem

The following code shows the method used by one of my earlier posts.

def elliptical_image(image): '''Return an elliptical copy of the image.''' # Make an elliptical mask image with transparent background. mask = Image.new('L', (image.width, image.height), 0) dr = ImageDraw.Draw(mask) dr.ellipse((0, 0, image.width-1, image.height-1), fill='white', outline=None) # Copy the image so we don't mess up the original. image = image.copy() # Use the mask to set the image's transparency. image.putalpha(mask) return image

The code creates a black mask image and fills it with a white ellipse. It then uses image.putalpha to copy the mask's alpha (opacity) channel to the image. That makes the image transparent where the mask is transparent: the black pixels outside of the ellipse.

Unfortunately, image.putalpha overwrites the alpha channel of the original image. Parts of the mask that are not transparent (the white ellipse) copy their alpha values onto the image, too, so the corresponding parts of the image are now opaque. That means any pats of the original image that were transparent lose that transparency.

Adopting Another Approach

To solve the problem, this example extracts the original image's alpha (opacity) channel and combines it with the mask. The following code shows that approach with the new code highlighted in blue.

from PIL import ImageChops def elliptical_transparent_image(image): '''Return an elliptical copy of the image.''' # Make an elliptical mask image, initially transparent. ellipse_mask = Image.new('L', (image.width, image.height), 0) dr = ImageDraw.Draw(ellipse_mask) dr.ellipse((0, 0, image.width-1, image.height-1), fill='white', outline=None) # Copy the image so we don't mess up the original. image = image.copy() # Get the image's alpha channel. original_alpha = image.split()[3] # Multiply the masks so the result is transparent (0) outside # the ellipse and where the original image is transparent. final_mask = ImageChops.multiply(original_alpha, ellipse_mask) # Use the mask to set the image's transparency. image.putalpha(final_mask) return image

The code creates its mask and makes a copy of the image as before. It then calls image.split to extract the image's alpha channel.

Next, the code calls ImageChops.multiply to multiply the mask and the alpha image's channel. That method performs bitwise multiplication of the images' pixel values and divides the result by the maximum value allowed for the result. In this case, the mask and the image's alpha channel both use 8 bits so the maximum value is 255. Dividing the result by 255 means the final value lies between 0 and 255.

The mask pixels have values 0 (black = transparent) or 255 (white = opaque) so the result of multiplying them with the image pixels are 0 where either the mask pixel or the image's alpha channel is 0. The result is a final mask that is 0 outside of the ellipse and where the original image was transparent.

Having created the new mask, the function calls image.putalpha as before and returns the result.

Conclusion

The previous posts overwrote the image's opacity so they ignored any transparent areas on the original image. This example combines an elliptical mask with the original image's alpha channel so its transparency is preserved.

In my next post, I'll use the elliptical_transparent_image function to draw images on an Apollonian circle packing. Meanwhile, download the example to experiment with it and to see additional details.

© 2025 - 2026 Rocky Mountain Computer Consulting, Inc. All rights reserved.