I'm making a drawing program where you can later save and export your changes as an image. The non-transparent image option works perfectly, but the transparent option does not. The code I have right now is based on this post.
Whenever I draw a line in the transparent option, nothing on the image shows up. It's completely transparent.
print("Transparent:", str(transparent))
if not transparent:
image = np.zeros((height, width, 3), np.uint8) # initialize image for saving preferences
image[:] = backgroundColorBGR # change background color
for drawing in drawings: # loop through each drawing
cv2.line(image, drawing[0], drawing[1], drawing[2], thickness = drawing[3]) # create line that user drew
cv2.imwrite("yourimage.png", image)
else:
image = np.zeros((height, width, 4), dtype = np.uint8) # 4 channels instead of 3, for transparent images
for drawing in drawings: # loop through each drawing
cv2.line(image, drawing[0], drawing[1], drawing[2], thickness = drawing[3])
cv2.imwrite("yourimage.png", image)
Thanks to Mark Setchell, I found a working solution to this. In the color parameter in cv2.line(), pass a tuple where the first three values are the BGR color. The fourth value is for transparency/alpha. So your code should look like cv2.line(color=(0, 0, 200, 255)) # etc if you want to draw on a transparent image.
The OpenCV drawing functions are very limited and only intended for simple markings in images.
They do not support the concept of transparency in images. For example, the line method understands two modes: three channels (colored line) or non-three channels (grayscale line). That's it.
The result is that in your case, the same value is written to all four channels. A black line should stay invisible, while a white or blue line should end up as a white line.
Reference: Source code of the method
Related
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.
I have a binary image where I am trying to identify the shape inside of a black blob. Both the background and the object of interest are white. I am trying to find a solution to inverting only the white background such that the only white pixels remaining is the shape inside of the black blob. Is there a way to separate identifying all the white pixels from the background from the white pixels of the object?
Sample Input:
Desired Output:
It's clear that the background is not "surrounded" anywhere by the black object. Then you can use cv2.floodFill to fill the white background with new_color:
# assuming `img` is binary image
seed = (0,0) # a point in the background, here I choose top-left corner
new_color = (0,0,0) # since you want to make it black
cv2.floodFill(img, None, seedPoint=seed, newVal=new_color)
cv2.imshow('flood', img)
Note that this function is an in-place operation, which means changes are applied directly to img. If you want to keep your old img, consider copying it:
backup_img = np.copy(img)
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
----------------------------------------
I am trying to combine some parts of the image together while still maintaining some parts unchanged.
This is first image
This is the code to get the first image, the parameter for the input are img which is original image but already colorized with green while jawline,eyebrows,etc are (x,y) coordinates to cut those parts from the image
def getmask(img,jawline,eyebrows,eyes,mouth):
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
imArray = np.asarray(img)
# create mask
polygon = jawline.flatten().tolist()
maskIm = Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)
ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill='white')
#ImageDraw.Draw(maskIm).polygon(polygon, outline=(1))
# draw eyes
righteyes=eyes[0:6].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(righteyes, outline=1, fill='black')
lefteyes=eyes[6:].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(lefteyes, outline=1, fill='black')
# draw eyebrows
rightbrows=eyebrows[0:6].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(rightbrows, outline=2, fill='black')
leftbrows=eyebrows[6:].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(leftbrows, outline=2, fill='black')
# draw mouth
mouth=mouth.flatten().tolist()
ImageDraw.Draw(maskIm).polygon(mouth, outline=1, fill='black')
mask = np.array(maskIm)
mask = np.multiply(img,mask)+ np.multiply((1-mask),np.ones((L,P,3)))
return mask
This is the second image which will fill the white blank inside the first image
I used this code to cut the parts which is very similar to the code on first image.
def getface(img,eyebrows,eyes,mouth):
im=img.copy()
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
imArray = np.asarray(img)
# create mask
maskIm = Image.new('L', (imArray.shape[1], imArray.shape[0]), 0)
righteyes=eyes[0:6].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(righteyes, outline=1,fill='white')
lefteyes=eyes[6:].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(lefteyes, outline=1,fill='white')
# draw eyebrows
rightbrows=eyebrows[0:6].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(rightbrows, outline=2, fill='white')
leftbrows=eyebrows[6:].flatten().tolist()
ImageDraw.Draw(maskIm).polygon(leftbrows, outline=2, fill='white')
# draw mouth
mouth=mouth.flatten().tolist()
ImageDraw.Draw(maskIm).polygon(mouth, outline=1, fill='white')
cutted_part = np.array(maskIm)
cutted_part = cv2.bitwise_or(im,im,mask=mask)
return cutted_part
So far I have tried to combine those two images by first inversing the second image so that the black background become white and then multiply the first and second image. But the result isn't satisfactory.
As you can see, there are some white space between the combined area and I notice that some part from second image become smaller or missing which I suspect create those white space when combined (Please don't mind the slightly different color on the result). Maybe someone can share how to resolve this problem or has better ways to combine 2 images together?
If you provide your results as actual pictures instead of cropped screenshots we can reproduce your problem, so far i would recommend:
Invert the background of your cutout (black to white) and then simply combine both pictures either by adding them (They need to have the same dimensions, which i presume is the case.) or overlaying them by using opencv's addWeighted function to adjust opacity.
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.