Reputation: 9376
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)
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')
This is giving me a giant black square. I want it to be a semi transparent black square with a cat. Any Ideas?
Upvotes: 29
Views: 29585
Reputation: 9720
Given that I keep coming back to this issue whenever I want to draw a transparent rectangle with PIL, I decided to give an update.
Your code is pretty much working for me if I just change one thing: Save the image in the PNG format instead of JPEG.
So when I'm running
from io import BytesIO
from urllib.request import urlopen
from PIL import Image
from PIL import ImageDraw
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 get this wonderful image:
Upvotes: 25
Reputation: 123473
Sorry, the comment I made about it being a bug was incorrect, so...
You can do it by creating a temporary image and using Image.alpha_composite()
as shown in the code below. Note that it supports semi-transparent squares other than black.
from PIL import Image, ImageDraw
from io import BytesIO
from urllib.request import urlopen
TINT_COLOR = (0, 0, 0) # Black
TRANSPARENCY = .25 # Degree of transparency, 0-100%
OPACITY = int(255 * TRANSPARENCY)
url = "https://i.ytimg.com/vi/W4qijIdAPZA/maxresdefault.jpg"
with BytesIO(urlopen(url).read()) as file:
img = Image.open(file)
img = img.convert("RGBA")
# Determine extent of the largest possible square centered on the image.
# and the image's shorter dimension.
if img.size[0] > img.size[1]:
shorter = img.size[1]
llx, lly = (img.size[0]-img.size[1]) // 2 , 0
else:
shorter = img.size[0]
llx, lly = 0, (img.size[1]-img.size[0]) // 2
# Calculate upper point + 1 because second point needs to be just outside the
# drawn rectangle when drawing rectangles.
urx, ury = llx+shorter+1, lly+shorter+1
# Make a blank image the same size as the image for the rectangle, initialized
# to a fully transparent (0% opaque) version of the tint color, then draw a
# semi-transparent version of the square on it.
overlay = Image.new('RGBA', img.size, TINT_COLOR+(0,))
draw = ImageDraw.Draw(overlay) # Create a context for drawing things on it.
draw.rectangle(((llx, lly), (urx, ury)), fill=TINT_COLOR+(OPACITY,))
# Alpha composite these two images together to obtain the desired result.
img = Image.alpha_composite(img, overlay)
img = img.convert("RGB") # Remove alpha for saving in jpg format.
img.save('dark-cat.jpg')
img.show()
Here's the result of applying it to your test image:
Upvotes: 36
Reputation: 9597
If you just want to dim the entire image, there's a simpler way:
img = Image.eval(img, lambda x: x/2)
Upvotes: 2