spacr.object_distances¶
Every distance worth measuring, within and between segmented objects.
What existed before this module: within ONE object type, centroid-to- centroid neighbour distances over a KD-tree; and between types, a single family measuring from a CHANNEL’S intensity centre of mass to the nearest nucleus or pathogen surface. So there was no centre-to-centre between types, no centre-to-perimeter in either direction, no perimeter-to- perimeter, nothing about local maxima, and nothing about where an object sits inside its parent.
A Euclidean distance transform is computed once for each object-type mask. At every pixel it records the distance to the nearest pixel belonging to that object type, with zero inside an object. Centre-to-surface, surface-to-surface, and local-maximum distances can therefore be obtained by sampling or reducing the precomputed field rather than evaluating every pair of objects. The principal operations are:
centre -> nearest surface dt_b[centroid_a]
surface -> surface min(dt_b[a_boundary])
local maximum -> surface dt_b[peak]
The transform costs O(image pixels) per object type.
WHAT “DISTANCE” MEANS HERE, because three different numbers get called it:
- centre_to_centre between the two centroids. Big for two large
objects that are touching.
- centre_to_surface from a’s centroid to the nearest point on ANY b.
Asymmetric: a’s centre to b’s edge is not b’s centre to a’s edge, so both are emitted.
- surface_to_surface closest point to closest point. ZERO when they
touch, and it is the number a biologist means by “how far apart are they”.
NO OBJECT-TYPE PREFIX ON THE COLUMNS. measure prefixes every measurement
family with the object it belongs to, so a column called
distance_to_own_boundary here reaches the database as
cell_distance_to_own_boundary. Naming it cell_... here produced
cell_cell_..., which is the shape of every doubled-prefix bug.
NEVER NaN WHERE A NUMBER IS MEANINGFUL. An object with no partner of the
other type is inf – genuinely infinitely far, which is a fact – and
NaN is reserved for “not measured”.
Functions¶
|
Distances from every |
|
How far each channel's intensity centre sits from the geometric one. |
|
Distance from every point INSIDE an object to that object's boundary. |
|
Coordinates of the intensity peaks inside one object. |
|
Where each object's intensity peaks are, and what they are near. |
|
Every distance this module measures, for one object type. |
|
Distance from every point to the nearest object surface in |
Module Contents¶
- spacr.object_distances.between_object_types(masks: Dict[str, numpy.ndarray], *, primary: str, spacing=None) pandas.DataFrame[source]¶
Distances from every
primaryobject to every other object type.- Parameters:
masks – object type -> label image, all the same shape.
primary – the type whose objects are the rows.
spacing – voxel size, so the numbers carry physical units.
- Returns:
one row per primary object, keyed on
label.
THREE NUMBERS PER PAIR OF TYPES, because they answer three different questions – see the module docstring. Plus where the object sits inside itself and how close it is to the edge of the field, which is what says an object is clipped.
- spacr.object_distances.intensity_centre_offset(mask, images, *, primary: str, channels: Sequence[int] = (), spacing=None) pandas.DataFrame[source]¶
How far each channel’s intensity centre sits from the geometric one.
- Parameters:
mask – label image whose instances define rows and geometric centroids.
images – aligned intensity field with channels on its final axis, or a single intensity plane.
primary – name of the object type represented by
mask.
POLARISATION IN ONE NUMBER. A uniformly stained object has an offset of about zero; one whose signal is all at one end does not, and no intensity summary says so.
- spacr.object_distances.interior_distance_transform(mask, spacing=None)[source]¶
Distance from every point INSIDE an object to that object’s boundary.
- Parameters:
mask – label or binary image containing the target objects.
The complement of
surface_distance_transform(): run on the mask itself, so it is 0 outside and peaks at each object’s deepest point. Read at a centroid it says how far the centre is from its own rim, which is what makes a relative radial position possible.
- spacr.object_distances.local_maxima(image, mask, label: int) numpy.ndarray[source]¶
Coordinates of the intensity peaks inside one object.
- Parameters:
image – intensity image aligned with
mask.mask – labelled object image that limits the peak search.
label – object label whose interior is searched.
- Returns:
an
(n, ndim)array, possibly empty.
- spacr.object_distances.maxima_distances(masks: Dict[str, numpy.ndarray], images, *, primary: str, channels: Sequence[int] = (), spacing=None) pandas.DataFrame[source]¶
Where each object’s intensity peaks are, and what they are near.
- Parameters:
masks – object-type label images sharing the field geometry.
images – the field as
(..., channel).primary – object type whose labelled instances define output rows.
channels – which channels to find maxima in. Empty means all.
- Returns:
one row per primary object, keyed on
label.
- spacr.object_distances.object_distances(masks: Dict[str, numpy.ndarray], images=None, *, primary: str, channels: Sequence[int] = (), spacing=None, maxima: bool = True) pandas.DataFrame[source]¶
Every distance this module measures, for one object type.
The one call the measure pipeline makes. Joined on
labelso it widens the object’s row like any other measurement family.- Parameters:
masks – object type -> label image.
images – the field, for the intensity-derived families. None skips them.
primary – object type whose instances define the returned rows.
maxima – whether to find local maxima. The most expensive part.
- spacr.object_distances.surface_distance_transform(mask, spacing=None)[source]¶
Distance from every point to the nearest object surface in
mask.- Parameters:
mask – label or binary image containing the target objects.
ZERO INSIDE AN OBJECT.
distance_transform_edtmeasures distance to the nearest ZERO, so it is run on the INVERTED mask: the result is 0 on any labelled pixel and grows outward. That is what makes a lookup at another object’s centroid mean “distance to the nearest surface of this type”, and what makes two touching objects come out at 0.