Neighborhood definitions#

In pyclesperanto, neighbor adjacency graphs are used to investigate relationships between neighboring labeled objects, such as cells. This notebook demonstrates the considered neighborhood definitions.

See also

import pyclesperanto_prototype as cle
import numpy as np
import matplotlib
from numpy.random import random

cle.select_device("RTX")
<NVIDIA GeForce RTX 3050 Ti Laptop GPU on Platform: NVIDIA CUDA (1 refs)>

For visualizing relationships between neighboring objects, we start at an artificial cell grid. Cells are aligned approximately in a honeycomb grid. Intensity in these cells is uniformly distributed. Just a single cell in the center of the grid has much higher intensity.

# Generate artificial cells as test data
tissue = cle.artificial_tissue_2d()

# fill it with random measurements
values = random([int(cle.maximum_of_all_pixels(tissue))])
for i, y in enumerate(values):
    if (i != 95):
        values[i] = values[i] * 10 + 45
    else:
        values[i] = values[i] * 10 + 90

measurements = cle.push(np.asarray([values]))

# visualize measurments in space
example_image = cle.replace_intensities(tissue, measurements)

Example data#

Let’s take a look at an image with arbitrarily shaped pixels. Let’s call them “cells”. In our example image, there is one cell in the center with higher intensity:

cle.imshow(example_image, min_display_intensity=30, max_display_intensity=90, color_map='jet')
../_images/01_neighborhood_definitions_5_0.png

Touching neighbors#

We can show all cells that belong to the “touching” neighborhood by visualizing the touching neighbor graph as mesh.

mesh = cle.draw_mesh_between_touching_labels(tissue)

# make lines a bit thicker for visualization purposes
mesh = cle.maximum_sphere(mesh, radius_x=1, radius_y=1)

cle.imshow(mesh)
../_images/01_neighborhood_definitions_7_0.png

If we apply a local maximum filter to this grid, we can see how the high intensity of the single cell in the center spreads to directly touching neighbor cells.

local_maximum = cle.maximum_of_touching_neighbors_map(example_image, tissue)

cle.imshow(local_maximum, min_display_intensity=30, max_display_intensity=90, color_map='jet')
../_images/01_neighborhood_definitions_9_0.png

Neighbors of touching neighbors#

You can also extend the neighborhood by considering neighbors of neighbor (of neighbors (of neighbors)). How far you go, can be configured with a radius parameter.

  • Radius==0 means, no neighbors are taken into account,

  • radius==1 is identical with touching neighbors,

  • radius > 1 are neighbors of neighbors.

for radius in range(0, 5):
    local_maximum = cle.maximum_of_touching_neighbors_map(example_image, tissue, radius=radius)
    cle.imshow(local_maximum, min_display_intensity=30, max_display_intensity=90, color_map='jet')
../_images/01_neighborhood_definitions_11_0.png ../_images/01_neighborhood_definitions_11_1.png ../_images/01_neighborhood_definitions_11_2.png ../_images/01_neighborhood_definitions_11_3.png ../_images/01_neighborhood_definitions_11_4.png

N nearest neighbors#

You can also define a neighborhood from the distances between cells. As distance measurement, we use the Euclidean distance between label centroids. Also in this case you an configure how far the neighborhood should range by setting the number of nearest neighbors n. As mentioned above, neighborhoods include the center cell. Thus, the neighborhood of an object and its n=1 nearest neighbor contains two neighbors.

for n in range(1, 10):
    print("n = ", n)
    mesh = cle.draw_mesh_between_n_closest_labels(tissue, n=n)

    # make lines a bit thicker for visualization purposes
    mesh = cle.maximum_sphere(mesh, radius_x=1, radius_y=1)

    cle.imshow(mesh)
n =  1
../_images/01_neighborhood_definitions_13_1.png
n =  2
../_images/01_neighborhood_definitions_13_3.png
n =  3
../_images/01_neighborhood_definitions_13_5.png
n =  4
../_images/01_neighborhood_definitions_13_7.png
n =  5
../_images/01_neighborhood_definitions_13_9.png
n =  6
../_images/01_neighborhood_definitions_13_11.png
n =  7
../_images/01_neighborhood_definitions_13_13.png
n =  8
../_images/01_neighborhood_definitions_13_15.png
n =  9
../_images/01_neighborhood_definitions_13_17.png

Also this can be visualized using the local maximum filter. Compared to neighbors-of-neighbors, the radius of the neighborhood can be tuned more fine-granular using n-nearest neighbors. Note that for computing the maximum in that neighborhood, not every increase by n += 1 the size of the red area increases. This is due to the fact that not the maximum pixel pushes its intensity to the neighbors, it’s the neighbors which pull that intensity when applying the maximum filter.

for n in range(1, 10):
    print("n = ", n)

    local_maximum = cle.maximum_of_n_nearest_neighbors_map(example_image, tissue, n=n)
    cle.imshow(local_maximum, min_display_intensity=30, max_display_intensity=90, color_map='jet')
n =  1
../_images/01_neighborhood_definitions_15_1.png
n =  2
../_images/01_neighborhood_definitions_15_3.png
n =  3
../_images/01_neighborhood_definitions_15_5.png
n =  4
../_images/01_neighborhood_definitions_15_7.png
n =  5
../_images/01_neighborhood_definitions_15_9.png
n =  6
../_images/01_neighborhood_definitions_15_11.png
n =  7
../_images/01_neighborhood_definitions_15_13.png
n =  8
../_images/01_neighborhood_definitions_15_15.png
n =  9
../_images/01_neighborhood_definitions_15_17.png

Proximal neighbors#

We can also compute the local maximum of cells with centroid distances below a given upper threshold.

local_maximum = cle.maximum_of_proximal_neighbors_map(example_image, tissue, max_distance=20)
cle.imshow(local_maximum, min_display_intensity=30, max_display_intensity=90, color_map='jet')
../_images/01_neighborhood_definitions_17_0.png
local_maximum = cle.maximum_of_proximal_neighbors_map(example_image, tissue, max_distance=50)
cle.imshow(local_maximum, min_display_intensity=30, max_display_intensity=90, color_map='jet')
../_images/01_neighborhood_definitions_18_0.png

Exercise#

Draw a mesh between proximal neighbors with different distances, e.g. in a for-loop.