7

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)
taga
  • 3,537
  • 13
  • 53
  • 119

2 Answers2

18

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

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • And is it possible to remove all of 'green' color? – taga Oct 19 '19 at 18:01
  • Probably not with flood fill without changing the white part of the shirt. But you can try changing the values for the range of green colors. A better way might be using floodfill or inRange() thresholding on green hues in HSV colorspace to make a mask and then using the mask to change the the colors in the green of the original image. But there may still be and edge of green around the shirts that may need some antialiasing to remove. – fmw42 Oct 19 '19 at 18:08
  • And can you tell me why do you use number 2 in `[h + 2, w + 2]` and how you came up the numbers in floodfill? – taga Oct 19 '19 at 18:09
  • 1
    The mask has to be 2 pixels larger. See the documentation. I got the colors by measuring them interactively in some tool similar to GIMP or Photoshop in the dark green areas. – fmw42 Oct 19 '19 at 18:11
  • Possibly. In Python/OpenCV, convert to HSV. Get the HSV values at the top left corner. Do inRange() thresholding on the HSV image to make a mask. Use the mask to change the original image color to white where the mask is white. That will only work, if there is no green other than in the background. Or see https://www.remove.bg for a fully automated online way. – fmw42 Oct 19 '19 at 18:19
  • `@taga` See my ADDITION for inRange() thresholding in HSV. – fmw42 Oct 19 '19 at 23:53
  • @taga See my ADDITION for inRange() thresholding in LAB – fmw42 Oct 20 '19 at 00:23
0

How you have chosen seed points ?Is there any calculation or you have to trial and error to find the seed point? Then these seedpoints just work for this particular image. It is not comprehensive.

MJELVEH
  • 83
  • 5