Measuring distance to neigbours

Hello! We were wondering if there is a tool in imagej that allows us to calculate the distance to every neighbour for every point in the image. We are studying marine biology and we are trying to measure aggregation behaviour under predator stress in mussels. We have no experience with imagej so we hope you can help us! Regards, Ferdi & Demi

Yes this is possible, at least with a corresponding Imagej macro.

The problem with your sample image is perspective distortion and reflections.
Please don’t underestimate the problem of image acquisition.
What you need is a reproducible position for your camera with careful adjustment which needs to provide raw images. Best is to use telecentric optics or at least long focal length optics.

Furthemore, your objects need to show good contrast and the illumination needs to be as even as possible.

Regards

Herbie

Hi Herbie! Thanks for taking the time to answer :slight_smile:

We are aware of the reflections in this picture. We are using an umbrella right now to shield most of the light. This experimental set up is located outside and we are using natural sediment found in Zeeland (province in the Netherlands) to ensure an environment that is as natural as possible. Unfortunately, this makes it hard to get good contrast between the mussels and their sediment and even lighting.

We figured it might be possible to manually pinpoint the mussels and then calculate the distances between those points using some kind of macro. Please let us know if that makes sense, we appreciate your help!

Kind regards,
Ferdi & Demi

Hello Herbie, we know the circumstances are not ideal, this is almost always the case when you work in the field. We don’t have many resources to work with since this is a research course of 7 weeks. We don’t have spectacular cameras etc, we have to work with the material we were given. We just want to know if it is possible to automatically measure the distance between manually selected points :slight_smile: Thank you!

Thanks anyway, we’ll see if someone else can help us :slight_smile:

Hi @DDamstra

Looks like a fun project.

However we would probably need a bit more info to make sure we can help you get on the right foot.

Like @anon96376101 says, the moment you need quantifiable data, you need a very stringent control over the acquisition parameters to ensure your experiments are reproducible and that the numbers you are extracting are meaningful.
One such problem is indeed possible distortions by the water and the angle at which you are acquiring.

If your camera is in the very same position for each experiment and the water at the same level, you can probably get away with it and interpret trends in the change of the distances, which is probably what you want. I would just be weary if writing something like 'the mussels were on average 5cm closer to each other" but instead say something like “compared to the control, the computed average neighbor distance was reduced by 20%”. It will be safer if you keep it relative.

Onto the fun stuff

What I currently see are points drawn manually (Seeing how different they are from the background, it seems feasible to detect your mussels automatically…(See @anon96376101 points above for better acquisitions)

Now to measure nearest neighbors there are many tools to do it. A potential recommendation would be to use Delaunay Triangulation.

I’ve attached a script that will show you an example of what you can do with Delaunay triangulation. You can download and run it here: (You need Fiji, because I use the Delaunay Voronoi plugin)

3 Likes

If you knew the exact size of the frame AND you are taking the photos from exactly the same point every time, then you could attempt to correct the perspective. There are plugins for thatlike TransformJ and https://imagej.net/Interactive_Perspective
That does still not resolve the problem of the reflections and uneven illumination.
If not possible then as others observed, you have several problems to resolve before attempting to obtain a meaningful result.

Also, please no JPGs!

Dear all,

I would like to offer a counter-point to the issues pointed out during the discussion on this topic.

While it it clear that there are reflections and that the illumination is sub-optimal, the only thing @DDamstra requested was a way to get nearest neighbors from Manually selected points. It was us that suddenly wanted to do automatically and requested these good practices to be enacted.

Now regarding perspective

Perhaps a deeper understanding of the biology in question would help mitigate this issue. The mussels are very flat and are lying exclusively at the bottom of the tank, so any argument about 3D measurements, while valid, will most likely yield minimal differences.

Onto the perspective correction and calibration of the image.

It is clear that the images are taken by hand, as I can see the reflection of the photographer in the picture :slight_smile:
Again, from my point of view this is not a problem as we can correct for the perspective.
The problem would be the calibration of the image. But I am confident that with knowledge of the length and width of the bottom of the tank, we can compensate for small variations in the distance and perspective of the acquisition.

Please find attached a small macro that can perform perspective correction on your images.
You would need

  1. The length and width of your tank in mm let’s say
  2. To draw 4 points at each corner of the tank with the multipoint tool (top left, top right, bottom right, bottom left in order)
  3. Running the macro (and under the assumption you are using Fiji) it will call the Landmark Correspondences plugin, calibrate the image and give you the calibrated and corrected image.
  4. You can now draw the points you want to use and run the macro I offered above (Just delete lines 14 and 15 and it will run on your own hand drawn points)

Here is the small macro I wrote for this

/** Perspective Correction
*/
//@Double tank_length (label="Real Tank Length (mm)")
//@Double tank_width (label="Real Tank Width (mm)")
setBatchMode(true);
image = getTitle();
// Get width/height ratio
ratio = tank_length / tank_width

// Make the length be 1000 pixels 
template_length = 1000;
template_height = round(template_length / ratio);

//Calibration
cal = tank_length / template_length;


// Build image to perform registration
newImage("Template", "8-bit black", template_length+100, template_height+100, 1);

// Prepare registration points
xp = newArray(50, 50+template_length, 50+template_length, 50);
yp = newArray(50, 50, 50+template_height, 50+template_height);
makeSelection("point", xp,yp);

selectImage(image);

if(selectionType() != 10) {
	waitForUser("Prespective Correction\n\nPlease draw the four corners of the bottom of the tank\ntop left, top right, bottom right, bottom left");
}

run("Landmark Correspondences", "source_image=["+image+"] template_image=Template transformation_method=[Least Squares] alpha=1 mesh_resolution=32 transformation_class=Perspective interpolate");

rename("Transformed - "+image);
setVoxelSize(cal, cal, 1.0, "mm");
setBatchMode(false);
showMessage("Done, you can now draw your points for nearest neighbor selection");
8 Likes

After correcting the perspective, this little macro will print out the results while it also creates an array that holds all distances. Four sample mussels were put in together with a lot of temporary variables that allow you to inspect how macro code works (using ImageJ1 in debugging mode). Enjoy Zeeland guys!

print("\\Clear");
newImage("Untitled", "8-bit black", 128, 128, 1);
px=newArray(4);py=newArray(4);
px[0]=19;py[0]= 18;
px[1]=103;py[1]=23;
px[2]=64;py[2]=50;
px[3]=55;py[3]= 37;
makeSelection("point", px, py);

getSelectionCoordinates(x,y);
distance=newArray(x.length*x.length);

for(mussel1=0;mussel1<x.length;mussel1++){
	for(mussel2 = mussel1+1;mussel2<x.length;mussel2++){
		x1 = x[mussel1];y1 = y[mussel1];
		x2 = x[mussel2];y2 = y[mussel2];
		m1=mussel1+1;//display normal numbers
		m2=mussel2+1;
		makeLine(x1,y1,x2,y2);
		List.setMeasurements;
		thisDistance = List.getValue("Length");
		print ("distance mussel "+m1+" to mussel "+m2 + " is "+thisDistance + " px");
		distance[x.length*mussel1+mussel2] = thisDistance;
	}
}
makeSelection("point", px, py);
selectWindow("Results");
1 Like

Hi @oburri ! Thank you so much for this code. It is very useful.

I’m going to get back to the problem of measuring the distance to neighbors. I have a Voronoi tessellation image (built with the Biovoxxel toolbox) and I’m trying to build a mesh between first neighbors (objects edge to edge distance = 1 pixel). However, when I apply the Delaunay plugin some false links appear (see image).temp_delaunayMesh

Does anyone have any idea of why this is happening? And can be avoided?

Best,
Mauro Morais

Do you mean the cropped borders?
It must have been a larger image with points, otherwise how do you think the DT was computed?

Hi @gabriel,

Yes, this only happens with the particles that are near the edge of the image. Particles touching the image edges were excluded before running the Delaunay plugin. What I’m trying to achieve are the coordinates of each edge of this mesh (white particles center of mass are the nodes). But what I also get are edges between particles that are not first neighbors (edge to edge distance > 1 pixel). How to exclude those edges?

The problem is, I think, that the Delaunay triangulation does not know anything about the neighbourhood of the cells, it uses the centroids of these to compute the graph.
I think the Delaunay triangulation is computed correctly in your image, but you are after a different graph construct.
In your image there are some edges that cross to nodes of cells which are not nearest neighbours. See the attached cropped image.The two green edges jump over non-neighbouring cells.

Am I correct thinking that you want is some kind of adjacency graph that is constrained to the neighbours of each cell?
One way to do that (there might be others) is to pick a cell, identify its neighbour cells (those which are separated by a gap equal or less than sqt(8), then create edges between the centroids of those, move to the next cell, etc.

graph.tif (83.8 KB)

2 Likes

Yes, that’s what I’m trying to achieve. I know the position of the centroids (and center of mass) of each object (cell) in the image. But how can I identify its first neighbors (gap <= sqrt(8)) using IJ1 macro language?

The centroids do not help. Those might not be even be on the cell itself (think of a “C” shaped region). You can resolve this via mathematical morphology or region connection calculus.
Have look here:
https://blog.bham.ac.uk/intellimic/spatial-reasoning-with-imagej-using-the-region-connection-calculus/

The central cell is in NC relation (exactly this is <=sqrt(8) distance constraint) with each of the neighbours.
With RCC8D+ you can compute NC and work out which are the neighbours to a cell, then compute the centroids and finally make a graph.
If it gets too complicated send me a private message.

2 Likes

Thank you! :smiley: I’ll be in touch.