Measuring wall thickness of plastic extruded sample

Hi @etadobson,
thank you for your reply.

I tried to use the Morpholibj plugin to get those distances, this is what I achieved.

  • I skeletonized the thresholded image.
  • I applied the Geodesic Distance Map function (MorphoLibJ -> Binary Images -> Geodesic Distance Map), using the skeleton as the marker and the thresholded image as the mask.
  • The result is the LUT image attached; please note that I had to convert the image to jpeg, because for some reason I can not upload the LUT image as generated by ImageJ.
  • I exported the LUT in a readable format i.e. a table listing for each index the value of red, green and blue (Image -> Color -> Show LUT and then click on List…)
  • The list is attached

I have a couple of questions I hope you can help me out with.

  • The table exported from the LUT image has 256 lines: they seem to me a very few number; maybe ImageJ exports just a little part of the data?
  • How can I read the results? For example, at the index 111 I have the following values: red = 215, green=32, blue = 38; how can I get a distance from those numbers?
  • How can I understand what point of the circle a certain index refers to? Actually, this is not really important, because I have to find the maximum, minimum and mean value, not in what point they are, but maybe in the future I will have to find also this datum.

Many thanks for your help,
gio (1.4 KB)

Hi @anon96376101,
thank you for your reply.

This is what I get so far using the polar transformation.

  • First of all, I measured the center of mass of the image (Analyze -> Set measurements… and then Analyze -> Measure).
  • Then I applied the Polar Transformer with the following parameters:
    • Method: Polar
    • Degrees used for polar space: 360
    • Default center for cartesian space: not ticked (when asked for the center, I used the center of mass measured earlier)
    • For polar transforms, use one line per angle: not ticked (when asked for the number of lines in angle dimension I put 730)
  • I inverted the the image I obtained (Edit -> Invert)
  • The result (very similar to what you posted) is attached: please note that I had to convert the image to png because for some reason I am not able to upload images as saved by ImageJ.


Now I am stuck to the part of evaluating the horizontal profiles of the transformed image.

  • I selected all
  • I drew the plot profile (Analyze -> Plot profile…)
  • I get the graph attached
  • I exported the data in the attached excel file

Plot (999 Bytes)

How can I read the data I got in order to calculate minimum, maximum and mean thickness?


Good day Gio,

as always in the end things aren’t as easy as they might appear at the beginning …

  1. The center of mass is only one of possible centers for the polar transformation, fitting circle is another. You may even consider the latter as a mean of the fitted inner and outer contour. You must decide …

  2. You may transform either the halftone or the binarized image. In both cases you should binarize the transformed image. The decision is yours …

  3. For the profile, draw a horizontal line selection at the vertical position you want to measure. The profile values are either 0 or 255 and you get the width from the distance between the jumps.

  4. You have to decide whether the binarized images give sufficient precision of the measurements.

You can automate all processing steps by using a macro. The macro recoder may help to get the code for the processing steps.
Furthermore, have a look at the documentations:



1 Like


Start reading the thread at this response:

@iarganda explains step-by-step how to extract the data you want… as far as the # of measures… that is most likely based on the length of your longest-shortest-path length. Each measure comes from a point/pixel along that line…



Hi @anon96376101
as far as regards the center for the polar transforms and the accuracy, this is not a real issue for me: this will be an in line check (so the operator is aware if the extrusion process is proceeding correctly or something is going really wrong), the real test will be made in the laboratory by a technician with a much more precise instrumentation.

As far as regards the macro, thank you for the link: I am experimenting. For future reference of other users interested in the topic, here is the core of my macro: it scans all the lines of the image, and give the minimum, maximum and average value (please note that results are in pixel, but to get the real values is just a matter of a division):

var on = 0;
var measures = 0;
var max = 0;
var min = 0;
var total = 0;
var avg = 0;

for(i=0; i<730; i++) {
	run("Clear Results");
	makeLine(0, i, 419, i);
	profile = getProfile();
	for (j=0; j<profile.length; j++)
		if (profile[j] > 0) on++;
	if(min == 0) min = on;
  if(on < min) min = on;
  if(on > max) max = on;
  if(on > 0) measures++;
  total += on; 
	on = 0;

avg = total / measures;

print("Minimum: " + min);
print("Maximum: " + max);
print("Average: " + avg);


nice to see you’ve found a solution for the width measurement from the polar transform of your images!

Here is an ImageJ-macro that does the whole processing from the original gray-level image:

// imagej-macro "pipe wall thickness" (Herbie G., 14. March 2018)
requires( "1.51v" );
nme = "Polar_Transformer.class";
if ( !File.exists( getDirectory( "plugins" ) + nme ) ) exit( "Macro requires PlugIn \"" + nme + "\" !" );
if ( nImages != 1 ) exit( "A single image must be open!" );
a = newArray( "Center of Mass", "Center of fitting Circles" );
Dialog.create( "Polar Transformation" );
	Dialog.addChoice( "Method", a, a[0] );;
method = Dialog.getChoice();
run( "8-bit" );
setAutoThreshold( "Default dark" );
setOption( "BlackBackground", false );
run( "Convert to Mask" );
doWand( 0, getHeight() * 0.5 );
x = List.getValue( "XM" );
y = List.getValue( "YM" );
n = round( List.getValue( "Perim." ) );
run( "Select None" );
if ( method == a[1] ) {
	outer = newArray( 0, y );
	inner = newArray( x, y );
	fitCircle( outer );
	fitCircle( inner );
	run( "Select None" );
	x = ( outer[0] + inner[0] ) * 0.5;
	y = ( outer[1] + inner[1] ) * 0.5;
	method =  "CoC-"; 
} else { method =  "CoM-"; }
setBatchMode( true );
run( "Polar Transformer", "method=Polar degrees=360 number=[n] center_x=[x] center_y=[y]" );
run( "Make Binary" );
wMax = 0;
wMin = getWidth();
wMean = 0;
for ( i=0; i<n; i++ ) {
	w = calcWidth( i );
	wMean += w;
	if ( w > wMax ) wMax = w;
	if ( w < wMin ) wMin = w;
wMean /= n;
print( method + "Minimum: "+ wMin + " pixel" );
print( method + "Maximum: "+ wMax + " pixel" );
print( method + "Mean: "+ d2s( wMean, 2 ) + " pixel" );
setBatchMode( false );
function fitCircle( a ) {
	doWand( a[0], a[1] );
	run ("Fit Circle" );
	getSelectionBounds( x, y, w, h );
	a[0] = x + w * 0.5;
	a[1] = y + h * 0.5;
function calcWidth( idx ) {
	makeRectangle( 0, idx, getWidth(), 1 );
	p = getProfile();
	s = 0;
	for ( i=0; i<p.length; i++ ) { if ( p[i] > 0 ) s++; }
	return s;
// imagej-macro "pipe wall thickness" (Herbie G., 14. March 2018)


1 Like

Thank you for that, very helpful and instructive!

Hello @gio

The LUT is just a color mapping between pixels values and their corresponding color representation. In ImageJ LUTs contain 256 colors, so that’s why.

The distance is not stored there but in the result distance image. Each pixel has a value which is the distance to the closest border.

1 Like

Hi there,

In the image “”, I am trying to determine the thickness of the red ring along the entire circumference. I would very much appreciate advice how to determine ring thickness in an automated fashion, since I have hundreds of samples. This post seems to pointing in the right direction. (1.2 MB)

To this end, The macro’s look promising. I have tried them, but got the following error.

Error: Statement cannot begin with ‘’ in line 1:
{ <> rtf1 \ ansi \ ansicpg1252 \ cocoartf1671

I have tried the polar transformation, by fitting a circle around the outer edge of the ring. The zip file shows the result.
Screenshot 2019-01-29 (398.6 KB)

I’m not sure what to do at this point and would appreciate your insights. Thank you in advance for your help,


Good day!

the above posted macro works perfectly with your sample image after some pre-processing has been applied:

Using the very approach—that is based on the polar transformation—leads to erroneous thickness measurements if the ring-like structure deviates considerably from a circle which appears to be the case for the structure in question. In such cases approaches based on the polar transformation are unsuited.



Thank you Herbie - that is great the Macro works, although it is unfortunate my sample deviates too much from a circle. I will look into other approaches and see what I can find.


Hello @anon96376101 ,

Thank you so much for the macro you posted above. Would you be able to assist me further in it’s use? I’m trying to use it to batch process some microCT image slices of a polymer tube.

I’ve had varied success while using it to process the same image, which would suggest that it is something I’m doing that is introducing the variation.

Taking a single image:INITIALTUBESEGMENTSCAN1crop|nullxnull
I’ll provide the raw uncropped image too:

- the larger outer circle is the container used to house the sample (inner circle) :slight_smile:

**EDIT: I had trouble uploading the images apparently;


I had the macro work as I expected once and have thus far been unable to repeat it.
Using the CoC method the outputs were:
CoC-Minimum: 17 pixel
CoC-Maximum: 25 pixel
CoC-Mean: 21.62 pixel

Which with my scale (75 pixels to 1 mm) equates to a range of around 226 - 333 micrometres, and a mean of 288 micrometres; which is basically spot on what I’ve manually measured using a microscope.

Unfortunately, I often get an error message, “this command requires a selection”, or what seems to be random measurements often what appears to be a single measurement, say 90 pixels for all three outputs etc

I’m quite new to imageJ so perhaps I’m just screwing up something obviously simple. Any input would be greatly appreciated!!


Distance map, then find watershed, you can get the mid-axis ring (the value means the local radius). then do a pixel statistic.

1 Like

Good day Kerr,

please tell us what the green circle means?

Is it a selection and where does it come from?



My macro works perfectly if you reset the scale to pixels. Presently, my macro doesn’t respect any scale set to an image and I won’t change this in the near future.

You may just introduce the following code line to my macro to do this automatically:

if ( nImages != 1 ) exit( “A single image must be open!” );

run("Set Scale...", "distance=0 known=0 pixel=1 unit=pixel");

a = newArray( “Center of Mass”, “Center of fitting Circles” );

These are the values I get:



1 Like

Hi Herbie, thanks so much for the quick response.

You are correct, your macro works absolutely fine when I don’t apply a scaling. I can do that after the measurement phase manually or in an excel document so it’s completely fine :slight_smile:
EDIT - or I can just follow your advice above, cheers :slight_smile:

Apologies for not being more thorough in my application of it.

In reference to your other question the green line is a “region of interest” line as part of the silly microCT software for taking the 2D slices and constructing a 3D version of the object. I went back and removed it from the images to save any issues arising :slight_smile:

The next stage of what I’m looking to do requires batch processing of thousands of these images. I’ve been mucking around editing parts of the code trying to remove the dialog option and force the CoC mode, so that I can leave it running (via the multiple image processor tool) without user input to cycle and measure through all these images. I seem to introduce changes that enable the code to run without error…but by skipping over the actual “content” (pre-processing and measurement + display)… :sweat_smile::sweat_smile: (fairly disappointed in myself, I thought my matlab experience would have helped more :cry:)

If you want to work with the whole image, i.e. avoid the extraction of the tube of interest, you could change the following code line

doWand( 0, getHeight() * 0.5 );


doWand( 90, getHeight() * 0.48 );
setBackgroundColor(0, 0, 0);
run("Clear Outside");

This change assumes that the container position in your images is about the same for all of your images.



This zipped macro code (1.7 KB)
batch processes all images in a folder.

The macro assumes that the images show the tube of interest and its container, such as:

The measurements are output to an ImageJ-results table, one row per image. The results are in millimeters and it is assumed that the scale is the same for all images. It is set in this code line:

pel = 74; // pixels per millimeter
1 Like

Thanks Herbie, that is outstanding work and will save me an incredible amount of processing time.
Can I paypal you some beer (or whatever) money or something? Other than that all I can offer is an acknowledgement in my thesis :joy:

I have one last question; I’m assuming it is possible for the macro to report each individual measurement event as well as reporting the mean/min/max values?

Good day,

and good to hear that the code fits your needs.

Yes, in any case please mention in reports, theses or publications that you’ve received help. For details click my name. A beer is metaphorically accepted as well: Cheers!

The problem with your last question is the way you like to see the data outputted. Per image you get 360 plus three rows. So do you like to see the data in separate tables and files that are saved to the folder with the images. This would be a possible solution: one data-file per image.



Perhaps a good output would be 360 columns and a single row for the raw measurements per sample? (sacrificing the mean/min/max calcs)
(or preferably the inverse with 360 rows and 1 column per sample? [if that is even possible))

That way it would keep the data in a single file for many images together, and make it fairly easy to import into statistical software to delve a bit deeper (make a nice histogram etc)