Home > Enterprise >  Masked out large irregular shape from image with Python
Masked out large irregular shape from image with Python

Time:05-25

The objective is to remove large irregular area and maintained only character in the image.

For example, given the following

enter image description here

and the expected masked output

enter image description here

I have the impression this can be achieved as below

import cv2
import numpy as np
from matplotlib import pyplot as plt

dpath='remove_bg1.jpg'

img = cv2.imread(dpath)

img_fh=img.copy()
cv2.bitwise_not(img_fh,img_fh)
ksize=10
kernel = np.ones((ksize,ksize),np.uint8)
erosion = cv2.erode(img_fh,kernel,iterations = 3)
invertx = cv2.bitwise_not(erosion)
masked = cv2.bitwise_not(cv2.bitwise_and(img_fh,invertx))

all_image=[img,invertx,masked]
ncol=len(all_image)
for idx, i in enumerate(all_image):
    plt.subplot(int(f'1{ncol}{idx 1}')),plt.imshow(i)

plt.show()

which produce

enter image description here

Clearly, the code above did not produced the expected result.

May I know how to address this issue properly?

CodePudding user response:

To remove the unwanted blob, we must create a mask such that it encloses it completely.

Flow:

  1. Inversely binarize the image (such that you have a white foreground against dark background)
  2. Dilate the image (since the blob makes contact with letter 'A', it has to be isolated )
  3. Find contour with the largest area
  4. Draw the contour on an another 1-channel image and thicken it (dilation)
  5. Pixel Assignment: Pixels containing the dilated blob are made white on the original image

Code:

im = cv2.imread('stained_text.jpg')
im2 = im.copy()
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

# inverse binaraization
th = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV   cv2.THRESH_OTSU)[1]

enter image description here

Notice the blob region touching the letter 'A'. Hence to isolate it we perform erosion using an elliptical kernel

# erosion
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
erode = cv2.erode(th, kernel, iterations=2)

enter image description here

# find contours
contours, hierarchy = cv2.findContours(erode, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# Contour of maximum area
c = max(contours, key = cv2.contourArea)

# create 1-channel image in black 
black = np.zeros((im.shape[0], im.shape[1]), np.uint8)
# draw the contour on it
black = cv2.drawContours(black, [c], 0, 255, -1)
# perform dilation to have clean border
# we are using the same kernel
dilate = cv2.dilate(black, kernel, iterations = 3)

enter image description here

# assign the dilated area in white over the original image
im2[dilate == 255] = (255,255,255)

enter image description here

This was just one of the many possible ways on how to proceed. The key thing to note is how to isolate the blob.

  • Related