Measure the thickness of an Irregular ring

Hi, I obtained the Binarization image as below. I don’t know how to measure the thickness of the irregular ring along the angle. That’s make sense? corr

1 Like

I know it could be achieved by the Radial Profile Angle and calculating the distance with 0 pixel value. But I think it is a time-consuming method.

Good day,

the following thread may help:

https://forum.image.sc/t/measuring-wall-thickness-of-plastic-extruded-sample/9866?u=herbie

Regards

Herbie

Hi Herbie,
Thank you very much for your help. I think the thread is helpful. I installed the “polar transformation” plugins firstly. I am very new to ImageJ. After running your code, The minimum, maximum and Mean value are obtained. But the result (All are 2 pixel) is not corrected.Sometimes it shows that “File is not in a supported format, a reader plugin is not available or it was not found.” Could you please tell me what’s wrong with it? And I want to measure the thickness every 1 degree. The maximum or mean value is not what I care. Thanks again. I just use the code below.

// 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] );
Dialog.show();
method = Dialog.getChoice();
run( "8-bit" );
setAutoThreshold( "Default dark" );
setOption( "BlackBackground", false );
run( "Convert to Mask" );
doWand( 0, getHeight() * 0.5 );
List.setMeasurements;
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;
close();
print( method + "Minimum: "+ wMin + " pixel" );
print( method + "Maximum: "+ wMax + " pixel" );
print( method + "Mean: "+ d2s( wMean, 2 ) + " pixel" );
setBatchMode( false );
exit();
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)

Good day!

  1. Please post a typical raw image in the original TIF- or PNG-format. No JPG-format though, because JPG introduces artifacts! You may also post images as Zip-archives. (Converting a JPG-compressed image to TIFF- or PNG-format doesn’t make sense.)
  2. My macro assumes a gray-level image as input, not a binarized image. It performs binarization by “setAutoThreshold( “Default dark” );” and “run( “Make Binary” );”. However the macro should work with binarized images as well. If you prefer to post a binarized image then please don’t use JPG-compression.

The minimum, maximum and Mean value are obtained. But the result (All are 2 pixel) is not corrected.

The object must be completely close which is not the case for the posted binarized image!
Here is a modified version of the posted image with the ring closed:
closedRing
The results I get appear reasonable:results_closedRing

“File is not in a supported format, a reader plugin is not available or it was not found.”

I can’t comment on this. Most probable it has nothing to do with the macro but with the kind of image files you are using. TIFF-files are recommended.

I want to measure the thickness every 1 degree.

I shall post a macro version that does this.

Stay tuned!

Herbie

Here is a macro that writes the desired values to the Results table:

// imagej-macro "pipe wall thickness" (Herbie G., 06. August 2018)
requires( "1.52e" );
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( "Wall Thickness" );
   Dialog.addChoice( "Method", a, a[0] );
   Dialog.addNumber( "Increments", 1, 2, 5, "deg" );
   Dialog.setInsets( 9, 72, 0 );
   Dialog.addCheckbox( "Results table with all values", true );
   Dialog.addMessage( "Measurements start at 3 o’clock position\nwith clockwise increasing angle." );
Dialog.show();
method = Dialog.getChoice();
deg = Dialog.getNumber();
all = Dialog.getCheckbox();
run( "8-bit" );
setAutoThreshold( "Default dark" );
run( "Convert to Mask" );
doWand( 0, getHeight() * 0.5 );
List.setMeasurements;
x = List.getValue( "XM" );
y = List.getValue( "YM" );
nMax = 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 );
n = round( 360 / deg );
if ( n > nMax ) {
   n = nMax;
   deg = 360 / nMax;
   showMessage( "The minimum reasonable angle increment of " + d2s( deg, 3 ) + " degrees is used!" );
}
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 );
   if ( all ) {
      setResult( "Angle", nResults, i*deg );
      setResult( method + "Thickness", nResults-1, w );
   }
   wMean += w;
   if ( w > wMax ) wMax = w;
   if ( w < wMin ) wMin = w;
}
wMean /= n;
close();
run( "Revert" );
print( method + "Minimum: "+ wMin + " pixel" );
print( method + "Maximum: "+ wMax + " pixel" );
print( method + "Mean: "+ d2s( wMean, 2 ) + " pixel" );
setBatchMode( false );
exit();
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., 06. August 2018)

Please make sure that your objects are closed rings!

Good luck

Herbie

Hi Herbie,

Thanks very very much for your help. I just wonder how to close the ring. I have tried fill the hole and remove outliers but it doesn’t work.

Best regards,

Xun

Xun,

I fear that the problem comes from your way of binarization.

If you post a typical raw image in the original TIF- or PNG-format I could have a look at the issue.

Regards

Herbie

Hi Herbie,

I found I cannot upload the tif format image. It is a PNG one.

corrosion

But this is again a binarized image.

Can’t you post the image before the binarization?

Regards

Herbie

Xun_10b1st_01-1%20rotated%20rotated-111

It is the cropped image.

Thanks for posting the image!

Before I want to use the Radial Profile Angle

Why do you need this?

And then I binary the image with “if(v>17747)v=0;if(v<14386)v=0;if(v>0)v=65535”.

Setting individual thresholds is not a good idea.

Then I manually fill the hole and delete some corrosion in the cracks.

Manual interventions cannot be rerproduced and cannot be tolerated with scientific work.

Regards

Herbie

By using Radial Profile Angle, I could obtain the pixel value along the polar line and than calculate the distance with pixel 0.

The value is obtained by trial with threshold .

I know it is unacceptable but I just want to find a way to obtain the thickness of the ring. It is not the final way to process the data.

Could you please give me a suggestion to segmentation the corrosion. Or just introduce me how to make the ring connected or closed? Thanks.

Xun

By using Radial Profile Angle, I could obtain the pixel value along the polar line and than calculate the distance with pixel 0.

I don’t understand what you mean.

The value is obtained by trial with threshold .

That’s not acceptable for reproducibel results and doesn’t generalize. Don’t do this for scientific work.

What you are looking for requires quite some image pre-processing. I think it is possible to create a processing scheme that generalizes but I don’t have the time to create a suitable scheme. I’ve already spent a couple of hours to modify the macro.

It’s your turn now.

Regards

Herbie

Hi Herbie,

Thanks very much for your help. I will learn how to do the pre-processing. Your reminder is very helpful. I want to delete some posts about my original data because I noticed that my supervisor doesnot permit me to show the raw data in public online.

Cheers,

Xun

Here is a macro that works nicely with the provided gray-value sample image:

Two Revisions (for better generalization)

// imagej-macro "corrosion thickness" (Herbie G., 06. August 2018)
requires( "1.52e" );
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!" );
setBatchMode(true);
w = getWidth();
h = getHeight();
orig = getImageID();
run("Duplicate...", " ");
cpy = getImageID();
run("Find Edges");
setAutoThreshold("Minimum dark no-reset");
setOption("BlackBackground", false);
run("Convert to Mask");
run("Median...", "radius=1");
run("Options...", "iterations=3 count=1 do=Dilate");
doWand(0, 0.5*h);
selectImage(orig);
run("Restore Selection");
setForegroundColor(0, 0, 0);
run("Fill", "slice");
run("Select None");
setAutoThreshold("MaxEntropy dark no-reset");
getThreshold(lower, upper);
run( "Revert" );
setThreshold(lower, upper);
run("Convert to Mask");
run("Fill Holes");
run("Restore Selection");
setBackgroundColor(255, 255, 255);
run("Clear", "slice");
run("Select None");
selectImage(cpy);
cntr = newArray(2);
fitCircle(cntr);
close();
run("Canvas Size...", "width="+(w+2)+" height="+(h+2)+" position=Center zero");
run( "Polar Transformer", "method=Polar degrees=360 number=360 center_x="+cntr[0]+" center_y="+cntr[1] );
run( "Make Binary" );
for ( i=0; i<360; i++ ) {
   makeRectangle( 0, i, w, 1 );
   setResult( "Angle", nResults, i );
   setResult( "Thickness", nResults-1, calcWidth() );
}
close();
run( "Revert" );
setBatchMode(false);
exit();
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() {
   p = getProfile();
   s = 0;
   for ( i=0; i<p.length; i++ ) { if ( p[i] > 0 ) s++; }
   return s;
}
// imagej-macro "corrosion thickness" (Herbie G., 06. August 2018)

I can’t test how the macro generalizes to other images…

HTH

Herbie

Hi Herbie,

That’s great! Thank you! I appreciated your help. And it will help others with similar question. I have tested this code and it works well. I will learn it carefully.

Have a good day.

Cheers,

Xun