Skip to content

Segmenting data

The qim3d library provides a set of methods for data segmentation.

qim3d.segmentation.watershed

watershed(bin_vol, min_distance=5)

Apply watershed segmentation to a binary volume.

Parameters:

Name Type Description Default
bin_vol ndarray

Binary volume to segment. The input should be a 3D binary image where non-zero elements represent the objects to be segmented.

required
min_distance int

Minimum number of pixels separating peaks in the distance transform. Peaks that are too close will be merged, affecting the number of segmented objects. Default is 5.

5

Returns:

Name Type Description
labeled_vol ndarray

A 3D array of the same shape as the input bin_vol, where each segmented object is assigned a unique integer label.

num_labels int

The total number of unique objects found in the labeled volume.

Example

import qim3d

vol = qim3d.examples.cement_128x128x128
bin_vol = qim3d.filters.gaussian(vol, sigma = 2)<60

fig1 = qim3d.viz.slices_grid(bin_vol, slice_axis=1, display_figure=True)
operations-watershed_before

labeled_volume, num_labels = qim3d.segmentation.watershed(bin_vol)

cmap = qim3d.viz.colormaps.segmentation(num_labels)
fig2 = qim3d.viz.slices_grid(labeled_volume, slice_axis=1, color_map=cmap, display_figure=True)
operations-watershed_after

Source code in qim3d/segmentation/_common_segmentation_methods.py
def watershed(bin_vol: np.ndarray, min_distance: int = 5) -> tuple[np.ndarray, int]:
    """
    Apply watershed segmentation to a binary volume.

    Args:
        bin_vol (np.ndarray): Binary volume to segment. The input should be a 3D binary image where non-zero elements
                              represent the objects to be segmented.
        min_distance (int): Minimum number of pixels separating peaks in the distance transform. Peaks that are
                            too close will be merged, affecting the number of segmented objects. Default is 5.

    Returns:
        labeled_vol (np.ndarray): A 3D array of the same shape as the input `bin_vol`, where each segmented object is assigned a unique integer label.
        num_labels (int): The total number of unique objects found in the labeled volume.

    Example:
        ```python
        import qim3d

        vol = qim3d.examples.cement_128x128x128
        bin_vol = qim3d.filters.gaussian(vol, sigma = 2)<60

        fig1 = qim3d.viz.slices_grid(bin_vol, slice_axis=1, display_figure=True)
        ```
        ![operations-watershed_before](../../assets/screenshots/operations-watershed_before.png)

        ```python
        labeled_volume, num_labels = qim3d.segmentation.watershed(bin_vol)

        cmap = qim3d.viz.colormaps.segmentation(num_labels)
        fig2 = qim3d.viz.slices_grid(labeled_volume, slice_axis=1, color_map=cmap, display_figure=True)
        ```
        ![operations-watershed_after](../../assets/screenshots/operations-watershed_after.png)

    """
    import scipy
    import skimage

    if len(np.unique(bin_vol)) > 2:
        raise ValueError(
            'bin_vol has to be binary volume - it must contain max 2 unique values.'
        )

    # Compute distance transform of binary volume
    distance = scipy.ndimage.distance_transform_edt(bin_vol)

    # Find peak coordinates in distance transform
    coords = skimage.feature.peak_local_max(
        distance, min_distance=min_distance, labels=bin_vol
    )

    # Create a mask with peak coordinates
    mask = np.zeros(distance.shape, dtype=bool)
    mask[tuple(coords.T)] = True

    # Label peaks
    markers, _ = scipy.ndimage.label(mask)

    # Apply watershed segmentation
    labeled_volume = skimage.segmentation.watershed(
        -distance, markers=markers, mask=bin_vol
    )

    # Extract number of objects found
    num_labels = len(np.unique(labeled_volume)) - 1
    log.info(f'Total number of objects found: {num_labels}')

    return labeled_volume, num_labels

qim3d.segmentation.connected_components

connected_components(volume, connectivity=1)

Computes connected components of a binary volume.

Parameters:

Name Type Description Default
volume ndarray

An array-like object to be labeled. Any non-zero values in input are counted as features and zero values are considered the background.

required
connectivity int

Controls the squared distance of connectivity. Can range from 1 to 3.

1

Returns:

Name Type Description
cc ConnectedComponents

A ConnectedComponents object containing the labeled volume and a number of useful methods and attributes.

Example
import qim3d

vol = qim3d.examples.cement_128x128x128
binary = qim3d.filters.gaussian(vol, sigma=2) < 60
cc = qim3d.segmentation.connected_components(binary)
color_map = qim3d.viz.colormaps.segmentation(len(cc), style='bright')
qim3d.viz.slicer(cc.labels, slice_axis=1, color_map=color_map)
Show the largest connected components
import qim3d

vol = qim3d.examples.cement_128x128x128
binary = qim3d.filters.gaussian(vol, sigma=2) < 60
cc = qim3d.segmentation.connected_components(binary)
filtered = cc.filter_by_largest(5)

color_map = qim3d.viz.colormaps.segmentation(len(cc), style='bright')
qim3d.viz.volumetric(filtered, color_map=color_map, constant_opacity=True)
Filter the connected components by size
import qim3d

vol = qim3d.examples.cement_128x128x128
binary = qim3d.filters.gaussian(vol, sigma=2) < 60
cc = qim3d.segmentation.connected_components(binary)

# Show a histogram of the distribution of label sizes
cc.sizes_histogram()

# Based on the histogram, choose a range of sizes
filtered = cc.filter_by_size(min_size=1e2, max_size=2e2)

color_map = qim3d.viz.colormaps.segmentation(len(cc), style='bright')
qim3d.viz.volumetric(filtered, color_map=color_map, constant_opacity=True)
Source code in qim3d/segmentation/_connected_components.py
def connected_components(volume: np.ndarray, connectivity: int = 1) -> ConnectedComponents:
    """
    Computes connected components of a binary volume.

    Args:
        volume (np.ndarray): An array-like object to be labeled. Any non-zero values in `input` are
            counted as features and zero values are considered the background.
        connectivity (int, optional): Controls the squared distance of connectivity. Can range from 1 to 3.

    Returns:
        cc: A ConnectedComponents object containing the labeled volume and a number of useful methods and attributes.

    Example:
        ```python
        import qim3d

        vol = qim3d.examples.cement_128x128x128
        binary = qim3d.filters.gaussian(vol, sigma=2) < 60
        cc = qim3d.segmentation.connected_components(binary)
        color_map = qim3d.viz.colormaps.segmentation(len(cc), style='bright')
        qim3d.viz.slicer(cc.labels, slice_axis=1, color_map=color_map)
        ```

    Example: Show the largest connected components
        ```python
        import qim3d

        vol = qim3d.examples.cement_128x128x128
        binary = qim3d.filters.gaussian(vol, sigma=2) < 60
        cc = qim3d.segmentation.connected_components(binary)
        filtered = cc.filter_by_largest(5)

        color_map = qim3d.viz.colormaps.segmentation(len(cc), style='bright')
        qim3d.viz.volumetric(filtered, color_map=color_map, constant_opacity=True)
        ```

    Example: Filter the connected components by size
        ```python
        import qim3d

        vol = qim3d.examples.cement_128x128x128
        binary = qim3d.filters.gaussian(vol, sigma=2) < 60
        cc = qim3d.segmentation.connected_components(binary)

        # Show a histogram of the distribution of label sizes
        cc.sizes_histogram()

        # Based on the histogram, choose a range of sizes
        filtered = cc.filter_by_size(min_size=1e2, max_size=2e2)

        color_map = qim3d.viz.colormaps.segmentation(len(cc), style='bright')
        qim3d.viz.volumetric(filtered, color_map=color_map, constant_opacity=True)
        ```

    """
    cc = ConnectedComponents(volume, connectivity)
    return cc