How to remove white background with some shadow in grabcut?

Viewed 734

I am using grabcut to remove the white background from the images. Some part of the background at the edges remain. Can you please help me to remove the shadow part as well ?

Input image

Input image

Output image

output image

Tried grabcut, contour, normal thresholding but shadow remains

img_ray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(img_ray, 127, 255, cv.THRESH_BINARY+cv.THRESH_OTSU)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
cnt = contours[4]
cv.drawContours(img, [cnt], 0, (0, 255, 0), 3)
mask = np.zeros(img.shape[:2], np.uint8)
bgdmodel = np.zeros((1, 65), np.float64)
fgdmodel = np.zeros((1, 65), np.float64)
height = img.shape[0]
width = img.shape[1]
rect = (50, 0, width, height)
cv.grabCut(img, mask, rect, bgdmodel, fgdmodel, 10, cv.GC_INIT_WITH_RECT)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img = img * mask2[:, :, np.newaxis]
img[np.where((img == [230, 230, 230]).all(axis=2))] = [0, 0, 0]
cv.bitwise_not(img)

it should remove complete background

1 Answers

This answer explains how to use Grabcut and a mask to extract the foreground. There are two steps in this answer. The first step is creating a mask that labels the pixels as sure foreground, sure background, or unknown. The second step is applying the Grabcut algorithm.

The mask is created using the Canny edge filter and two morphological transformations.

edges = cv.Canny(img, 80,150)
kernel = np.ones((5,5), np.uint8)
closing = cv.morphologyEx(edges, cv.MORPH_CLOSE, kernel, iterations=3)
erosion = cv.morphologyEx(closing, cv.MORPH_ERODE, kernel, iterations=1)

# When using Grabcut the mask image should be:
#    0 - sure background
#    1 - sure foreground
#    2 - unknown

mask = np.zeros(img.shape[:2], np.uint8)
mask[:] = 2
mask[erosion == 255] = 1

This mask will give the Grabcut algorithm hints about what is the sure foreground and what is the sure background. Next, apply Grabcut with the mask:

bgdmodel = np.zeros((1, 65), np.float64)
fgdmodel = np.zeros((1, 65), np.float64)

out_mask = mask.copy()
out_mask, _, _ = cv.grabCut(img,out_mask,None,bgdmodel,fgdmodel,1,cv.GC_INIT_WITH_MASK)
out_mask = np.where((out_mask==2)|(out_mask==0),0,1).astype('uint8')
out_img = img*out_mask[:,:,np.newaxis]

The mask is shown as mask*123 so that sure forground is gray, sure background is black, and unknown is white: output

Related