Search code examples
pythonimage-processingscikit-image

Extracting feature perimeter coordinates from scikit-image


I am looking to extract the coordinates of a features perimeter. My intension is to use these coordinates to find the nearest edge-to-edge Euclidean distance for all features of interest from a .tif image. I am relatively new to scikit-image and am wondering if this can be done using options available from https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.regionprops ?

I am able to extract the filled area binary boolean matrix from skimage.measure.regionprops.

 [[False False  True  True  True False False False False False]
 [False False  True  True  True  True  True  True False False]
 [False False  True  True  True  True  True  True False False]
 [False False  True  True  True  True  True  True  True False]
 [False False False  True  True  True  True  True  True  True]
 [False  True  True  True  True  True  True  True  True  True]
 [False  True  True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True  True  True]
 [False  True  True  True  True  True  True  True  True  True]
 [False  True  True  True  True  True  True  True  True  True]
 [False  True  True  True  True  True  True  True  True False]
 [False  True  True  True  True  True  True  True False False]
 [ True  True  True  True  True  True  True False False False]
 [ True  True  True  True  True  True False False False False]
 [False False  True  True  True False False False False False]]

Binary Heat Map of Feature

These features are identified based on grayscale and morphological properties using a binary logistic regression classifier after some smoothing, dilation, and erosion techniques.

My attempt to get the perimeter coordinates:

for m in matrix:
    y, x = m.shape
    l = []
    for i in np.arange(x):
        r = np.argwhere(m[:, i][m[:, i] == 0]).ravel().reshape(-1, 1)
        t = np.full(shape=len(r), fill_value=i, dtype=int).reshape(-1, 1)
        if i==0 or i==x:
            coords = np.hstack((r, t))
            l.append(coords)
        else:
            if len(r) <= 1:
                coords = np.hstack((r, t))
                l.append(coords)
            else:
                r = r[[0, -1]] # Problem, may not capture all coordinates.
                # if the feature has concave regions.
                t = np.full(shape=len(r), fill_value=i, dtype=int).reshape(-1,1)
                coords = np.hstack((r, t))
                l.append(coords)

I think this works if there are no concave regions, unfortunately some of the features I am looking at definitely are crescent shaped. I suppose I could run the function second time across the other axis and identify the unique coordinates? I would imagine there is a better approach.

Also, eventually I need to map these coordinates back on to the original images coordinates. I figure I can use the bounding box corners to do that coordinate transfer. It be nice if I can get the coordinates ordered so I can plot them as a polygon around the feature, but this is not necessary to do the minimum edge-to-edge distance calculation.

Would this solution be something I can add as a udf to scikit-image.measure.regionsprops 'extra_properties' parameter?


Solution

  • With Bilal's response I was able to come up with a suitable solution.

    perimeter = binary_image ^ binary_erosion(binary_image)
    for i in [0, -1]:
       vedges = np.argwhere(m[:, i] == True).ravel()
       if len(vedges) == 0: continue
       for x in vedges:
           perimeter[x, i] = True
       hedges = np.argwhere(m[i, :] == True).ravel()
       if len(hedges) == 0: continue
       for y in hedges:
           perimeter[i, y] = True
    
    coordinates = np.argwhere(perimeter == True)