Analyzing cell layer thickness



So @ofirforsht

Using the binary image you attached… I did the following steps:

  1. Threshold the binary image
  2. Create Selection - I also added it to the ROI Manager
  3. Skeletonize - I did have to do an inversion before this step to make sure the skeletonization was occurring within the bounds of your cell layer
  4. and Analyze Skeleton to calculate the longest shortest path (make sure you select that calculation)… this is your center line along the length of your cell layer

For me - this workflow was fine. Try again and let us know.

And then look at this old forum post for finding your values along that line:

That should do it!



Sorry for the hassle, but I followed your steps and still couldn’t get the results…I wanted to go through the steps again to see where I go wrong:

  1. I started with the original image - black ROI on white background. At which part did you invert the image?
  2. Thresholding the image does nothing, why did you do it? it’s already B&W. I did it (image>adjust>threshold and kept the auto threshold). thresholding makes the ROI black and background white again if I invert first.
  3. create selection chooses the ROI, so if I try to invert it will have to be before this step otherwise I will end up with entire image being white…
  4. Any attempt to skeletonize the image when the ROI is white on black background resulted in the weird skeletonization I posted in the last post (the second image, when the skeleton is constructed outside of the ROI instead of in it). BTW if I try to analyze skeleton on it I will get results…just irrelevant ones of a bad skeletonization.
    I would appreciate going (literally) step be step with me so I could reproduce your results.
    Thanks again for all the help


sorry for coming out from the blue,
but please check if all of you have the same background colors options, in:

  1. Edit->Options->Colors
  2. Process->Binary-> Options

and all of you are using “inverted” or “not inverted” LUTs LookUp Tables

Sometimes, it’s just that.

Emanuele Martini


Thanks for that!
in the binary options, black background is unchecked. After I checked it everything works perfect!


After getting the detailed branch list I looked and some of them seem to be very short.

  1. Is a branch only the length of one side of the longest shortest path (thus needs to be doubled to get the thicknes)?
  2. I see that some of them are not perpendicular to the longest shortest path and some are the short processes at the end of each branch - both types of lines will add noise to my results…how should I substract them from the results, considering that the cell layer thickness may vary so I can’t set a minimum-maximum thickness to gate them out?



You can use the longest-shortest path as the line selection from which to measure your local thickness. You don’t use the branches in this case… just read through all of the above links I provided.



So I went through the previous posts and I assume you refer to the getShortestPathPoints() function to be used on my Longest shortest path in order to get the array of lines along it…unfortunately I’m a newbie in java so I couldn’t understand how to implement this on my Longest shortest path.
The macro I recorded for the analysis of the binarized image is:

run("Analyze Particles...", "size=1000-Infinity display add");
run("Fill Holes");
setOption("BlackBackground", true);
run("Analyze Skeleton (2D/3D)", "prune=none calculate");
selectWindow("Longest shortest paths");
// ArrayList<point>[] getShortestPathPoints()  - not a clew how to proceed...

I would appreciate it if you could point me out on how to define the parameters for my output in order to get the array.



Sorry for the delay in response… The code you would need is in this older forum post that I had posted previously:

Just read through that forum thread and then give things a try… if you are still stuck - we can try to get you unstuck!

eta :slight_smile:


So I went through the post and I see the corrected Analyze skeleton, the problem is that I’m not sure how to use it… as I mentioned I’m a newbie so I would appreciate a step by step on how to use it.
Thanks for all your help!



In the Segmentation workshop at around 1:46:20… an example using Analyze Skeletons is used. So that should be enough to get you started. If you have more specific questions again - don’t hesitate to ask.

eta :slight_smile:


Hi @etarena
So I already made it through the workshop and managed to get the total area of the cell layer (ROI) and the longest shortest path.
What I’m looking for as a result is the thickness along the longest shortest path, for example to get an array of each measured point along the path from left to right and to extract the distance from that point along the path to the border of the ROI, so I could ultimately draw a graph of the thickness distribution along the cell layer…



Sorry! My brain was still waking up… I misread your post.

Let me take a look at what @imagejan did in this old post to see if I can tease it out of there (haven’t done it myself yet!).

But you are also asking in this thread for this solution - yes??

So… let me know if you just want to go that direction instead - seems like @iarganda is getting you there.



Good day Ofir Forsht,

here is my result concerning your second sample image:

Thickness in pixels determined in a 16pixel wide running window (black) and polynomial fit (blue):

The method I’ve used is only applicable if the layers are rather straight.



Measuring distance between two lines
Corrosion Thickness Measurement

Hi @etarena
Yes, I tried my luck in a few threads…I got quite far the other way but not there yet.
The thread you mentioned seems relevant but honestly I didn’t understand what they did or how to implement the new Analyze_skeleton they created on my project.
Still need your help if you could figure it out.
Thank you,


Hi @Herbie,
Thanks for your help, but the layer I’m checking is almost never straight and sometimes can even have a twist in the middle so not sure if this is applicable in my case…
Let me know if you think otherwise.
Thanks anyway,


Dear Ofir,

it’s always difficult to help if the posted sample images are not representative for the task in question.

I showed a solution for one of your samples (that appears to be taken from the literature) and I really should like to know if the result is what you were looking for.




Hello @Herbie,
First of all, thank you for your help.
I tried to use polynomial fit and couldn’t get the results you got, didn’t even had an idea on what to choose in the X/Y axis degree…I might need a step by step instructions. hopefully we are talking about the same plugin -

Regarding the not representative sample remark - the images I attached are representative, it is the less common images that I am afraid of.
As the person who made all the steps to generate the images from a full animal (not taken from the literature BTW) I know that variation in staining intensity, sample integrity, folds and twist during histological slide preparation and a few more factors can create less than ideal samples. I’m attaching a few of them so you could see for yourself.
I am looking for a robust method that can be applied on all samples with minimum change in each image analysis to decrease variation, this is why I addressed your remark regarding the requirement for a straight sample as a problem.


Good day Ofir,

thanks for the details and the images!

Just a few comments:

  1. I tried to create a series of processing steps that finally led from the second of your initially posted sample images to the result image and width data shown in my previous post.
  2. To create this series of ImageJ macros took me about a day (actually much more than eight hours though). There is no relation to the plugin you’ve mentioned.
  3. The main processing steps are:
    a) Rotational alignment of the image
    b) Segmentation of the layers
    c) Width analysis of the target layer
  4. I was heading for the most accurate width measurements possible, i.e. I’ve tried to avoid lowpass filtering as much as possible.
  5. The greatest problem was with step b) and I know that my approach doesn’t generalize well, even if the layers are rather straight. You’ve mentioned preparation problems (smearing etc,) that I’m aware of.
  6. My impression that the image comes from the literature is based on a Fourier-spectral analysis used in step a) that indicates the presence of a pronounced high frequnecy periodic structure in the image that may be related to a printing raster. However, it may also have different causes (did you do image stiching?).
  7. Meanwhile, I explored another approach that you may have already seen on this Forum:
  8. This approach is much more universal and robust and doesn’t require approximately straight layers; but the problem regarding layer separation remains (you’ve just posted a request that concerns this issue).
  9. Regarding good layer separation, I don’t see a straightforward automatic solution.




Hi @Herbie,
Thanks for you elaborated reply.
I can say with good confidence that cell layer segmentation is not an issue for my anymore. Using either Weka segmentation or color segmentation I generate a very good segmentation of My ROI, other than the overlapping layers I have mentioned and you’ve answered - the manual correction will actually be a good segmentation QA step in my analysis.
My concern now is how to create the cell layer thickness along the entire layer so I would love to hear about the new approach you mentioned in comment #7 (I saw no details on how to do it). I am sorry in advance, I’m a newbie in imageJ and java so you should probably take me step by step in your explanations…
BTW I attached the original images taken with Nikon eclipse 80i microscope, so I have no clue how could the high frequency periodic structure you mentioned be formed.


Well Ofir,

that cell layer segmentation is no longer a problem is somehow astonishing for me but maybe due to the fact that you are happy with approximate segmentations. The effort doesn’t scale linearly with precison/accuracy and perhaps my picture of the goal was different.

[…] approach you mentioned in comment #7

The approach is that proposed by the original poster of the thread, i.e. not exactly, because it is only the first step.

Applying this method to binary images is possible with existing ImageJ-plugins as demonstrated by the original poster in his last post of the thread: A schematic result is shown in his image C.

My approach however works with the original image data, i.e. without thresholding. The code is experimental and not for distribution, but I’m sure you will find someone who is able to write the necessary code, or even better: Do it yourself.