Pillow handles PNG files incorrectly - python

I can successfully convert a rectangular image into a png with transparent rounded corners like this:
However, when I take this transparent cornered image and I want to use it in another image generated with Pillow, I end up with this:
The transparent corners become black. I've been playing around with this for a while but I can't find any way in which the transparent parts of an image don't turn black once I place them on another image with Pillow.
Here is the code I use:
mask = Image.open('Test mask.png').convert('L')
im = Image.open('boat.jpg')
im.resize(mask.size)
output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)
output.save('output.png')
im = Image.open('output.png')
image_bg = Image.new('RGBA', (1292,440), (255,255,255,100))
image_fg = im.resize((710, 400), Image.ANTIALIAS)
image_bg.paste(image_fg, (20, 20))
image_bg.save('output2.jpg')
Is there a solution for this? Thanks.
Per some suggestions I exported the 2nd image as a PNG, but then I ended up with an image with holes in it:
Obviously I want the second image to have a consistent white background without holes.
Here is what I actually want to end up with. The orange is only placed there to highlight the image itself. It's a rectangular image with white background, with a picture placed into it with rounded corners.

If you paste an image with transparent pixels onto another image, the transparent pixels are just copied as well. It looks like you only want to paste the non-transparent pixels. In that case, you need a mask for the paste function.
image_bg.paste(image_fg, (20, 20), mask=image_fg)
Note the third argument here. From the documentation:
If a mask is given, this method updates only the regions indicated by
the mask. You can use either "1", "L" or "RGBA" images (in the latter
case, the alpha band is used as mask). Where the mask is 255, the
given image is copied as is. Where the mask is 0, the current value
is preserved. Intermediate values will mix the two images together,
including their alpha channels if they have them.
What we did here is provide an RGBA image as mask, and use the alpha channel as mask.

Related

Change background color for Thresholderd image

I have been trying to write a code to extract cracks from an image using thresholding. However, I wanted to keep the background black. What would be a good solution to keep the outer boundary visible and the background black. Attached below is the original image along with the threshold image and the code used to extract this image.
import cv2
#Read Image
img = cv2.imread('Original.png')
# Convert into gray scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Image processing ( smoothing )
# Averaging
blur = cv2.blur(gray,(3,3))
ret,th1 = cv2.threshold(blur,145,255,cv2.THRESH_BINARY)
inverted = np.invert(th1)
plt.figure(figsize = (20,20))
plt.subplot(121),plt.imshow(img)
plt.title('Original'),plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(inverted,cmap='gray')
plt.title('Threshold'),plt.xticks([]), plt.yticks([])
Method 1
Assuming the circle in your images stays in one spot throughout your image set you can manually create a black 'mask' image with a white hole in the middle, then overlay it on the final inverted image.
You can easily make the mask image using your favorite image editor's magic wand tool.
I made this1 by also expanding the circle inwards by one pixel to take into account some of the pixels the magic wand tool couldn't catch.
You would then use the mask image like this:
mask = cv2.imread('/path/to/mask.png')
masked = cv2.bitwise_and(inverted, inverted, mask=mask)
Method 2
If the circle does NOT stay is the same spot throughout your entire image set you can try to make the mask from all the fully black pixels in your original image. This assumes that the 'sample' itself (the thing with the cracks) does not contain fully black pixels. Although this will result in the text on the bottom left to be left white.
# make all the non black pixels white
_,mask = cv2.threshold(gray,1,255,cv2.THRESH_BINARY)
1 The original is not the same size as your inverted image and thus the mask I made won't actually fit, you're gonna have to make it yourself.

How to overlay outline Image with transparent background and filling?

I want to take one image, and overlay it as its outline only without background/filling. I have one image that is an outline in PNG format, that has had its background, as well as the contents within the outline removed, so that when opened, all is transparent except the outline, similar to this image:
However, when I open the image and try to overlay it in OpenCV, the background and area within the outline shows as all-white, showing the full rectangle of the image's dimensions and obscuring the background image.
However, what I want to do is the following, where only the outline is overlayed on the background image, like so:
Bonus points if you can help me with changing the color of the outline as well.
I don't want to deal with any blending with alphas, as I need the background to appear in full, and want the outline very clear.
In this special case, your image has some alpha channel you can use. Using Boolean array indexing, you can access all values 255 in the alpha channel. What's left to do, is setting up some region of interest (ROI) in the "background" image w.r.t. some position, and in that ROI, you again use Boolean array indexing to set all pixels to some color, i.e. red.
Here's some code:
import cv2
# Open overlay image, and its dimensions
overlay_img = cv2.imread('1W7HZ.png', cv2.IMREAD_UNCHANGED)
h, w = overlay_img.shape[:2]
# In this special case, take the alpha channel of the overlay image, and
# check for value 255; idx is a Boolean array
idx = overlay_img[:, :, 3] == 255
# Open image to work on
img = cv2.imread('path/to/your/image.jpg')
# Position for overlay image
top, left = (50, 50)
# Access region of interest with overlay image's dimensions at position
# img[top:top+h, left:left+w] and there, use Boolean array indexing
# to set the color to red (for example)
img[top:top+h, left:left+w, :][idx] = (0, 0, 255)
# Save image
cv2.imwrite('output.png', img)
That's the output for some random "background" image:
For the general case, i.e. without a proper alpha channel, you could threshold the overlay image to set up a proper mask for the Boolean array indexing.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.8.5
OpenCV: 4.5.1
----------------------------------------

Python PIL remove every alpha channel completely

I tried so hard to converting PNG to Bitmap smoothly but failed every time.
but now I think I might found a reason.
it's because of the alpha channels.
('feather' in Photoshop)
Input image:
Output I've expected:
Current output:
I want to convert it to 8bit Bitmap and colour every invisible(alpha) pixels to purple(#FF00FF) and set them to dot zero. (very first palette)
but apparently, the background area and the invisible area around the actual image has a different colour.
i want all of them coloured same as background.
what should i do?
i tried these three
image = Image.open(file).convert('RGB')
image = Image.open(file)
image = image.convert('P')
pp = image.getpalette()
pp[0] = 255
pp[1] = 0
pp[2] = 255
image.putpalette(pp)
image = Image.open('feather.png')
result = image.quantize(colors=256, method=2)
the third method looks better but it becomes the same when I save it as a bitmap.
I just want to get it over now. I wasted too much time on this.
if i remove background from the output file,
it still looks awkward.
You question is kind of misleading as You stated:-
I want to convert it to 8bit Bitmap and colour every invisible(alpha) pixels to purple(#FF00FF) and set them to dot zero. (very first palette)
But in the description you gave an input image having no alpha channel. Luckily, I have seen your previous question Convert PNG to 8 bit bitmap, therefore I obtained the image containing alpha (that you mentioned in the description) but didn't posted.
HERE IS THE IMAGE WITH ALPHA:-
Now we have to obtain .bmp equivalent of this image, in P mode.
from PIL import Image
image = Image.open(r"Image_loc")
new_img = Image.new("RGB", (image.size[0],image.size[1]), (255, 0, 255))
cmp_img = Image.composite(image, new_img, image).quantize(colors=256, method=2)
cmp_img.save("Destination_path.bmp")
OUTPUT IMAGE:-

How to hide overlapping pixels using Pillow?

I have two images of similar dimensions as such:
Since the outer circle should have close to overlapping pixels, I would like to have a resultant image that has the inner circle from image A and the square from image B. I thought inverting image A and then calling PIL.Image.composite(imageA, imageB, mask) would do something but it just gave me a combination of imageA and imageB.
Is there a way to do what I want using Pillow or perhaps using numpy somehow to make white the pixels that are similar between both images?
I think you are looking for an XOR between the two images.
I'll work up to it slowly in case you don't do many logical expression evaluations, so starting with OR, you will get white pixels out as a result where the either image A OR image B has white pixels. Then with an AND, you will get white pixels out where both image A AND image B are white. Finally, with an XOR, you will get white pixels out where either image A or image B but exclusively one or the other but not both have white pixels.
In code, that looks like this:
#!/usr/local/bin/python3
from PIL import Image, ImageChops
# Load up the two images, discarding any alpha channel
im1 = Image.open('im1.png').convert('1')
im2 = Image.open('im2.png').convert('1')
# XOR the images together
result = ImageChops.logical_xor(im1,im2)
result = ImageChops.invert(result)
# Save the result
result.save('result.png')

PIL : PNG image as watermark for a JPG image

I'm trying to make a composite image from a JPEG photo (1600x900) and a PNG logo with alpha channel (400x62).
Here is a command that does the job with image magick:
composite -geometry +25+25 watermark.png original_photo.jpg watermarked_photo.jpg
Now I'd like to do something similar in a python script, without invoking this shell command externally, with PIL.
Here is what I tried :
photo = Image.open('original_photo.jpg')
watermark = Image.open('watermark.png')
photo.paste(watermark, (25, 25))
The problem here is that the alpha channel is completely ignored and the result is as if my watermark were black and white rather than rbga(0, 0, 0, 0) and rbga(255, 255, 255, 128).
Indeed, PIL docs state : "See alpha_composite() if you want to combine images with respect to their alpha channels."
So I looked at alpha_composite(). Unfortunately, this function requires both images to be of the same size and mode.
Eventually, I read Image.paste() more carefully and found this out:
If a mask is given, this method updates only the regions indicated by the mask. You can use either “1”, “L” or “RGBA” images (in the latter case, the alpha band is used as mask). Where the mask is 255, the given image is copied as is. Where the mask is 0, the current value is preserved. Intermediate values will mix the two images together, including their alpha channels if they have them.
So I tried the following :
photo = Image.open('original_photo.jpg')
watermark = Image.open('watermark.png')
photo.paste(watermark, (25, 25), watermark)
And... it worked!

Categories

Resources