Arrays of individual connected components

Using connected components, I am iterating through a 3D image with several blobs. I need the array/mesh of each components, how can I do this without writing too much to disk? I can get individual stats, I need the individual arrays.

Further clarification: I need to extract one label from a labelmap. sitk.ExtractLabel seems a retired function, looking for the replacement…

1 Like

I am not sure what you mean by “extract one label”. Please provide a description of the input, and the expected output. Do you want a 3D image? an isolated mask? location values? intensity values?

Hi Bradley,

I have a 3D image of a distribution of spherical bacteria and I am measuring the surface-to-surface distance to the cell membrane. I can use connectedcomponents to iterate through the stats of the bacteria, but I would like to get the contours of each bacteria to measure to the membrane.

I thought instead of a connectedcomponents/Binary image approach, maybe using BinaryImageToLabelMap could label each bacteria in a labelmap, then I only need to extract each label at a time. Do-able?

Perhaps this method may help you:

LabelShapeStatisticsImageFilter::GetIndexes This will return a flattened list of indexes that can be used to access the values of an intensity image of interest.

Is that the type of access you are looking for?

Almost there! I can get the indexes out but having problems converting to point coordinates. Off a previous post Extract list of non zero pixels of a binary image I would like to use sitk.TransformIndexToPhysicalPoint but that seems defunct now. Is there a newer function to get the indexes to physical coordinates?

Hello @Sean_Hatton,

You are probably thinking of the image’s TransformIndexToPhysicalPoint method.

Hi Zivy,

Thanks for your suggestion. I think this is getting the list of surface points per label that I wanted, still testing:

def index2points(img, index):
df = []
for j in index:
physical_point = img.TransformContinuousIndexToPhysicalPoint((j))
df.append(physical_point)
return df

stats = sitk.LabelShapeStatisticsImageFilter()
stats.Execute(seg)
for l in stats.GetLabels():
print(“Label {0} Centroid: {1} Size: {2}”.format(l, stats.GetCentroid(l), round(stats.GetPhysicalSize(l))))
index = stats.GetIndexes(l)
tuples = [((i), (i + 1) % len(index), (i + 2) % len(index))
for i in range(len(index))]
points = index2points(seg,tuples)

I will post how successful this is…

Hi all,

I ran the TransformContinuousIndexToPhysicalPoint but each object returns the surfacepoints for the entire region of interest (i.e. the same cube of the perimeter of the total image), not the surface points of the current object (i.e. one of a variety of small spheres).

Is there a way of running a connected components on the array?

seg_file = (f’{wkdir}/example5.seg.nrrd’)
seg = sitk.ReadImage(seg_file, imageIO=‘NrrdImageIO’)
seg_array = sitk.GetArrayViewFromImage(seg)
seg_cc = sitk.ConnectedComponent(seg_array)
for id, array in enumerate(seg_cc):
print(id)

Hello @Sean_Hatton,

Not exactly clear what it is you want to do. Hopefully the following code snippet addresses your needs. If not it provides us with a concrete example so that we can figure out how to help you.

import SimpleITK as sitk

# Create artifical dataset 2D or 3D, set dim to desired dimensionality
# so either contours or surfaces
dim = 2
binary_image = sitk.Image([256]*dim, sitk.sitkUInt8)
binary_image[[slice(10,20)]*dim] = 1
binary_image[[slice(100,150)]*dim] = 1
binary_image[[slice(200,225)]*dim] = 1

# Extract contours/surfaces from binary blobs
fully_connected = True
background = 0
foreground = 1
binary_contour_image = sitk.BinaryContour(binary_image,
                                          fully_connected,
                                          background,
                                          foreground)
# Label the contours/surfaces
contour_cc = sitk.ConnectedComponent(binary_contour_image, fully_connected)

# Get the physical coordinates of each contour/surface
label_shape_stats = sitk.LabelShapeStatisticsImageFilter()
label_shape_stats.Execute(contour_cc)
label_contour_physical_points = []
for contour_label in label_shape_stats.GetLabels():
    # indexes returned as a flat list
    contour_indexes = label_shape_stats.GetIndexes(contour_label)
    label_contour_physical_points.append([contour_cc.TransformIndexToPhysicalPoint(contour_indexes[i:i+dim]) for i in range(0,len(contour_indexes),dim)])

# Do something with the per contour/surface point clouds

p.s. Please use three backticks to quote code, makes it more readable.

1 Like

For the variables, does i=contour_label and dim=3? (3D objects)

Hello @Sean_Hatton,

Setting dim=3 in the code above creates a 3D image with blobs. The call to BinaryContour in 3D is extracting surfaces, so if you look at slices, sitk.Show(binary_contour_image), you will see that some slices have contours and some filled in regions which are the top and bottom of the blob in the z axis.

The index i is just iterating over the contour/surface points for each label. Because we received a flat list per label we have [x_1, y_1, x_2, y_2 \ldots] in 2D and [x_1, y_1, z_1, x_2, y_2, z_2 \ldots] in 3D. The code works for both, stepping along the list in increments of dim.

Excellent! I have the multiple physical points lists for each bacteria. Now I am trying to do the same with the membrane (i.e. 1 target). I get many contour indices for the membrane, but when I process the TransformIndexToPhysicalPoint it gives me 1000s of entries with the same physical coordinates. There is only 1 index/membrane to process, but not sure why the TransformIndexToPhysicalPoints are the same.

wkdir = os.getcwd()
dim=3
fully_connected = True
background = 0
foreground = 1

# Load images
mem_binary = sitk.ReadImage(f'{wkdir}/membrane.seg.nrrd', imageIO='NrrdImageIO')
mem_contour_image = sitk.BinaryContour(mem_binary,fully_connected,
                                          background,foreground)
mem_contour_cc = sitk.ConnectedComponent(mem_contour_image, fully_connected)

# Get the physical coordinates of the membrane contour/surface
mem_label_shape_stats = sitk.LabelShapeStatisticsImageFilter()
mem_label_shape_stats.Execute(mem_contour_cc)

for contour_label in mem_label_shape_stats.GetLabels():
    mem_contour_indexes = mem_label_shape_stats.GetIndexes(contour_label)
    mem_contour_physical_points = pd.DataFrame(
        [mem_contour_cc.TransformIndexToPhysicalPoint(mem_contour_indexes[contour_label:contour_label+dim]) 
        for contour_index in range(0,len(mem_contour_indexes),dim)])

Hello @Sean_Hatton

It’s just a small bug in your code, using contour_label instead of contour_index:

mem_contour_indexes[contour_label:contour_label+dim]

should be

mem_contour_indexes[contour_index:contour_index+dim]
1 Like