PIL fromarray function is giving wierd colormap - python

I am writing a little gui for testing opencv functions - to easily change parameter values (for thresholding, blob detection etc.). I started writing the gui using tkinter and get wierd result with the Image.fromarray function - my image gets a blue tint; when I display with cv2.imshow there's no such tint so its gotta be an artifact of fromarry, I blv. I checked the mode and its RGB as expected. The image pairs are before and after blob detection (which draws little circles). The left pair is opencv and the right pair is in my tkinter gui.
tk_img=Image.fromarray(newImg)
tk_photo=ImageTk.PhotoImage(tk_img)
mod=tk_img.mode
print('mode:'+str(mod))
label1 = Tkinter.Label(self, image=tk_photo)
label1.image = tk_photo
label1.grid(row = Imrow, column = Im2col, columnspan = Im2col, sticky=Tkinter.NW)
self.update()
cv2.imshow('orig', currentImg)
cv2.waitKey(0)
cv2.imshow('current', newImg)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV works with images using BGR color order. You need to change color order.
newImg = newImg[...,[2,1,0]]
Look this tread for more info PIL rotate image colors (BGR -> RGB)

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
----------------------------------------

Reduce opacity of image using Opencv in Python

In the program given below I am adding alpha channel to a 3 channel image to control its opacity. But no matter what value of alpha channel I give there is no effect on image! Anyone could explain me why?
import numpy as np
import cv2
image = cv2.imread('image.jpg')
print image
b_channel,g_channel,r_channel = cv2.split(image)
a_channel = np.ones(b_channel.shape, dtype=b_channel.dtype)*10
image = cv2.merge((b_channel,g_channel,r_channel,a_channel))
print image
cv2.imshow('img',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
I can see in the terminal that alpha channel is added and its value changes as I change it in the program, but there is no effect on the opacity of the image itself!
I am new to OpenCV so I might be missing something simple. Thanks for help!
Alpha is a channel that is used to control the opacity of an image. An alpha channel typically doesn't do anything unless you perform an action on it. It doesn't make an image transparent on its own.
Alpha is usually used to either remove unimportant areas of an image or to combine one image with another image. In the first case the image is usually simply multiplied by its alpha. This is sometimes referred to premultiplying. In this case the dark areas of the alpha channel darken the RGB and the bright areas leave the RGB untouched.
R = R*A
G = G*A
B = B*A
Here is a version of your code that might do what you want (Note- I converted to 32-bit because it's easier to use alpha channels when they are ranged from 0 to 1):
import numpy as np
import cv2
i = cv2.imread('image.jpg')
img = np.array(i, dtype=np.float)
img /= 255.0
cv2.imshow('img',img)
cv2.waitKey(0)
#pre-multiplication
a_channel = np.ones(img.shape, dtype=np.float)/2.0
image = img*a_channel
cv2.imshow('img',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
The second case is used when trying to overlay an image over another image. This is a compositing operation that is often referred to as an "over" merge or a "blend" merge. In this case there is a foreground image "A" and a background image "B" and an alpha channel which could be included in the RGB images or on its own. In this case you can place A over B using:
output = (A * alpha) + (B * (1-alpha))
Actually, the answer is simple. OpenCV's imshow() function ignores the alpha channel.
If you want to see the effect of your alpha channel, save your image in PNG format (because that supports alpha channel) and display in a different viewer.
I also wrote a decorator/enhancement for imshow() here that helps visualise transparent images.

Pillow handles PNG files incorrectly

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.

Drawing semi-transparent polygons in PIL

How do you draw semi-transparent polygons using the Python Imaging Library?
Can you draw the polygon on a separate RGBA image then use the Image.paste(image, box, mask) method?
Edit: This works.
from PIL import Image
from PIL import ImageDraw
back = Image.new('RGBA', (512,512), (255,0,0,0))
poly = Image.new('RGBA', (512,512))
pdraw = ImageDraw.Draw(poly)
pdraw.polygon([(128,128),(384,384),(128,384),(384,128)],
fill=(255,255,255,127),outline=(255,255,255,255))
back.paste(poly,mask=poly)
back.show()
http://effbot.org/imagingbook/image.htm#image-paste-method
I think #Nick T's answer is good, but you need to be careful when using his code as written with a very large background image, especially in the case that you may be annotating several polygons on said image. This is something I do when processing huge satellite images with some object detection code and annotating the detections using a transparent rectangle. To make the code efficient no matter the size of the background image, I make the following suggestion.
I would modify the solution to specify that the polygon image that you will paste be only as large as required to hold the polygon, not the same size as the back image. The coordinates of the polygon are specified with respect to the local bounding box, not the global image coordinates. Then you paste the polygon image at the offset in the larger background image.
import Image
import ImageDraw
img_size = (512,512)
poly_size = (256,256)
poly_offset = (128,128) #location in larger image
back = Image.new('RGBA', img_size, (255,0,0,0) )
poly = Image.new('RGBA', poly_size )
pdraw = ImageDraw.Draw(poly)
pdraw.polygon([ (0,0), (256,256), (0,256), (256,0)],
fill=(255,255,255,127), outline=(255,255,255,255))
back.paste(poly, poly_offset, mask=poly)
back.show()
Using the Image.paste(image, box, mask) method will convert the alpha channel in the pasted area of the background image into the corresponding transparency value of the polygon image.
The Image.alpha_composite(im1,im2) method utilizes the alpha channel of the "pasted" image, and will not turn the background transparent. However, this method again needs two equally sized images.

Categories

Resources