I'm trying to add an transparent overlay on an jpeg image. In the example below, the desired result would be a red image with a pieslice where the it is light red.
My input is an jpeg Image. Since jpeg doesn't have an alpha channel, I though I could convert it to an 'RGBA' image and paste the overlay on it:
from PIL import Image, ImageDraw
# img = Image.open('input.jpg').convert('RGBA')
img = Image.new('RGBA', (400,400), (255,0,0))
img2 = Image.new('RGBA', (400,400))
draw2 = ImageDraw.Draw(img2)
draw2.pieslice([0,0,400,400], 90, 180, fill='white')
img.putalpha(128)
img.save('img.png')
img2.save('img2.png')
img.paste(img2)
img.save('img1+2.png')
However, this doesn't have the desired effect, and windows Photos cannot even open it correctly.
I saw the blend and alpha_composite functions but it doesn't have the desired effect for me. I don't want to lower the alpha values of the background outside of the overlay.
Related
I have two images.
Image 1:
Image 2:
Both the images have the same resolution. I want to overlay the second image on the first image.
This is what I tried:
from PIL import Image
img1 = Image.open("D:\\obj1__0.png")
img1 = img1.convert("RGBA")
img1 = img1.resize((640,360))
img2 = Image.open('D:\\Renderforest_Watermark.png')
img2 = img2.convert("RGBA")
img2 = img2.resize((640,360))
new_img = Image.blend(img1,img2,0.7)
new_img.save("D:\\Final.png")
Output:
My Question: How do I overlay image 2 on image 1 such that only the watermark is overlaid?
P.S: I already searched for this on Stack Overflow, but couldn't find any answers.
Your overlay image is incorrectly formed. It should be transparent where it is white. It has no transparency, so it isn't see-through. I can only suggest to make a synthetic alpha channel by guessing that it should be transparent where the overlay image is white:
#!/usr/bin/env python3
from PIL import Image
# Set a common size
size = (640, 360)
# Load background and overlay, removing the pointless alpha channel and resizing to a common size
bg = Image.open('background.png').convert('RGB').resize(size)
overlay = Image.open('overlay.png').convert('RGB').resize(size)
# Try and invent a mask by making white pixels transparent
mask = overlay.convert('L')
mask = mask.point(lambda p: 255 if p < 225 else 0)
# Paste overlay onto background only where the mask is, then save
bg.paste(overlay, None, mask)
bg.save('result.png')
If your image did have an alpha channel, you would avoid deleting the original alpha channel and open it like this instead:
overlay = Image.open('overlay.png').resize(size)
then delete these lines:
# Try and invent a mask by making white pixels transparent
mask = overlay.convert('L')
mask = mask.point(lambda p: 255 if p < 225 else 0)
then change the line after the above to:
# Paste overlay onto background only where the mask is, then save
bg.paste(overlay, None, overlay)
Keywords: Image processing, PIL, Pillow, overlay, watermark, transparent, alpha.
I have been messing around in python to see if I could "mix" two pictures together. What I mean by that is so that the image is transparent and you can see two pictures together. If that still does not make sense check out this link: (only I would mix a picture and a picture not a gif)
https://cdn.discordapp.com/attachments/652564556211683363/662770085844221963/communism.gif
Here is my code:
from PIL import Image
im1 = Image.open('oip.jpg')
im2 = Image.open('star.jpg')
bg = Image.blend(im1, im2, 0)
bg.save('star_oip_paste.jpg', quality=95)
and I get the error:
line 6, in <module> bg = Image.blend(im1, im2, 0) ValueError: images do not match
I'm not even sure if I'm using the right function for "mixing" two images together — so if I'm not, let me know.
There are several things going on here:
Your input images are both JPEG which doesn't support transparency, so you can only get a fixed blending throughout your image. I mean you can't see one image at one point and the other image at another. You will only see the same proportion of each image at each point. Is that what you want?
For example, if I take Paddington and Buckingham Palace and take 50% of each:
I get this:
If that's what you want, you need to resize the images to a common size and change this line:
bg = Image.blend(im1, im2, 0)
to
bg = Image.blend(im1, im2, 0.5) # blend half and half
If you want to paste something with transparency, so it only shows up in certain places, you need to load the overlay from a GIF or PNG with transparency and use:
background.paste(overlay, box=None, mask=overlay)
Then you can do this - note you can see different amounts of the two images at each point:
So, as a concrete example of overlaying a transparent image onto an opaque background, and starting with Paddington (400x400) and this star (500x500):
#!/usr/bin/env python3
from PIL import Image
# Open background and foreground and ensure they are RGB (not palette)
bg = Image.open('paddington.png').convert('RGB')
fg = Image.open('star.png').convert('RGBA')
# Resize foreground down from 500x500 to 100x100
fg_resized = fg.resize((100,100))
# Overlay foreground onto background at top right corner, using transparency of foreground as mask
bg.paste(fg_resized,box=(300,0),mask=fg_resized)
# Save result
bg.save('result.png')
If you want to grab an image from a website, use this:
from PIL import Image
import requests
from io import BytesIO
# Grab the star image from this answer
response = requests.get('https://i.stack.imgur.com/wKQCT.png')
# Make it into a PIL image
img = Image.open(BytesIO(response.content))
As an alternative, you could try with OpenCV (depending on your desired output)
import cv2
# Read the images
foreground = cv2.imread("puppets.png")
background = cv2.imread("ocean.png")
alpha = cv2.imread("puppets_alpha.png")
# Convert uint8 to float
foreground = foreground.astype(float)
background = background.astype(float)
# Normalize the alpha mask to keep intensity between 0 and 1
alpha = alpha.astype(float)/255
# Multiply the foreground with the alpha matte
foreground = cv2.multiply(alpha, foreground)
# Multiply the background with ( 1 - alpha )
background = cv2.multiply(1.0 - alpha, background)
# Add the masked foreground and background.
outImage = cv2.add(foreground, background)
# Display image
cv2.imshow("outImg", outImage/255)
cv2.waitKey(0)
I'm trying to read a RGBA BMP using python PIL, and it doesn't seem to work.
The following code segment shows that tensorflow bmp_decode function succeeds in this task, while PIL doesn't:
def read_image_tf(filename):
image_file = tf.read_file(filename, name='read_file')
decoded_bmp = tf.io.decode_bmp(bmp_image)
return decoded_bmp
def read_img_pil(filename):
img = np.asarray(Image.open(fh))
return img
img = K.eval(read_image_tf(<FILENAME>))
print (img.shape)
img = read_img_pil(<FILENAME>)
print (img.shape)
Output:
(3892, 3892, 4)
(3892, 3892, 3)
When trying to run imgobj.convert('RGBA') on Image.open(fh) I simply get a matrix that contains only the value of 255 (100% transparency, which is not the correct alpha value per pixel).
Is there a bug in PIL? Is there an alternative to reading RGBA using python?
PIL doesn't support 32 bit bitmap images. As the official documentation states:-
Pillow reads and writes Windows and OS/2 BMP files containing 1, L, P, or RGB data. 16-colour images are read as P images. Run-length encoding is not supported.
That's why it is generally recommended not to use Image.show() to view an image, as it converts the image to .bmp before displaying it. Therefore if the image contained alpha values (image of color mode LA, RGBA etc) the displayed image will not be display properly, and will have artifacts.
Therefore, when you try to open a .bmp image having RGBA color space in PIL, the color space gets truncated to RGB.
Example:-
from PIL import Image
# creating an red colored image with RGBA color space and full opacity
img = Image.new("RGBA", (100, 100), (255, 0, 0, 255))
# displaying the color mode of the image
print(img.mode)
# saving the image as a .bmp (bitmap)
img.save("new.bmp")
# Opening the previously saved .bmp image (having color mode RGBA)
img = Image.open("new.bmp")
# displaying the mode of the .bmp file
print(img.mode)
OUTPUT:-
RGBA
RGB
I am new to OpenCV so please bear with me if my qustion seems silly to you.
I have a set of images that all have a transparent border on the left and right like you can see below:
I want to erase these borders so I thought about edge detection which would be easy to do if I could transform these transparent borders to a white color. In the Docs I found that you can do this:
img = cv2.imread("./Green/image-000.png", 1)
cv2.imwrite('../image-000.png', img)
This erases the alpha channel of the png image but turns it into black.
Is there something similar that turns the borders white?
Or is there even a simpler method of erasing these borders?
You would make me really happy if you could help me!
PS: I use Python 2.7 and OpenCV 3.4
You should load image with IMREAD_UNCHANGED, i.e.
import cv2 as cv
img = cv.imread("./Green/imgage-000.png", cv.IMREAD_UNCHANGED)
Then, your image will have 4 channels (BGRA), and you can use alpha channel mask to turn the corresponding part to white:
alpha_channel = img[:, :, 3]
_, mask = cv.threshold(alpha_channel, 254, 255, cv.THRESH_BINARY) # binarize mask
color = img[:, :, :3]
new_img = cv.bitwise_not(cv.bitwise_not(color, mask=mask))
I tested this code with a transparent PNG where the color channels were black and the information was in the transparency:
The nested bitwise_not is ugly but is the only way I found to make it work.
I want to crop an image to its smaller size, by cutting the white areas on the borders. I tried the solution suggested in this forum Crop a PNG image to its minimum size but the getbbox() method of pil is returning a bounding box of the same size of the image, i.e., it seems that it doesn't recognize the blank areas around. I tried the following:
>>>import Image
>>>im=Image.open("myfile.png")
>>>print im.format, im.size, im.mode
>>>print im.getbbox()
PNG (2400,1800) RGBA
(0,0,2400,1800)
I checked that my image has truly white croppable borders by cropping the image with the GIMP auto-crop. I also tried with ps and eps versions of the figure, without luck.
Any help would be highly appreciated.
Trouble is getbbox() crops off the black borders, from the docs: Calculates the bounding box of the non-zero regions in the image.
import Image
im=Image.open("flowers_white_border.jpg")
print im.format, im.size, im.mode
print im.getbbox()
# white border output:
JPEG (300, 225) RGB
(0, 0, 300, 225)
im=Image.open("flowers_black_border.jpg")
print im.format, im.size, im.mode
print im.getbbox()
# black border output:
JPEG (300, 225) RGB
(16, 16, 288, 216) # cropped as desired
We can do an easy fix for white borders, by first inverting the image using ImageOps.invert, and then use getbbox():
import ImageOps
im=Image.open("flowers_white_border.jpg")
invert_im = ImageOps.invert(im)
print invert_im.getbbox()
# output:
(16, 16, 288, 216)