Count touching neighbors#

In this notebook we visualize which cells in a tissue are neighbors by drawing a mesh. Furthermore, we can change the intensity of the mesh so that it corresponds to the distance between the centroids of the cells.

import pyclesperanto_prototype as cle
import numpy as np
import pandas as pd

Test data#

Let’s generate some tissue-like structure consisting of cells which typically have approximately 6 neighbors.

cells = cle.artificial_tissue_2d(
    delta_x=48, 
    delta_y=32, 
    random_sigma_x=7, 
    random_sigma_y=7, 
    width=250, 
    height=250)

cle.imshow(cells, labels=True)
../_images/a239d30a5f638c6d12d046b9a4753ec2bb40d0b9731c9978630416c34e95cb81.png

Mesh between neighboring cells#

Before counting neighbors, we should visualize neighbor-relationships. We can do this by drawing a mesh between centroids of touching neighbor cells.

mesh = cle.draw_mesh_between_touching_labels(cells)

cle.imshow(mesh)
../_images/73eb44d8f84fd09a8bea5023ec835deda60879a71faccce7e1cfa0b327748bad.png

We can also combine both visualizations in one image. Note, these images should not be used any further for quantitative analysis. It just serves visualization purposes.

Centroid connections and cell borders#

A common way for visualizing tissues in this context is by drawing cell-borders and the centroid mesh in different colours.

visualization = mesh * 2 + cle.detect_label_edges(cells)

cle.imshow(visualization, color_map='jet')
../_images/06b49f4a39272a25bef41589fbe180a68febfa282f5fc85afccd2680da88a553.png

Analyze and visualize number of touching neighbors#

We can also count the touching neighbors and visualize the result as parametric image in colours.

neighbor_count_image = cle.touching_neighbor_count_map(cells)

cle.imshow(neighbor_count_image, color_map='jet', colorbar=True, min_display_intensity=0)
../_images/d25f8180795fcae6816e5176e9d82106bfe9bf4f8f4698be1faae724792c27e6.png

Note, the numbers along the image border may not be accurate. Hence, we should exclude the corresponding cells from the further analysis.

cells_ex_border = cle.exclude_labels_on_edges(cells)

cle.imshow(cells_ex_border, labels=True)
../_images/2661182fc554bd374db54a176ad01c6affed0f2d22399fe67678a21a9c723517.png

After correcting the label image, we can also correct the parametric image.

neighbor_count_image_ex_border = neighbor_count_image * (cells_ex_border != 0)

cle.imshow(neighbor_count_image_ex_border, color_map='jet', colorbar=True, min_display_intensity=0)
../_images/059356bb30ff5d2327058e3058156e4dd3bf37bb48fc7f3edb81e2803bdd131e.png

Now, we can measure the number of neighbors. We can either just read those numbers and put them in a list …

cle.read_intensities_from_map(cells_ex_border, neighbor_count_image_ex_border)
cl.Array([[0., 5., 6., 7., 6., 6., 7., 6., 6., 5., 5., 6., 6., 7., 6., 6.,
        6., 6., 6., 6., 6.]], dtype=float32)

… we can also read these values together with all other statistics and put them in a pandas DataFrame.

statistics = cle.statistics_of_labelled_pixels(neighbor_count_image_ex_border, cells_ex_border)

table = pd.DataFrame(statistics)

# rename a column
table = table.rename(columns={"mean_intensity": "number_of_neighbors"})

# only filter out a subset of all columns; only what we care
table = table[["label", "number_of_neighbors", "centroid_x", "centroid_y"]]

table
label number_of_neighbors centroid_x centroid_y
0 1 5.0 74.592697 23.731028
1 2 6.0 173.741379 35.582230
2 3 7.0 49.460915 51.928082
3 4 6.0 91.783768 52.666199
4 5 6.0 142.728210 62.962471
5 6 7.0 189.973206 68.526794
6 7 6.0 73.699181 88.797356
7 8 6.0 115.321083 90.665649
8 9 5.0 163.097733 99.824242
9 10 5.0 58.206852 122.341309
10 11 6.0 98.847565 134.280228
11 12 6.0 147.731705 127.326218
12 13 7.0 200.522903 112.817368
13 14 6.0 67.880905 162.991592
14 15 6.0 122.943306 157.515717
15 16 6.0 171.010025 153.333328
16 17 6.0 43.426266 189.492981
17 18 6.0 96.272728 192.326477
18 19 6.0 140.815964 194.446030
19 20 6.0 193.817596 193.019318

Exercise#

Analyze a larger field of view with more cells and vary the parameters random_sigma_x and random_sigma_y of the artificial_tissue_2d function. Use a touching-neighbor-count map to count the number of touching neighbors before and after applying a median filter to the map.