Repository navigation
ImageDraw.Draw.rectangle is ignoring fill alpha #2496
Description
Activity
It appears to be filling the rectangle with (0,0,0,127).
I'm not clear, but you either want to alpha composite a black half transparent rectangle over the cat, or put a half transparent alpha channel over the cat. Look at
Image.alpha_compositeorImage.putalpha.According to my SO question, it's not a bug, but rather I misunderstood what Draw is intented to do.
http://stackoverflow.com/questions/43618910/python-pil-drawing-a-semi-transparent-square-overlay-on-image#43620169
Closing this issue.More than 6 Years later and I'm running into the same confusion. Version 10.x comments in ImageDraw:
def __init__(self, im, mode=None): """ Create a drawing instance. :param im: The image to draw in. :param mode: Optional mode to use for color values. For RGB images, this argument can be RGB or RGBA (to blend the drawing into the image). For all other modes, this argument must be the same as the image mode. If omitted, the mode defaults to the mode of the image. """
[...] or RGBA (to blend the drawing into the image)
It's still replacing the original values of the image with the drawing (eg. polygon) resulting in black Images with Alpha instead of 'blending' anything.
I'd recommend to alter the Comment and maybe reference the alpha_composite option?The docstring states
For RGB images, this argument can be RGB or RGBA (to blend the drawing into the image).
In my evaluation, the problem in the code posted earlier is that it wasn't an RGB image.
img = img.convert("RGBA") draw = ImageDraw.Draw(img, "RGBA")
You can see, the image is converted to RGBA before being given to ImageDraw.
If the original image
is left as an RGB image instead,
from PIL import Image from PIL import ImageDraw from io import BytesIO from urllib.request import urlopen url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg" file = BytesIO(urlopen(url).read()) img = Image.open(file) # Commenting out this line to keep it as an RGB image #img = img.convert("RGBA") draw = ImageDraw.Draw(img, "RGBA") draw.rectangle(((0, 00), (img.size[0], img.size[1])), fill=(0,0,0,127)) img.save('dark-cat.jpg')
the result is fine.
As far as I can see, RGB images with RGBA ImageDraw instances blend perfectly well?
Your example seems even less intuitive. Using 'RGB' mode on the Image and 'RGBA' mode on ImageDraw works perfectly fine.
On the other side, using RGBA mode on both results in overwriting the original image.
I would expect both compositions to behave the same way. Is there a reason why it should not? Is this the intended behavior?
Keep the Docs in mind:
"[...] this argument can be RGB or RGBA (to blend the drawing into the image)."
This should at least emphasise, that the original image needs to be opened in 'RGB' mode.Reacted by seanjensengreyIt has been this way since the fork from PIL. Pillow values backwards compatibility, so I'm reluctant to change the behaviour, as it might break the code of existing users.
For RGB images, this argument can be RGB or RGBA (to blend the drawing into the image)
That is the only mention of blending in the docstring, and so seems to clearly state that blending requires an RGB image. If have an alternate suggestion for what the docstring should look like, feel free to suggest it.
This is really annoying and seems to be a bug.
# Fails for very large images def save_image(fn, data, dpi=100): shape=np.shape(data)[0:2][::-1] size = [float(i)/dpi for i in shape] fig = plt.figure() fig.set_size_inches(size) ax = plt.Axes(fig,[0,0,1,1]) ax.set_axis_off() fig.add_axes(ax) ax.imshow(data) fig.savefig(fn, dpi=dpi) #, bbox_inches='tight', format='svg') #plt.show() # fails to handle alpha correctly def save_as_png(canvas, fileName, type): canvas.save(fileName + '.' + type, type)
I am drawing a rectangle to set the background color then I draw row rectangles to make a grid. Using the first save the alpha channel works correctly and the rows blend in with the background. But here the image is cropped at a width of around 19000. Using the second no matter what I try the alpha channel is not correctly blended(it seems to just ignore the alpha). I don't know how PIL works but if it is some "retained mode" drawing then the canvas.save seems to ignore the alpha completely. I've tried both RGB and RGBA and it's the same problem.
It seems to be an issue with the save rather than anything else given that using the matplotlib saves the alpha correctly(but which I can't use since it is cropping the image for some reason).
It fails when, as a commenter says, when the image has an alpha channel.
It seems that the code that combines alphas is flawed and does not properly merge pixels with alpha.
url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg" file = BytesIO(urlopen(url).read()) img = Image.open(file) img = img.convert("RGBA") draw = ImageDraw.Draw(img, "RGBA") draw.rectangle(((280, 10), (1010, 706)), fill=(200, 100, 0, 127)) draw.rectangle(((280, 10), (1010, 706)), outline=(0, 0, 0, 127), width=3) img.save('Z:/orange-cat.png')
Without knowing what variables you are passing into the functions, the code from your first comment doesn't seem to use Pillow at all.
If you do not convert the image to RGBA in your second comment,
from PIL import Image, ImageDraw from io import BytesIO from urllib.request import urlopen url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg" file = BytesIO(urlopen(url).read()) img = Image.open(file) #img = img.convert("RGBA") draw = ImageDraw.Draw(img, "RGBA") draw.rectangle(((280, 10), (1010, 706)), fill=(200, 100, 0, 127)) draw.rectangle(((280, 10), (1010, 706)), outline=(0, 0, 0, 127), width=3) img.save('orange-cat.png')
then the orange square over the cat becomes translucent. See #2496 (comment) for my prior explanation of this behaviour.
Your example seems even less intuitive. Using 'RGB' mode on the Image and 'RGBA' mode on ImageDraw works perfectly fine.
On the other side, using RGBA mode on both results in overwriting the original image.
I would expect both compositions to behave the same way. Is there a reason why it should not? Is this the intended behavior? Keep the Docs in mind: "[...] this argument can be RGB or RGBA (to blend the drawing into the image)." This should at least emphasise, that the original image needs to be opened in 'RGB' mode.
First, 7 years later from the tickets creation, thank you for saving me hours of pain to finally get the result I wanted. @MyNameIsFu
Is there an existing ticket for improving the documentation on this? I even feel like an embedded warning might be appropriate... this is a real head-sore for new users. Happy to do those doc-changes myself if they would be appreciated.
cc @radarhere
https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.Draw
mode – Optional mode to use for color values. For RGB images, this argument can be RGB or RGBA (to blend the drawing into the image)
If you think this doesn't sufficiently explain that an RGB image with an RGBA mode is needed to blend, then feel free to suggest other wording.
I get that backwards compatibility is important somewhat, but not being able to draw in RGBA on top of RGBA is a serious feature gap which has had me looking around for some other more complete, more modern drawing library. (There's not a lot of great options... python is pretty weak in this area) But I figured out I can remedy with this simple fix, run this once in whichever file first imports ImageDraw:
image_draw_init = ImageDraw.__init__ def image_draw_init_force_blend(self, *args, **kwargs): image_draw_init(self, *args, **kwargs) self.draw = Image.core.draw(self.im, 1) ImageDraw.__init__ = image_draw_init_force_blend
I'm still thinking of trying something else like SKIA though.
not being able to draw in RGBA on top of RGBA is a serious feature gap
It is possible to do so. Taking code from #2496 (comment)
from PIL import Image, ImageDraw from io import BytesIO from urllib.request import urlopen url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg" file = BytesIO(urlopen(url).read()) img = Image.open(file) draw = ImageDraw.Draw(img, "RGBA") draw.rectangle(((280, 10), (1010, 706)), fill=(200, 100, 0, 127)) draw.rectangle(((280, 10), (1010, 706)), outline=(0, 0, 0, 127), width=3) img.save('orange-cat.png')
I can draw a translucent orange rectangle on top of the image.
Edit: Looking at this now, this code is actually drawing RGBA on top of RGB. I think different people might have different expectations about how the alpha values should be combined. Here is one possibility to consider.
from PIL import Image, ImageDraw im = Image.new("RGBA", (1, 1), (255, 0, 0, 255)) im2 = Image.new("RGBA", (1, 1)) d = ImageDraw.Draw(im2) d.rectangle((0, 0, 1, 1), (0, 255, 0, 127)) im.paste(im2.convert("RGB"), mask=im2) assert im.getpixel((0, 0)) == (128, 127, 0, 255)
I was also hit by the 'base image should be in RGB, but the drawn object should be in RGBA' roadblock for an hour today. The documentation fixes or a change to the example would have been a big help.
Reacted by Ricardo HerrmannWhich example are you referring to?
https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.Draw
mode – Optional mode to use for color values. For RGB images, this argument can be RGB or RGBA (to blend the drawing into the image)
If you think this doesn't sufficiently explain that an RGB image with an RGBA mode is needed to blend, then feel free to suggest other wording.
Reacted by Ross Olson- added a commit that references this issue
on Sep 9, 2025






What did you do?
What did you expect to happen?
I expected to see this:

What actually happened?
I got a black square:

What versions of Pillow and Python are you using?
Python 3.5.2
Pillow==4.1.0