pythonopencvedge-detectioncanny-operator

Is there a way to eliminate the disconnected elements of an image?


I'm working on surface roughness of an image, and I used the edge detection function from opencv, but it also detected the pores from the image. Is there a way to get rid of the pores?

picture of original image and the edges detected

I tried adjusting the threshold values but that only got rid of the edges on the side and made the pores more pronounced. I´m pretty new to opencv and this type of problem, so most of this code is just based on examples I saw online.

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
from scipy import ndimage

# need to add the scale to it
img = cv.imread('testim5.png', cv.IMREAD_GRAYSCALE)
blurred = ndimage.filters.gaussian_filter(img, 2, mode='nearest')
edges = cv.Canny(blurred,100,200) # adjusting thresholds will only eliminate the outside edges, not the pores 

[testim5.png] https://i.sstatic.net/Vs6K8.png


Solution

  • First, you could use morphological operations to ensure that the interesting edges are connected. You can read more about morphological operations in the web of OpenCV: https://docs.opencv.org/4.x/d9/d61/tutorial_py_morphological_ops.html.

    The code would be:

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    from scipy import ndimage
    
    # Open the image
    img = cv.imread('testim5.png', cv.IMREAD_GRAYSCALE)
    
    # Note that `scipy.ndimage.filters.gaussian_filter` is deprecated, use `scipy.ndimage.gaussian_filter` instead
    blurred = ndimage.gaussian_filter(img, 2, mode='nearest')
    edges = cv.Canny(blurred,100,175)
    
    # Apply morphological operation (closing) to the EDGES so they are connected
    kernel = np.ones((5,3),np.uint8)
    edges_closing = cv.morphologyEx(edges, cv.MORPH_CLOSE, kernel)
    

    The result of this is that the vertical edges aren't so disjoint, as you can see in the following image.

    Figure 1

    The second step is to remove the pores. It can be seen as a problem of removing small connected objects and the solution of @Soltius in How to remove small connected objects using OpenCV works well.

    The code in your case would be:

    # Remove smallest connected components
    # To understand the following lines, look the answer of @Soltius in https://stackoverflow.com/questions/42798659/how-to-remove-small-connected-objects-using-opencv
    nb_blobs, im_with_separated_blobs, stats, _ = cv.connectedComponentsWithStats(edges_closing)
    sizes = stats[:, -1]
    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.
    min_size = 80  
    
    final_edges = np.zeros_like(im_with_separated_blobs)
    for blob in range(nb_blobs):
        if sizes[blob] >= min_size:
            final_edges[im_with_separated_blobs == blob + 1] = 255
    
    fig, axs = plt.subplots(ncols=3)
    axs[0].imshow(img, cmap='gray')
    axs[1].imshow(edges, cmap='gray')
    axs[2].imshow(final_edges, cmap='gray')
    axs[0].set_title('Original image')
    axs[1].set_title('Original edges')
    axs[2].set_title('New edges')
    fig.tight_layout()
    plt.show()
    

    The final edges are:

    Figure 2