-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
ImageDraw.Draw.rectangle is ignoring fill alpha #2496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
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 |
According to my SO question, it's not a bug, but rather I misunderstood what Draw is intented to do. |
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.
"""
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. |
The docstring states
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? |
It 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.
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. |
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
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. |
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. ![]() |
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
The text was updated successfully, but these errors were encountered: