Removing Green Background and Replace it with other - python

So i'm having a problem with removing green background and replacing it, . I have this black box in a green background. When i replaced it with np.where(pixel == 0), the program chooses the black box and the mask to replace with another background. How do i fix this problem?
Here's my code:
import cv2
import numpy as np
frame = cv2.imread("black_box.jpg")
back_ground= cv2.imread("back_ground.jpg")
if back_ground.shape != frame.shape:
back_ground = cv2.resize(back_ground,(frame.shape[1],frame.shape[0]))
hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
lower_green = np.array([42, 180, 39])
green = np.array([81,255,255])
mask = cv2.inRange(hsv,lower_green,green)
mask_inv = cv2.bitwise_not(mask)
fg = cv2.bitwise_and(frame,frame, mask = mask_inv)
fg = np.where(fg == 0,back_ground,fg)
cv2.imshow("fg",fg)
cv2.waitKey(0)

I think there are some confusion on what your image is and what your background is. You apply the hsv conversion to the black and green image to detect the green then you do a bitwise_and mask with that mask and the same input image.
My code below gives, I believe, what you are after.
import cv2
import numpy as np
frame = cv2.imread("im2.png")
back_ground= cv2.imread("im1.png")
if back_ground.shape != frame.shape:
back_ground = cv2.resize(back_ground,(frame.shape[1],frame.shape[0]))
hsv = cv2.cvtColor(back_ground,cv2.COLOR_BGR2HSV)
lower_green = np.array([42, 180, 39])
green = np.array([81,255,255])
mask = cv2.inRange(hsv,lower_green,green)
mask_inv = cv2.bitwise_not(mask)
fg = cv2.bitwise_and(frame,frame, mask = mask)
fg_inv = cv2.bitwise_and(frame,frame, mask = mask_inv)
#fg = np.where(fg == 0,back_ground,fg) - unnecessary
cv2.imshow("mask", mask)
cv2.imshow("fg", fg)
cv2.imshow("mask_inv", mask_inv)
cv2.imshow("fg_inv", fg_inv)
cv2.waitKey(0)

Related

Can anyone tell me how can I change mask the foreground if I know the color range of background in RGB?

all of you,
Here is the image;
The exact background color is in RGB; (246, 46, 100)
I have tried several methods but those are too slow, one of the methods is below;
new_image = Image.open("image-from-rawpixel-id-6649116-original.png")
img_up = np.asarray(new_image)
for ind1, i in enumerate(tqdm(img_up)):
for ind2, i2 in enumerate(i):
if list(i2[:3]) != a:
img_up2 = img_up.copy()
img_up2.setflags(write=1)
img_up2[ind1][ind2][:3] = [0,0,0]
cv2.imshow('', img_up2)
cv2.waitKey()
I want to make the background white and the foreground person black (masked), but unable to find a quick method.
Modified
I have tied another method to mask the foreground but I think, I am doing some mistakes while converting between RGBs. Below is the code;
path = 'image-from-rawpixel-id-2923073-png.png'
im = Image.open(path).convert('RGBA')
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
#--------------------------- To change the background color that is not present in the foreground ---------------------------------------------
lst_ch_a = []
for pixel in tqdm(im.getdata()):
lst_ch_a.append(pixel[:3])
break_out = True
while break_out:
a = random.sample(range(0, 255), 3)
if a not in lst_ch_a:
new_image = Image.new("RGBA", im.size, tuple(a))
print(tuple(a))
break_out = False
new_image.paste(im, mask=im)
new_image.convert("RGB").save("check6.jpg")
#--------------------------- Numpy ----------------------------------------------------------------
img_up = np.asarray(new_image)
img = img_up.copy()
img.setflags(write=1)
img[:,:,:3][img[:,:,:3] != tuple(a)] = 0
img[img!=0]=255
img = cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA)
img = cv2.resize(img,(500,500), cv2.INTER_LINEAR)
cv2.imwrite('results1.jpg', img)
cv2.imshow('', img)
cv2.waitKey(0)
Below is the result, but I am getting pixels of Blue, green, red, and some other colors in the image. do you know why I am getting this?
You can see in the above first image, where I have changed the background. The image was transparent image, but then I changed the background color. There was no green, blue, or red colors but while masking the foreground the red, blue, and green color emerges.
Do you know why it is happening?
First you could read image using cv2.imread() and you get directly numpy.array.
You can use numpy image[ mask ] = [0,0,0] to assign value to many pixels in milliseconds.
For exact color you can create mask using img == (100, 46, 247).
cv2 keeps image as BGR instead of RGB so it needs (100,46,247) instead of (247,46,100).
It needs also .all(axis=-1) because it compares every value B,G,R separatelly and gets tuples (True, True, False) but it needs to reduce it to single True when all values are True.
import cv2
img = cv2.imread("image.png")
#print('color:', img[0, 0]) # [100 46 247]
mask = (img == (100, 46, 247)).all(axis=-1)
img1 = img.copy()
img2 = img.copy()
img1[ mask ] = [0,0,0]
img2[ ~mask ] = [0,0,0]
cv2.imshow('image1', img1)
cv2.imshow('image2', img2)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite('image2-1.png', img1)
cv2.imwrite('image2-2.png', img2)
Result:
image1:
image2:
BTW:
cv2 has function inRange() to select colors in some ranges and it may give better result.
Example code but I didn't find good range for this image.
Besides it starts removing similar pixels in lips.
import cv2
import numpy as np
img = cv2.imread("image.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#print('hsv:', hsv[0, 0])
lower = np.array([92, 120, 147])
upper = np.array([172, 208, 247]) # for BGR (100, 46, 247) - value from hsv[0,0]
upper = np.array([202, 218, 247])
mask = cv2.inRange(hsv, lower, upper)
print('masking')
# `mask==255` `mask==0`
#img = cv2.bitwise_and(img, img, mask=~mask) # OK
#img[np.where(mask==255)] = [0,0,0] # OK
img[ mask==255 ] = [0,0,0] # OK
#img[ mask.astype(bool) ] = [0,0,0] # OK
#img[ mask ] = [0,0,0] # WRONG (hangs)
print('display')
#h, w = img.shape[:2]
#img = cv2.resize(img, (h//5, w//5))
cv2.imshow('image', img)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite('image2b.jpg', img)
See also: Finding red color in image using Python & OpenCV - Stack Overflow
You should be able to do this more effectively with PIL.ImageDraw.floodfill() which is described here.
If you use a global replacement, you will catch reddish tones (such as the model's lips) anywhere and everywhere they occur in the image. If you use a floodfill from the top-left corner with a suitable tolerance, the flooding should get stopped by her dark hair before it can contaminate her lips.
The result should be like this:
I actually did that with ImageMagick equivalent operator in Terminal as I don't currently have Python to hand:
magick IXVJl.jpg -fuzz 30% -fill white -draw "color 0,0 floodfill" result.jpg

**Save Image with added mask in python**

I'm trying to save an image on which I added a white mask on all the interest areas. But for some reason, It doesn't save the final image and it's not returning any error message. How can I save my image with the mask?
import cv2
import numpy as np
image = cv2.imread('C:/Users/Desktop/testim.png')
gray_scale = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Set threshold level
Dark = 10
coords = np.column_stack(np.where(gray_scale < Dark))
print("xy:\n", coords)
mask = gray_scale < Dark
# Color the pixels in the mask
image[mask] = (255, 255, 255)
cv2.imshow('mask', image)
cv2.waitKey()
#save new image with the added mask to directory
if not cv2.imwrite(r'./mask.png', image):
raise Exception("Could not write image")
I think it relates to several typos in the program. After fixing them everything works quite nicely.
import cv2
import numpy as np
image = cv2.imread('/content/test.png')
gray_scale = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Set threshold level
Dark = 10
coords = np.column_stack(np.where(gray_scale < Dark))
# print("xy:\n", coords)
mask = gray_scale < Dark
# Color the pixels in the mask
image[mask] = (255, 255, 255)
# cv2.imshow('mask', image)
cv2.waitKey()
if not cv2.imwrite(r'./mask.png', image):
raise Exception("Could not write image")
image before manipulation
image after manipulation

How do I edit a masked image?

How do ı make the image I've masked smoother? I want to remove the little white pixels from the second picture, and I want to throw some of the masked places out of the picture, how can I do that?
import cv2
import numpy as np
img = cv2.imread("colors.jpg")
height,width = 720,720
img = cv2.resize(img,(width,height))
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
lower_range = np.array([100,50,50])
upper_range = np.array([150,255,255])
mask = cv2.inRange(hsv, lower_range, upper_range)
#res = cv2.bitwise_and(img,img, mask=mask)
cv2.imshow("Image",img)
cv2.imshow("Mask",mask)
#cv2.imshow("res",res)
cv2.waitKey(0)
cv2.destroyAllWindows
Try using the closing and opening concept of computer vision. You need is erosion in your case to get rid of the tiny white dots.
There are two things that can controlled for the resultant outcome as per your requirement i.e. kernel size and iterations.
Here is the code is I could reproduce and provide a near solution.
import numpy as np
img = cv2.imread("colors.jpg")
height,width = 720,720
img = cv2.resize(img,(width,height))
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
lower_range = np.array([100,50,50])
upper_range = np.array([150,255,255])
kernel = np.ones((5,5),np.uint8)
mask = cv2.inRange(hsv, lower_range, upper_range)
erosion = cv2.erode(mask,kernel,iterations = 1)
#res = cv2.bitwise_and(img,img, mask=mask)
cv2.imshow("Image",img)
cv2.imshow("Mask",erosion)
#cv2.imshow("res",res)
cv2.waitKey(0)
cv2.destroyAllWindows
import cv2
import numpy as np
​
img = cv2.imread("colors.jpg")
height,width = 720,720
img = cv2.resize(img,(width,height))
​
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
​
lower_range = np.array([100,50,50])
upper_range = np.array([150,255,255])
kernel = np.ones((5,5),np.uint8)
mask = cv2.inRange(hsv, lower_range, upper_range)
erosion = cv2.erode(mask,kernel,iterations = 1)
#res = cv2.bitwise_and(img,img, mask=mask)
​
​
cv2.imshow("Image",img)
cv2.imshow("Mask",erosion)
#cv2.imshow("res",res)
​
cv2.waitKey(0)
cv2.destroyAllWindows
Result:

How to blur red color in image with python opencv so that its not clearly visible?

I want to blur red color in image ("1.png" is attached) so that its not clearly visible. I tried below code where I can change red color to black color but how can I blur it? Please help.
import cv2
import numpy as np
frame = cv2.imread("1.png")
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower = np.array([0,50,50])
upper = np.array([10,255,255])
# define range of blue color in HSV
# lower = np.array([38, 86, 0])
# upper = np.array([121, 255, 255])
# define range of pink color in HSV
# http://www.workwithcolor.com/pink-color-hue-range-01.htm
# lower = np.array([158, 127, 0])
# upper = np.array([179, 255, 255])
# Threshold the HSV image to get only red colors
mask = cv2.inRange(hsv, lower, upper)
color_only = cv2.bitwise_and(frame, frame, mask = mask)
# convert mask to 3-channel image to perform subtract
mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
res = cv2.subtract(frame, mask) #negative values become 0 -> black
cv2.imshow("frame", frame)
# cv2.imshow("mask", mask)
# cv2.imshow("color_only", color_only)
cv2.imshow("res", res)
cv2.waitKey()
cv2.destroyAllWindows()
1.png
You need to blur the segmented image and then use alpha blending to composite the blurred ROI with the background image. This code takes you through all the steps:
Read image and segment the color of interest:
import cv2
import numpy as np
frame = cv2.imread("/home/stephen/Desktop/1.png")
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower = np.array([0,50,50])
upper = np.array([10,255,255])
mask = cv2.inRange(hsv, lower, upper)
color_only = cv2.bitwise_and(frame, frame, mask = mask)
### THE BACKGROUND MUST BE MADE WHITE, NOT BLACK ###
color_only[np.where((color_only==[0,0,0]).all(axis=2))] = [255,255,255]
cv2.imshow("color_only", color_only)
Next, blur the segmented image. Note, I am using a 13,13 kernel to blur the image:
blur = cv2.blur(color_only, (13,13))
cv2.imshow('blur', blur)
Next, blur the mask of the segmented image. We are going to use this to combine the images later. It's easy to combine images using a bitwise function, but that approach will not work here because the blurred image no longer occupies the same space as the segmented image:
maskForAlphaBlending = blur
## BLACK OUT THE WHITE BACKGROUND OF THE ALPHA MASK
maskForAlphaBlending[np.where((maskForAlphaBlending==[255,255,255]).all(axis=2))] = [0,0,0]
maskForAlphaBlending = cv2.cvtColor(maskForAlphaBlending, cv2.COLOR_BGR2GRAY)
cv2.imshow('maskForAlphaBlending', maskForAlphaBlending)
Finally alpha blending can be used to composite the blurred segmented image and the green and white background image:
foreground = blur
background = frame
alpha = cv2.cvtColor(maskForAlphaBlending, cv2.COLOR_GRAY2BGR)
foreground = foreground.astype(float)
background = background.astype(float)
alpha = alpha.astype(float)/100
foreground = cv2.multiply(alpha, foreground)
background = cv2.multiply(1.0 - alpha, background)
outImage = cv2.add(foreground, background)
cv2.imshow("outImg", outImage/255)
Note how the red lines are blurred, but the green and white border is not:

How can I select the right color to threshold an image with OpenCV?

I'm trying to select the green color in an image using OpenCV (the method to do it comes from this website. The image I'm treating is :
Here is the code I tried to write.
import cv2
import matplotlib.pyplot as plt
import numpy as np
greenhsv = (60, 255, 255)
green2hsv=(70,100,170)
g_square = np.full((10, 10, 3), greenhsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g_square))
plt.show()
g1_square = np.full((10, 10, 3), green2hsv, dtype=np.uint8)/255.0
plt.imshow(hsv_to_rgb(g1_square))
plt.show()
nucl = cv2.imread('./Pictures/image_nucleation_essai0.png')
nucl = cv2.cvtColor(nucl, cv2.COLOR_BGR2RGB)
plt.imshow(nucl)
plt.show()
hsv_nucl = cv2.cvtColor(nucl, cv2.COLOR_RGB2HSV)
mask = cv2.inRange(hsv_nucl, greenhsv,green2hsv)
result = cv2.bitwise_and(nucl, nucl, mask=mask)
plt.imshow(mask, cmap="gray")
plt.show()
plt.imshow(result)
plt.show()
The result is :
So the mask did not work.
Your color ranges are not quite right yet. Also the variables in the inRange() function are in the wrong order. It's from-to, so the darker color must be first. Change your code to cv2.inRange(hsv_nucl, green2hsv,greenhsv) You can use/tweak the values in the code below, that works.
Result:
With white background:
import numpy as np
import cv2
# load image
img = cv2.imread("Eding.png")
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# set lower and upper color limits
lower_val = np.array([50,100,170])
upper_val = np.array([70,255,255])
# Threshold the HSV image to get only green colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# apply mask to original image - this shows the green with black blackground
only_green = cv2.bitwise_and(img,img, mask= mask)
# create a black image with the dimensions of the input image
background = np.zeros(img.shape, img.dtype)
# invert to create a white image
background = cv2.bitwise_not(background)
# invert the mask that blocks everything except green -
# so now it only blocks the green area's
mask_inv = cv2.bitwise_not(mask)
# apply the inverted mask to the white image,
# so it now has black where the original image had green
masked_bg = cv2.bitwise_and(background,background, mask= mask_inv)
# add the 2 images together. It adds all the pixel values,
# so the result is white background and the the green from the first image
final = cv2.add(only_green, masked_bg)
#show image
cv2.imshow("img", final)
cv2.waitKey(0)
cv2.destroyAllWindows()

Categories

Resources