Search code examples
pythonimage-processingscikit-image

How do skimage.morphology.remove_small_holes and skimage.morphology.remove_small_objects differ?


Both of these methods are included in the Scikit-Image library for Python. I'm trying to extract certain objects from images and ran into these two methods in a senior dev's code, written for the same purpose.

I have read the documentation for both skimage.morphology.remove_small_holes and skimage.morphology.remove_small_objects. But I can't understand what difference these two methods pose when they are run on a ndarray containing an image.


Solution

  • One removes holes (value 0) within objects (any other single value), the other removes objects. Note that it acts on either binary images (ndarray of dtype bool) or segmentation masks (ndarray of dtype int, where each value represents one object). Hopefully this example clarifies their use:

    import numpy as np
    from skimage import morphology
    
    objects = np.array([
            [1, 1, 1, 0, 0],
            [1, 0, 1, 0, 0],
            [1, 1, 1, 0, 0],
            [0, 0, 0, 0, 2],
            ])
    

    You can see that this array has two objects, object "1" has 8 pixels, and a hole in it of size 1 pixel, while object "2" has only 1 pixel total. Now I do:

    print(morphology.remove_small_objects(objects, 2))
    

    This removes objects of size strictly less than 2 pixels, so "2" disappears:

    [[1 1 1 0 0]
     [1 0 1 0 0]
     [1 1 1 0 0]
     [0 0 0 0 0]]
    

    Removing holes is a little more complicated, because that function only works with boolean arrays, but the same principle applies. We are going to:

    • convert the object image to binary/boolean
    • remove the holes
    • use segmentation.watershed to get objects back — preserving their original IDs.
    from skimage import segmentation
    
    binary_objects = objects.astype(bool)
    binary_filled = morphology.remove_small_holes(binary_objects, 2)
    objects_filled = segmentation.watershed(
            binary_filled, objects, mask=binary_filled
            )
    print(objects_filled)
    

    This removes from any object holes of size strictly less than 2, so the hole in the object "1" is removed:

    [[1 1 1 0 0]
     [1 1 1 0 0]
     [1 1 1 0 0]
     [0 0 0 0 2]]