Average distance between two lines and between a line and particles

Dear users,

I recently started to work with ImageJ and I am facing some problems to analyze my data. I add a representative picture of the kind of analysis I need to do and I have two aims:

Captura|690x163

  1. Measure the average distance between the upper and lower lines. That would be easy if the distance I want to measure would be equal everywhere, but I need to make multiple measurements to get the mean thickness of a layer. I tried to do that manually but that would take a lot of time to do for all the images I have and also the lines I draw and measure are not perpendicular so that could lead to a bias in the results. To draw the lines, which I use to measure area, I use the Polygon Selection Tool and I make many segments, would it be possible to draw perpendicular lines from all those points which join the line below?

  2. On my second analysis I am interested in measuring the distance of each individual particle to the upper line. At the moment I am counting the particles automatically with imageJ, therefore those are added to the ROI manager and labelled with a number as showed in the picture. I want to find a way that I can draw a perpendicular line from he line above to each particle and then measure the distance.

I am very new with this software and I almost do not understand the published codes so I can not modify them by myself for my needs. I would appreciate any help from you!

Many thanks!

In principle you can use two different approaches for the measurements which might help based on older threads in this forum.

  1. A pure image based method:
  1. Using the software R and the spatstat package within Bio7:
1 Like

Hi
@Catalina

Answer to question 1.
This macro (ugly !) measures the thickness on the vertical segments, from the image published on the forum.
Please, manually check some measurements (Do the results seem reliable?).
Can you tell me about your results, if you don’t mind.
Greetings
PS: Please, accept to post your unprocessed source image on the forum.

//----------------------------
/* 
*Clear environment
*/
//-------------------------
print("Thickness measurements");
close("ROI Manager");
close("Results");

//----------------------------
/* Open a image file
*Binarize
*Profile analyze
*/
//------------------------------
//setTool("wand");
doWand(434, 68, 25,"Legacy");
setBackgroundColor(0, 0, 0);
run("Clear Outside");
setOption("BlackBackground", true);
run("Convert to Mask");
run("Fill Holes");
//---------------------------------
//setTool("line");
h=getHeight();
l=getWidth();
interval=l/50;
for(k=1; k<50; k++)
{
makeLine(k*interval, 0 ,k*interval, h);
roiManager("Add");
//----------------------------------------
  profile = getProfile();
  for (i=0; i<profile.length; i++)
      setResult("Value", i, profile[i]);
  updateResults;
//-------------------------------
/* 
*Thickness measurements
*/
//------------------------------------

n=0;
 for (i=0; i<nResults; i++)
{
if(getResult("Value", i)>0 )
n++;
}
print("n=",n);
}
//-----------------------
close("\\Others");
roiManager("Show All without labels");
//close("ROI Manager");

Hi Mathew,

thank you very much for your answer. I tried your macro but it draws 50 vertical lines on a completely black image and gives me a list of values which are all 0. I post the original here so you can maybe apply it and find what went wrong with the macro. I am interested in measuring the thickness of the green layer which is mucus, for that I need that the lines are perpendicular to the upper limit of the green layer, else it wouldn’t give me the thickness. Some other pictures I have are not in the same orientation so sometimes those lines won’t have to be vertical but maybe horizontal or just in a different angle, so the perfect way to do it is if we could define that it draws 50 between 2 lines and that they are perpendicular to one of them. I am sorry if it’s a bit complicated or if I’m explaining it wrong, let me know any doubts you have.

Many thanks!


I’ can not upload it in tiff format, is jpeg fine or which other format would work for you?

Draw a segmented line (right-click the line tool to switch to segmented line tool) along the green area you are interested in. Set the selection to the width of the green using a double click on the segmented line tool, enter the desired width of the line in pixels. Then Edit>Selection>Straighten gives a new window where your selection has been straigthened. Then you can measure vertical lines.

1 Like

Hi
@Catalina
My macro is designed for this image.
It deals with the first question.
It measures thickness.
Please check.


Greetings

1 Like

Well, my first suggestion (if you follow the link) can be applied to both measurements by creating a binary from the lower line to the bottom and then apply the “Exact Distance Euclidean” function which gives you distances from the binary as pixel values.

Now you can select the upper line and extract the distances under the lines (see snippet in the post).

Vice versa you can create a binary form the upper line to the top and then extract the pixel values at the different point selections

1 Like

Hi @Mathew it works fine to measure the distance between then lines and the results are very reliable, however the distance is not perpendicular to the upper line, and then that does not give me the “real” thickness.

I’m gonna try this,

thank you!!!

Hi @eljonco,

thank you for your answer and the approach, I didn’t know this function and that could be very usefull in some cases, however for this particular analysis is not perfect because the width is not constant.

Greetings!

Please note that you can make your line and point selections in the original image and add them to the ROI Manager.
Then you can create a simple binary image with the same dimension (and if necessary scale) and enable the selections on the new image to easier create the binary to apply the measurements.

1 Like

Here a screenshot what it approx. should look like for the 1 question:

Here the binary with the lower line filled to the bottom (of cours it should be more precise - see my link):

And then the Euclidean distance image (Exact Euclidean Distance Transfom) from this binary on which you can enable the ROI’s (point and lines) to extract the pixel values at the coordinates (distances):

You can also create a binary from the upper line filled to the top to measure the distance of the points to the upper ROI.

I would also measure some controls manually if everything was setup correctly.

1 Like

Hi,

thank you so much for your response. I am not obtaining the same as you when I try to get the binary just by drawing a segmented line.

Could you explain me a bit more in detail how to do that?

Hi. I was wondering, can you get the area of the shape (area between the upper and lower line). It seems like you draw a closed shape with vertical lines at the edges of the image. If so, you could simply divide the area with the width of the image and get a mean distance (height). Would that work?

Hi Sorel,

yes, I am getting the area of that shape actually for a further analysis I have to do. However the width is varying along the length of the layer (in some images is very different) and it is not a regular shape as well, therefore it is not that simple (that was also my first way to try to do it).

I see, It would have been to easy. Good luck.

@Catalina
Here is a graphical result.
Greetings

Hi
As @Bio7 had suggested, there is a macro below that gives you a map of Euclidean distances.
Obviously, we can still improve and especially extract a table of distances (Not performed here).
Can you tell me about your results, if you don’t mind.
Greetings.
Mathew

macro "Average distance between two lines and between a line and particles. "
{
requires("1.52v");
setBackgroundColor(0,0,0);
setOption("BlackBackground",true);
// Clean the environment
//-----------------------------------
msg="Have you cleaned up the environment?";
yesLabel="Yes";
noLabel="No";
getBoolean(msg, yesLabel, noLabel);
//---------------------------
// Start batch mode
setBatchMode(true);
//-----------------------------
// Copy and select
orig=getImageID();
run("Duplicate...","copy1");
close("\\Others");
copy1=getImageID();
selectImage(copy1);
run("Duplicate...","copy2");
//-------------------------------
// Start processing
//-------------------------------
run("Duplicate...", "title=1");
run("Duplicate...", "title=2");
run("RGB to CMYK");
run("Stack to Images");
close("C"); close("Y");
close("ROI Manager");
//-----------------------------------------
// Step 1: Delete the upper part.
selectWindow("M");
setAutoThreshold("Triangle dark");
//run("Threshold...");
run("Convert to Mask");
run("Fill Holes");
run("Set Measurements...", "add redirect=None decimal=4");
run("Analyze Particles...", "size=1000-Infinity display add");
wait(1000);
roiManager("Select", 0);
selectWindow("1");
run("Restore Selection");
run("Clear", "slice");
run("Select None");
roiManager("reset");
close("Results");
close("M");
//----------------------------------
// Step 2: Delete the lower part.
selectWindow("K");
setAutoThreshold("Triangle dark");
//run("Threshold...");
run("Convert to Mask");
run("Analyze Particles...", "size=1000-Infinity display add");
roiManager("Select", 0);
selectWindow("1");
run("Restore Selection");
run("Clear", "slice");
run("Select None");
close("K");
//----------------------------------------------------
// Step 3: Isolate the part to be studied.
selectWindow("1");
//setTool("wand");
doWand(52, 1496, 51, "Legacy");
setBackgroundColor(0, 0, 0);
run("Clear Outside");
run("Select None");
run("Duplicate...", " ");
doWand(52, 1496, 51, "Legacy");
setBackgroundColor(255, 255, 255);
doWand(52, 1496, 51, "Legacy");
run("Clear");
run("Select None");
roiManager("reset");
//--------------------------------------------
//Step 4: Select all points of interest
selectWindow("1");
run("Split Channels");
close("1 (blue)");
close("1 (green)");
selectWindow("1 (red)");
run("Find Maxima...", "prominence=50 output=List");
//----------------------------------------------------
// Step 5: Create the Euclidean distance map
selectWindow("1-1");
setOption("BlackBackground", true);
run("Convert to Mask");
run("Distance Map");
run("16 Colors"); 
//-------------------------
// Step 6: Place all points of interest on the Euclidean distance map
for(i=0;i<nResults;i++)
{
x=getResult("X",i);
print(x);
y=getResult("Y",i);
print(y);
selectWindow("1-1");
makePoint(x,y);
roiManager("Add");
}
roiManager("Show All without labels");
//-------------------------------
// End of processing
//----------------------------
// End of batch mode
setBatchMode(false);
run("Calibration Bar...", "location=[Upper Right] fill=White label=Black number=5 decimal=0 font=12 zoom=4 overlay");
close("Log")
run("Cascade");
//--------------------------------
exit("All is done !");
}

1 Like

Another presentation:
Macro:

macro  "Average distance a line and particles. (2) "
{
requires("1.52v");
setBackgroundColor(0,0,0);
setOption("BlackBackground",true);
//---------------------------
// Start batch mode
//Copy and select
//--------------------------
id=getImageID();
selectImage(id);
run("Duplicate...","title=1");
close("\\Others");
setBatchMode(true);
run("Duplicate...","title=2");
run("Duplicate...","title=3");
//-------------------------------
// Start processing
//-------------------------------
run("Invert");
run("RGB to CMYK");
run("Stack to Images");
//-----------------------------------------------
// Clean the lower part
//-----------------------------------------------
selectWindow("C"); close("K"); close("Y"); close("M");
close("3");
setAutoThreshold("Huang dark");
//run("Threshold...");
setOption("BlackBackground", true);
run("Convert to Mask");
run("Fill Holes");
run("Set Measurements...", "area add redirect=None decimal=4");
run("Analyze Particles...", "size=1000000-Infinity add");
setBackgroundColor(0, 0, 0);
roiManager("Select", 0);
run("Clear Outside");
//---------------------------------------------
roiManager("Show All without labels");
roiManager("Show None");
roiManager("Delete");
run("Duplicate...", " ");
//---------------------------------------------
//  Clean the upper part and create the distance map
//---------------------------------------------
setAutoThreshold("RenyiEntropy ");
//run("Threshold...");
//setThreshold(0, 0);
setOption("BlackBackground", true);
run("Convert to Mask");
run("Analyze Particles...", "size=1000000-Infinity add");
//-----------------------------------------------
roiManager("Select", 0);
run("Clear Outside");
roiManager("Show All without labels");
roiManager("Show None");
roiManager("Delete");
//-----------------------------------------------
run("Distance Map");
run("16 Colors");
close("C");
//----------------------------------------------
// Select particles
//-----------------------------------------------
selectWindow("2");
run("Split Channels");
close("2 (green)");close("2 (blue)");
setAutoThreshold("Yen dark");
//run("Threshold...");
//setThreshold(76, 255);
setOption("BlackBackground", true);
run("Convert to Mask");
run("Watershed");
run("Set Measurements...", "area add redirect=None decimal=4");
run("Analyze Particles...", "size=0-200 add");
selectWindow("1"); 
roiManager("Show All without labels");
selectWindow("C-1");
run("Calibration Bar...", "location=[Upper Right] fill=White label=Black number=5 decimal=0 font=12 zoom=4 overlay");
close("2 (red)");
//------------------------------------------------
// Transfer the particles to the distance map.
//-------------------------------------------------
run("From ROI Manager");
run("Tile");
//-----------------------------
// End of processing
//----------------------------
// End of batch mode
setBatchMode(false);
run("Tile");
//--------------------------------
exit("All is done !");
}

1 Like