Inner and outer cell borders#

When studying tissues, organisms and organoids, often the position and orientation of the cell and its membranes within the tissue is relevant. For example, we differentiate apical (at an apex, at the end, outer) and basal (at the base, inner) sides of cells within tissue. Starting from a cell segmentation label image, we can identify pixels that sit outside or inside of a structure made of cells. In the following example we work with a synthetic two-dimensional image of some cells forming an organoid. The same functions will also work in 3D.

import numpy as np
import pyclesperanto_prototype as cle

First, we build our synthetic dataset. It is made of 6 cell-centers we dilated to form an organoid.

points = np.asarray([
    [50, 50],
    [60, 60],
    [25, 40],
    [70, 30],
    [35, 65],
    [50, 25]
]).T
image = np.zeros((100, 100))
spots = cle.pointlist_to_labelled_spots(points, image)
cells = cle.dilate_labels(spots, radius=15)
spots.shape
(100, 100)

These are our cells:

cells
cle._ image
shape(100, 100)
dtypeuint32
size39.1 kB
min0.0
max6.0

And that’s the organoid:

organoid = cells > 0
organoid
cle._ image
shape(100, 100)
dtypeuint8
size9.8 kB
min0.0
max1.0

We now identify the pixels that sit on the borders of the cells.

cell_borders = cle.reduce_labels_to_label_edges(cells)
cell_borders
cle._ image
shape(100, 100)
dtypeuint32
size39.1 kB
min0.0
max6.0

We can do exactly the same with the organoid to identify the pixels on its surface.

organoid_border = cle.reduce_labels_to_label_edges(organoid)
organoid_border
cle._ image
shape(100, 100)
dtypeuint32
size39.1 kB
min0.0
max1.0

By masking the cell borders with the organoid border - technically that’s a pixel-by-pixel multiplication - we can identify the outer borders.

outer_borders = cle.mask(cell_borders, organoid_border).astype(np.uint32)
outer_borders
cle._ image
shape(100, 100)
dtypeuint32
size39.1 kB
min0.0
max6.0

If we subtract the outer borders from all cell borders, we retrieve the inner borders

inner_borders = (cell_borders - outer_borders).astype(np.uint32)
inner_borders
cle._ image
shape(100, 100)
dtypeuint32
size39.1 kB
min0.0
max6.0

When post-processing these label images, be a bit careful, because these images may not be sequentially labeled. There are libraries and functions which may have issues with those kind of label images (e.g. cle.statistics_of_labelled_pixels()). You can print out which labels exist in a label image using np.unique() and you could make the label images sequential using cle.relabel_sequential().

np.unique(outer_borders)
array([0, 2, 3, 4, 5, 6], dtype=uint32)
np.unique(inner_borders)
array([0, 1, 2, 3, 4, 5, 6], dtype=uint32)