Search code examples
pythonvectorizationimage-segmentationscikit-imagefeature-extraction

skimage segmetation extracting labels filtered by properties


I would like to extract skimage identified "labels" or segments which meet thresholds of parameters. My binary image was successfully split into segments, which skimage seems to call "labels", as the following:

labels = measure.label(classified, connectivity = image.ndim)

#symoblize each label with a different colour and plot it over the original image
image_label_overlay = label2rgb(labels, image=image)
plt.imshow(image_label_overlay)

enter image description here

As you can see, each labels are individually coloured.

My question is: How can I select these labeled segments base on their properties, and vectorize them in some way (convert to geopandas dataframe entries or something). My attempt to plot filtered labels is as follows, but was unsuccessful:

props = measure.regionprops_table(labels, image,
                                      properties = ['label',
                                                   'area',
                                                   'eccentricity',
                                                   'perimeter',
                                                   'equivalent_diameter',
                                                   'mean_intensity',
                                                   'solidity'])

#create a dataframe from these object properties
segs = pd.DataFrame(props)

#extract segments of low eccentricity (0 would be a perfect circle)
segs_filtered =segs[segs['eccentricity'] < 0.1]

#create list of label indexed meeting criteria
filtered_labels = segs_filtered['label']

I think filtered labels is actually indexing the pixels that belong to the labels I want to keep instead of the labels as a whole. That is why the following plotting method doesn't work.

fig = px.imshow(labels, binary_string=True)
fig.update_traces(hoverinfo='skip') # hover is only for label info

# For each label, add a filled scatter trace for its contour,
# and display the properties of the label in the hover of this trace.
for index in filtered_labels:
    label_i = props[index].label
    contour = measure.find_contours(labels == label_i, 0.5)[0]
    y, x = contour.T
    hoverinfo = ''
    for prop_name in properties:
        hoverinfo += f'<b>{prop_name}: {getattr(props[index], prop_name):.2f}</b><br>'
    fig.add_trace(go.Scatter(
        x=x, y=y, name=label_i,
        mode='lines', fill='toself', showlegend=False,
        hovertemplate=hoverinfo, hoveron='points+fills'))

plotly.io.show(fig)

label_i = props[index].label produces a Key error: 1

Please let me know if there is a way of filtering and producing vector objects from these labels.

For reproducibilityenter link description here, here is the classified tiff "classified" that is input:

Also I am sorry if this is unclear. I don't really understand how the labelling and properties work in skimage.

Thanks very much for reading!


Solution

  • Ok so the issue is that props is the output from regionprops_table, which is a dictionary mapping column names to columns of values. (You can see this by looking at the API documentation here.) Since you don't have a column called 1, (the columns are 'label', 'eccentricity', etc.) you get the KeyError.

    It looks to me like you've mixed examples of regionprops, which returns a list of regions, and regionprops_table, which returns a dictionary of columns: props[index].label is what you would do if you were iterating over the list of regions, as is getattr(props[index], prop_name).

    If you just want to plot, then probably you should just use regionprops, and bring the filtering into the for-loop:

    prop_names = [
            'label','area','eccentricity','perimeter',
            'equivalent_diameter','mean_intensity','solidity',
            ])
    props = measure.regionprops(labels, image)
    
    fig = px.imshow(labels, binary_string=True)
    fig.update_traces(hoverinfo='skip') # hover is only for label info
    
    for prop in props:
        if prop.eccentricity < 0.1:  # or whatever filtering you want to do
            label_i = prop.label
            # find the bbox in the props image and add offset, much faster than
            # using full image
            contour = measure.find_contours(prop.image, 0.5)[0] + props.bbox[:2]
            y, x = contour.T
            hoverinfo = ''
            for prop_name in prop_names:
                hoverinfo += f'<b>{prop_name}: {getattr(prop, prop_name):.2f}</b><br>'
            fig.add_trace(go.Scatter(
                    x=x, y=y, name=label_i,
                    mode='lines', fill='toself', showlegend=False,
                    hovertemplate=hoverinfo, hoveron='points+fills'
                    ))
    plotly.io.show(fig)