1

I am currently working on a project that is needed to cut background and after all smooth it's borders

What I have as a input:

Input:

Input

What I wnat to do :

Cut background:

Cut background

Smooth edges:

Smooth edges

The idea of code is that after background cut, edges are not perfect and it would be better if these edges have been smoothed

What I tried: I saves mask edges

img_path = 'able.png'
img_data = cv2.imread(img_path)
img_data = img_data > 128


img_data = np.asarray(img_data[:, :, 0], dtype=np.double)
gx, gy = np.gradient(img_data)


temp_edge = gy * gy + gx * gx


temp_edge



temp_edge[temp_edge != 0.0] = 255.0


temp_edge = np.asarray(temp_edge, dtype=np.uint8)

cv2.imwrite('mask_edge.png', temp_edge)

After all I get stuck

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
  • 1
    See https://stackoverflow.com/questions/58465783/how-to-set-background-color-on-image-to-white-with-opencv-in-python/58466505#58466505 – fmw42 Jul 05 '22 at 14:47

2 Answers2

3

You need to blur the alpha channel.

Your target picture shows no blur in the color information, only in transparency. Blurring the color data will not replicate this faithfully.

Your segmented image is 4-channel. It contains the transparency mask.

im = cv.imread("image.png", cv.IMREAD_UNCHANGED)
(height, width) = im.shape[:2]
color = im[:,:,0:3]
alpha_mask = im[:,:,3]

blurred_alpha = cv.GaussianBlur(alpha_mask, ksize=None, sigmaX=7)

blurred

Then compose:

color_f = np.float32(1/255) * color
factor = np.float32(1/255) * blurred_alpha
factor = factor[..., None] # add a dimension for broadcasting
composite = color_f * factor + 1.0 * (1-factor)
# suitable for imshow

Composited onto white background:

composite

That looks wrong because the color information in the transparent pixels can't be relied upon. It is invalid, black even, so you don't see any actual background shining through:

masked lena color channels

To fix that, I took the color channels from a complete (non-masked) Lena and downsampled it to fit:

big_lena = cv.imread(cv.samples.findFile("lena.jpg"))
color = cv.resize(big_lena, (width, height), interpolation=cv.INTER_LANCZOS4)

composite but better

You can adjust how close the blur comes to the edges of the mask by dilating or eroding the mask (before blurring it).

You don't need to compose onto (white) background at all. You can just take the color and alpha channel and save that again.

with_new_alpha = np.dstack([color, blurred_alpha])

Drag it, it's got actual alpha:

with new alpha

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
1

Approach:

Continuing from where you have stopped, load the cut background image.

  • Blur the image and store in a different variable
  • Convert to grayscale and find contour surrounding the region of interest
  • Create a mask and draw the contour with certain thickness
  • In a new variable keep the blurred pixel values in the contour while leaving the rest from the cut background image, with the help of where() function from Numpy

Code:

img = cv2.imread('cut_background.jpg', 1)

img_blur = cv2.GaussianBlur(img, (7, 7), 0)

enter image description here

gray =  cv2.cvtColor(img , cv2.COLOR_BGR2GRAY)
th = cv2.threshold(gray,254,255,cv2.THRESH_BINARY_INV)[1]
contours, hierarchy = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

mask = np.zeros(img.shape, np.uint8)
mask = cv2.drawContours(mask, contours, -1, (255,255,255),7)

enter image description here

result = np.where(mask==(255, 255, 255), img_blur, img )

enter image description here

You can try varying the kernel size used by the Gaussian filter and the thickness of the contour being drawn.

Update:

To save the final image with a transparent background:

# apply Gaussian blur to th with same kernel size as before
b1 = cv2.GaussianBlur(th, (7, 7), 0)

# merge with existing result
result_trans = cv2.merge((result[:,:,0],result[:,:,1],result[:,:,2], b1))

# or simply use dstack from Numpy
result_trans = np.dstack((result,b1))

# remember to save this result as PNG file to preserve the transparent/alpha channel
cv2.imwrite(r'lena_processed.png', result_trans)
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87