How to remove small connected objects using OpenCV

Question:

I use OpenCV and Python and I want to remove the small connected object from my image.

I have the following binary image as input:

Input binary image

The image is the result of this code:

dilation = cv2.dilate(dst,kernel,iterations = 2)
erosion = cv2.erode(dilation,kernel,iterations = 3)

I want to remove the objects highlighted in red:

enter image description here

How can I achieve this using OpenCV?

Asked By: Zahra

||

Answers:

In order to remove objects automatically you need to locate them in the image.
From the image you provided I see nothing that distinguishes the 7 highlighted items from others.
You have to tell your computer how to recognize objects you don’t want. If they look the same, this is not possible.

If you have multiple images where the objects always look like that you could use template matching techniques.

Also the closing operation doesn’t make much sense to me.

Answered By: Piglet

How about with connectedComponentsWithStats (doc):

# find all of the connected components (white blobs in your image).
# im_with_separated_blobs is an image where each detected blob has a different pixel value ranging from 1 to nb_blobs - 1.
nb_blobs, im_with_separated_blobs, stats, _ = cv2.connectedComponentsWithStats(im)
# stats (and the silenced output centroids) gives some information about the blobs. See the docs for more information. 
# here, we're interested only in the size of the blobs, contained in the last column of stats.
sizes = stats[:, -1]
# the following lines result in taking out the background which is also considered a component, which I find for most applications to not be the expected output.
# you may also keep the results as they are by commenting out the following lines. You'll have to update the ranges in the for loop below. 
sizes = sizes[1:]
nb_blobs -= 1

# minimum size of particles we want to keep (number of pixels).
# here, it's a fixed value, but you can set it as you want, eg the mean of the sizes or whatever.
min_size = 150  

# output image with only the kept components
im_result = np.zeros_like(im_with_separated_blobs)
# for every component in the image, keep it only if it's above min_size
for blob in range(nb_blobs):
    if sizes[blob] >= min_size:
        # see description of im_with_separated_blobs above
        im_result[im_with_separated_blobs == blob + 1] = 255

Output : enter image description here

Answered By: Soltius

#For isolated or unconnected blobs: Try this (you can set noise_removal_threshold to whatever you like and make it relative to the largest contour for example or a nominal value like 100 or 25).

    mask = np.zeros_like(img)
    for contour in contours:
      area = cv2.contourArea(contour)
      if area > noise_removal_threshold:
        cv2.fillPoly(mask, [contour], 255)
Answered By: Neeraj Madan

Removing small connected components by area is called area opening. OpenCV does not have this as a function, it can be implemented as shown in other answers. But most other image processing packages will have an area opening function.

For example using scikit-image:

import skimage
import imageio.v3 as iio

img = iio.imread('cQMZm.png')[:,:,0]

out = skimage.morphology.area_opening(img, area_threshold=150, connectivity=2)

For example using DIPlib:

import diplib as dip

out = dip.AreaOpening(img, filterSize=150, connectivity=2)

PS: The DIPlib implementation is noticeably faster. Disclaimer: I’m an author of DIPlib.

Answered By: Cris Luengo
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.