Count number of neighbour nuclei per nucleus

Hello!

I am currently extracting some measurements from masked images of a densely packed tissue. Among the several parameters I am interested into, I would like to count the number of neighbour nuclei per nucleus. This is an example of a frame from a masked stack (so the nuclei are in 3D actually):

example_frame_masked-stack

I am using Python to analyse these images and I would like to know whether it already exists a function that has been implemented for this task. On this forum, I found questions and solution regarding solely CellProfiler. Is there something also in Python?

Otherwise, is there a way to do it ? Maybe expanding the 3D surface area until objects touch? Do you know a similar script I can use as a basis to try out this?

Thank you a lot!
Best,
Lucrezia

1 Like

Hi @LucreziaF,

that sounds like a great project! Just to clarify: Do you analyse the data in Python or in Fijis Jython?

I did something similar as an example tutorial for the CLIJ Fiji plugin: I measure the distance between touching neighbors. Getting their number instead is straight-forward.

The CLIJ tutorial is written in ImageJ Macro, but we could easily translate it to Fiji Jython if you like:
https://clij.github.io/clij2-docs/md/tribolium_morphometry/

If you work with Python (outside Fiji) and are in experimental mood, you can try the Python-equivalent for CLIJ, clEsperanto, which is currently under development.
The same tutorial workflow is implemented there as well:

Hi Lucrezia,

it’s not entirely clear to me what you are calling a neighbour in this context. Is it touching nuclei or close ones. I think for both cases, you can use something like what you had in mind. Here’s an example on how to detect touching objects. The logic is to individually dilate objects and check with which other objects they overlap. If you want to find neighbours that are not directly touching, you can vary the size of the dilation element (the disk radius in this case). Of course in your case you’ll have to do everything in 3D but the scikit-image functions should work exactly in the same way.

import numpy as np
import matplotlib.pyplot as plt

from skimage.draw import random_shapes
import skimage.morphology

# radius of neighborhood to consider
radius = 1
# create synthetic image with labelled and touching objects
image, _ = random_shapes((128, 128),
                          max_shapes=10,
                          shape='ellipse',
                          multichannel=False,
                          allow_overlap=True,
                          random_seed=0)
image[image == 255] = 0
image = skimage.morphology.label(image)

# display image
fig, ax = plt.subplots()
ax.imshow(image)
plt.show()

touching = {}
for i in range(1, image.max() + 1):
    # dilate each object individually
    dilated = skimage.morphology.binary_dilation(
        image == i, skimage.morphology.disk(radius))
    # using indexing, find which neighbours the dilated object touches
    touching_indices = np.unique(image[dilated])
    # remove background and "self-index"
    touching_indices = touching_indices[(touching_indices != 0)
                                        & (touching_indices != i)]
    # add touching indices to dict
    touching[i] = touching_indices
    print(f'Object {i} touches: {len(touching_indices)} objects')

You can test this here: https://repl.it/@guiwitz/Touchingobjects#main.py

Cheers,

Guillaume

1 Like

Thank you a lot @haesleinhuepf for the great comment!
I will now look into what you sent. I am currently trying to write a script in Python, so I would go for that first. Thank you a lot, I will try what you sent and give feedback!

Hi @guiwitz,
since it is nuclei, they do not necessarily touch, as there should be still a bit of cytoplasm in between them, but they are indeed tightly packed. Therefore I would like to count the number of close nuclei, which may also almost touch each other. Thank you a lot for the link, I tried it out and it looks great, I will also try it in my script and see how it works!

1 Like

Another alternative (can you have too many? :joy:) is to dilate once (by half the radius), and then find touching objects with a region adjacency graph. I recently answered almost the same question on StackOverflow recently!

from skimage import future

rag = future.graph.RAG(dilated)

then remove the background with:

rag.remove_node(0)

and get the neighbours of region i with

list(rag.neighbors(i))

The RAG is a networkx Graph, which means you can use a whole bunch of graph algorithms on this, depending on your downstream analysis. For example, for counting neighbours, use rag.degree.

2 Likes

As far as I can tell, the Neighbor Analysis in the #biovoxxel toolbox (by @biovoxxel) offers exactly this:

2 Likes