Drawing a polygon using ImageDraw.polygon and an ellipse using ImageDraw.ellipse with no outline specified, with an opaque outline, and with a semi-transparent outline.
ImageDraw.polygon and ImageDraw.ellipse should behave the same for the mentioned cases.
The output created by the code below:

Looking at the polygon, if no outline is specified (top left image), we see that the black line is visible, which is one of the polygon's borders. Specifying an opaque outline (top center image), the black line's no longer visible. Setting a semi-transparent outline (top right image) reveals, that the outline is identical to the polygon's border.
Now, the same for the ellipse: If no outline is set (top left), an outline is shown nevertheless, most likely the same color as used for the fill parameter, but without incorporating an alpha value. Setting an opaque outline (top center) "overwrites" the unexpected existent outline, but when setting a semi-transparent outline, we see that the unexpected outline is still there.
This effect becomes even more obvious, when setting width > 1 in ellipse, see the bottom row. The unexpected outline still seems to have width = 1, whereas the explicitly set outline has width = 5.
from matplotlib import pyplot as plt
from PIL import Image, ImageDraw
def example(outline_alpha=None, width=None):
if outline_alpha is None:
outline = None
else:
outline = (255, 255, 0, outline_alpha)
if width is None:
width = 0
img = Image.new('RGB', (100, 100), (255, 255, 255))
drw = ImageDraw.Draw(img, 'RGBA')
drw.line([(0, 40), (100, 40)], (0, 0, 0, 255))
drw.line([(50, 100), (100, 0)], (0, 0, 0, 255))
drw.polygon([(50, 100), (100, 0), (0, 0)], (0, 255, 0, 128), outline)
drw.ellipse([(40, 40), (90, 90)], (0, 0, 255, 128), outline, width)
return img
plt.figure(1, figsize=(15, 10))
plt.subplot(2, 3, 1), plt.imshow(example()), plt.title('No outlines specified, width = 0')
plt.subplot(2, 3, 2), plt.imshow(example(255)), plt.title('Opaque outlines specified, width = 0')
plt.subplot(2, 3, 3), plt.imshow(example(128)), plt.title('Semi-transparent outlines specified, width = 0')
plt.subplot(2, 3, 4), plt.imshow(example(None, 5)), plt.title('No outlines specified, width = 5')
plt.subplot(2, 3, 5), plt.imshow(example(255, 5)), plt.title('Opaque outlines specified, width = 5')
plt.subplot(2, 3, 6), plt.imshow(example(20, 5)), plt.title('Semi-transparent outlines specified, width = 5')
plt.tight_layout()
plt.show()
Making 'opaque outlines' and 'semi-transparent outlines' behave the same as 'no outlines' specified is easy enough -
def ellipse(self, xy, fill=None, outline=None, width=0):
"""Draw an ellipse."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_ellipse(xy, fill, 1)
- if ink is not None and ink != fill:
+ if ink is not None and ink != fill and width != 0:
self.draw.draw_ellipse(xy, ink, 0, width)
However, 'no outlines' may be more difficult, as it is sort of a known problem -
If you're interested in a workaround, you could draw the ellipse without an alpha value, and then paste it with an alpha value.
I have created PR #4333 and #4334. #4333 will remove the darker outline using the fill colour, and #4334 will stop the application of the specified outline colour for a zero width.

Could I clarify - you have no problem with the behaviour of polygon, yes? Just ellipse? If both are merged to create the attached image, then this issue is resolved?
@radarhere Correct, polygon works as I would've expected. And, yes, issue's resolved then. Thank you very much for your effort! (And, sorry for not responding back in December, these holidays...)