Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set background color on image to white with OpenCV in Python

Tags:

python

opencv

I have one picture read with OpenCV lib in Python. Im wondering, how can I change background color to white. I just want to have persons from the image and white background.

For example:

enter image description here

I want to change to this:

enter image description here

How can I do such thing:

import numpy as np
import cv2

my_image = r'C:\Users\Pc\Desktop\preklapanje4.jpg'
my_image = cv2.imread(my_image, 1)

cv2.imshow('img',my_image)
cv2.waitKey(0)
like image 516
taga Avatar asked Oct 19 '19 16:10

taga


People also ask

How do I change the background on my OpenCV?

Step 1: First of all, import the library OpenCV. Step 2: Now, import the image from your computer. Step 3: Then, read the image in OpenCV. Step 4: Then, convert the image background to gray image background.


1 Answers

In this image, you need to flood fill in several regions, since all the background green color is not connected.

import cv2
import numpy as np

# load image and get dimensions
img = cv2.imread("soccer.jpg")
h, w, c = img.shape

# create zeros mask 2 pixels larger in each dimension
mask = np.zeros([h + 2, w + 2], np.uint8)

# do floodfill
result = img.copy()
cv2.floodFill(result, mask, (0,0), (255,255,255), (3,151,65), (3,151,65), flags=8)
cv2.floodFill(result, mask, (38,313), (255,255,255), (3,151,65), (3,151,65), flags=8)
cv2.floodFill(result, mask, (363,345), (255,255,255), (3,151,65), (3,151,65), flags=8)
cv2.floodFill(result, mask, (619,342), (255,255,255), (3,151,65), (3,151,65), flags=8)

# write result to disk
cv2.imwrite("soccer_floodfill.jpg", result)

# display it
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Input:

enter image description here

Result:

enter image description here

Adjust the low and high color ranges as desired to get rid of more of the green.

See floodfill

ADDITION:

Here is the code for doing inRange thresholding in HSV as suggested from my comments. But note that global thresholding has affected some of the near-white in the shirt to make it pure white. Some of that could be removed by doing some large size morphology close or from filling the smaller contours with white.

import cv2
import numpy as np
import skimage.exposure

# load image and get dimensions
img = cv2.imread("soccer.jpg")

# convert to hsv
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

# threshold using inRange
range1 = (20,80,80)
range2 = (90,255,255)
mask = cv2.inRange(hsv,range1,range2)
mask = 255 - mask

# apply morphology opening to mask
kernel = np.ones((3,3), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

# antialias mask
mask = cv2.GaussianBlur(mask, (0,0), sigmaX=3, sigmaY=3, borderType = cv2.BORDER_DEFAULT)
mask = skimage.exposure.rescale_intensity(mask, in_range=(127.5,255), out_range=(0,255))

result = img.copy()
result[mask==0] = (255,255,255)

# write result to disk
cv2.imwrite("soccer_mask.png", mask)
cv2.imwrite("soccer_green2white.jpg", result)

# display it
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Mask:

enter image description here

Result:

enter image description here

ADDITION2:

Here is another way that I have found to be effective in doing green screen removal. Convert to LAB. Then negate the A and multiply it by B. Then do inRange() thresholding on that to create a mask. Then use the mask to make the green into white. It keeps the near-white in the shirts from being forced to pure white, better than the earlier method. But unfortunately leaves a bit of green behind.

import cv2
import numpy as np
import skimage.exposure

# load image and get dimensions
img = cv2.imread("soccer.jpg")

# convert to hsv
lab = cv2.cvtColor(img,cv2.COLOR_BGR2LAB)
L = lab[:,:,0]
A = lab[:,:,1]
B = lab[:,:,2]

# negate A
A = (255 - A)

# multiply negated A by B
nAB = 255 * (A/255) * (B/255)
nAB = np.clip((nAB), 0, 255)
nAB = np.uint8(nAB)


# threshold using inRange
range1 = 100
range2 = 160
mask = cv2.inRange(nAB,range1,range2)
mask = 255 - mask

# apply morphology opening to mask
kernel = np.ones((3,3), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

# antialias mask
mask = cv2.GaussianBlur(mask, (0,0), sigmaX=3, sigmaY=3, borderType = cv2.BORDER_DEFAULT)
mask = skimage.exposure.rescale_intensity(mask, in_range=(127.5,255), out_range=(0,255))

# put white where ever the mask is zero
result = img.copy()
result[mask==0] = (255,255,255)

# write result to disk
cv2.imwrite("soccer_green2white_inrange_lab.jpg", result)

# display it
cv2.imshow("nAB", nAB)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Result:

enter image description here

like image 139
fmw42 Avatar answered Oct 23 '22 20:10

fmw42