Circularity/ roundness (DIN ISO 1101)

Dear all,

I’m trying to find the minimum and maximum distance of center of ROI to permeter of ROI, see picture attached.

I’d really appreciate if you could help me :slight_smile:

Katharina

1 Like

Good day Kath,

please tell us if the centerpoint coordinates are known?

Best

Herbie

Hi Herbie,

thank you for your answer!
We get the centerpoint of the roi with the roi-manager.
The other two circles should have the same centerpoint coordinates.

Geetings
Katharina

Katharina,

here is a macro that does what you want for selections (ROIs) that are not rectangular:

/////////////////////// macro start ///////////////////////
requires(“1.51o”);
if (Roi.getType == “rectangle”) { exit(“Rectangular ROIs are not allowed!”); }
Roi.getCoordinates( roiX, roiY );
//Array.show( “Coordinates”, roiX, roiY );
List.setMeasurements;
cX = List.getValue(“X”);
cY = List.getValue(“Y”);
print( "Center X = " + cX + "; Center Y = " + cY + “;” );
w = getWidth();
h = getHeight();
dSqmin = w * w + h * h;
dSqmax = 0;
for ( i=0; i<roiX.length; i++ ) {
dSq = (roiX[i]-cX) * (roiX[i]-cX) + (roiY[i]-cY) * (roiY[i]-cY);
if ( dSq > dSqmax ) { dSqmax = dSq; }
if ( dSq < dSqmin ) { dSqmin = dSq; }
}
print( "Max Distance = " + sqrt(dSqmax) + "; Min Distance = " + sqrt(dSqmin) + “;” );
exit();
/////////////////////// macro end ///////////////////////

The code uses the Centroid as center.

Paste the code in a new macro window:
Plugins > New > Macro

You run the macro by Cmd-R (Mac) or Ctrl-R (PC).

HTH

Herbie

Edit: Updated script to include calibrated calculations.

Hello Katharina, and welcome to the forum. I see that @anon96376101 has already provided a macro solution to your question. I was playing around with this before I saw his post, just for practicing, so I will post my solution as well.

Edit: I added a lot of comments to the code, in case you are not familiar with programming (or just python).

It is very simple, but you can build on it if you are using this as part of a greater analysis protocol. It currently assumes that the ROI you want to analyse is the only (or first) ROI in the Roimanager.

To run it (in Fiji) copy-paste the code below to:

File >> New >> Script.

In the script window, select the language at the top (Python).

Click Run.

from ij import IJ
from ij.gui import Roi
from ij.plugin.frame import RoiManager
from java.awt import Polygon
import math
from ij import ImagePlus
from ij.measure import Calibration

def Roi_Dist():
	""" Distance from center calculator. """

	# Gets active image and it's calibration.
	imp = IJ.getImage()
	calib = imp.getCalibration()
	
	# Get RoiManager.
	rm = RoiManager.getInstance()

	# Get the desired region, I only have the one in rm so index = 0.
	region = rm.getRoi(0)

	# Find the center [x,y] however you prefer, this gets the contourcentroid.
	center_pixels = region.getContourCentroid()
	center_calib = [calib.getX(center_pixels[0]), calib.getY(center_pixels[1])]

	# Gets the pixel coords of all the points in the ROI polygon.
	poly = region.getPolygon()
		
	poly_xy = zip(poly.xpoints, poly.ypoints)

	# Calibrated coordinates.
	calib_x = [calib.getX(poly.xpoints[i]) for i in range (len(poly.xpoints))]
	calib_y = [calib.getY(poly.ypoints[i]) for i in range (len(poly.ypoints))]
	poly_xy_calib = zip(calib_x, calib_y)
	
	# Calculates distance from the center to each point in the polygon.
	# sqrt( (x2 - x1)^2 + (y2 - y1)^2 )
	distances = [ math.sqrt((poly_xy[i][0] - center_pixels[0])**2
				+ (poly_xy[i][1] - center_pixels[1])**2) for i in range(len(poly_xy)) ]

	distances_calib = [ math.sqrt((poly_xy_calib[i][0] - center_calib[0])**2
				+ (poly_xy_calib[i][1] - center_calib[1])**2) for i in range(len(poly_xy)) ]

	# To round the distances to 2 decimals, include the following line.
	#distances = [ round(i, 2) for i in distances ]
	
	# Prints distances.
	print "List of distances from center: ", distances
	print "List of calibrated distances from center: ", distances_calib

	# Prints (and IJ.logs) longest and shortest distances.
	print "Longest distance: ", max(distances), "Shortest distance: ", min(distances)
	IJ.log( "Longest distance: " + str(max(distances) ))
	IJ.log( "Shortest distance: " + str(min(distances) ))
	
	print "Longest calibrated distance: ", max(distances_calib)
	print "Shortest calibrated distance: ", min(distances_calib)
		
	IJ.log( "Longest calibrated distance: " + str(max(distances_calib) ))
	IJ.log( "Shortest calibrated distance: " + str(min(distances_calib) ))

	

Roi_Dist()

Hi you two,

thanks a lot for your help!
It seems, that both solutions work. My only problem now is, that I set a scale to change pixel for mm before, but I think the macros don’t consider that. The values appear too high.

Katharina

HTH Katharina.

No, none of these approaches consider the image calibration/scale. I updated my post to include this, try it out. Note that it targets the active image, so the image you selected before you hit run. There are better ways to do this, so if you need to improve it just ask!

Best,

Sverre

Hi Sverre,

I tried it with some examples and I just don’t get it. Perhaps some of the things that I do before I use your code are wrong.

  1. I take the big white round circles major distance to calibrate the picture (set scale --> 40 mm) (there e.g. results 28,975 pixel/mm)
  2. Calculate the area (with roi manager) of the white circle with the black hole, calculate the area of the black hole to get the share of the black whole
  3. Calculate the centerpoints of white circle and black hole (with roi manager) to get displacement of the black whole in the white circle
  4. get the roundess of the black whole (what we try to solve since yesterday ;-))

To show you two examples of the roundness-calculation:
The values that I get don’t seem right to me.

Hello Kath, let me try to clarify this.

This is what the script does, and to me it looks very accurate. Why do you feel these values are off?

I ran your bottom image with a scale of 40x40 mm and the script told me the longest distance from the center is ~15mm. Sounds correct in the current scale. A manual test with the line tool (see below) gives me 14.96 mm.

To your questions:

So your scale is defined as: the longest distance in the white circle = 40 mm? This sounds off. If the circle is not rigid, how can you know the real value of the longest distance?

Can you specify what the big white round circles are? Do you mean the big black circle? Or the white area between the black hole and the big black circle?

Aha, so what you actually want is the distance from the centerpoint of the black hole to the perimeter of the white circle?

This script does not measure roundness! It simply measure the distance of the center of the ROI to the perimeter. It is in no way a roundness-calculation. If you want roundness all you need is the feret diameter and the area… There is also a built-in roundness measurement in Fiji.

Analyze >> Set measurements >> Shape descriptors >>>> select ROI and click measure.

Good day Katharina,

here is a version of my macro that respects the scale setting of the image:

/////////////////////// macro start ///////////////////////
requires(“1.51o”);
if (Roi.getType == “rectangle”) { exit(“Rectangular ROIs are not allowed!”); }
Roi.getCoordinates( roiX, roiY );
toScaled( roiX, roiY );
List.setMeasurements;
cX = List.getValue(“X”);
cY = List.getValue(“Y”);
print( "Center X = " + cX + "; Center Y = " + cY + “;” );
w = getWidth();
h = getHeight();
dSqmin = w * w + h * h;
dSqmax = 0;
for ( i=0; i<roiX.length; i++ ) {
dSq = (roiX[i]-cX) * (roiX[i]-cX) + (roiY[i]-cY) * (roiY[i]-cY);
if ( dSq > dSqmax ) { dSqmax = dSq; }
if ( dSq < dSqmin ) { dSqmin = dSq; }
}
print( "Max Distance = " + sqrt(dSqmax) + "; Min Distance = " + sqrt(dSqmin) + “;” );
exit();
/////////////////////// macro end ///////////////////////

Regarding some other issues I think they may be related to making reasonable selections for your analyses but I may be wrong.

Regards

Herbie