How to measure the correct circularity??

36 views (last 30 days)
Timo Holle
Timo Holle on 10 Jul 2020
Commented: Steve Eddins on 22 Mar 2023
Hi,
I am trying to calculate the Circularity using regionprops (based formula: (Perimeter^2)/(4 Pi Area)). The Problem ist that the Circularity is more than 1 for some objects. Are there any other way's for a better calculation?

Answers (3)

Steve Eddins
Steve Eddins on 22 Mar 2023
R2023a Update: Correcting regionprops Circularity Measurements That Are Greater Than 1.0
Image Processing Toolbox R2023a or Later
In releases R2023a or later, the Circularity computation in regionprops has been revised to correct a bias towards higher values, and it no longer returns values greater than 1.0.
See this 21-Mar-2023 blog post for a detailed explanation.
if verLessThan("images","11.7")
error("Use this code with releases R2023a or later.")
end
A = imread("text.png");
props = regionprops("table",A,"Circularity");
Image Processing Toolbox R2019a to R2022b
In earlier releases, you can apply the same correction that was introduced in R2023a by adapting the following example. Note that the call to regionprops below uses the form that returns a table because it makes the correction steps easier.
if verLessThan("images","10.4") || ~verLessThan("images","11.7")
error("Use this code with releases R2019a through R2022b.")
end
A = imread("text.png");
props = regionprops("table",A,["Circularity" "Perimeter"]);
c = props.Circularity;
p = props.Perimeter;
r_eq = p/(2*pi) + 0.5;
correction = (1 - (0.5./r_eq)).^2;
props.Circularity = min(c .* correction, 1);
Image Processing Toolbox R2018b or Earlier
In these older releases, the function regionprops did not compute Circularity. You can compute it by adapting the following example.
if ~verLessThan("images","10.4")
error("Use this code with releases R2018b or earlier.")
end
A = imread("text.png");
props = regionprops("table",A,["Area" "Perimeter"]);
a = props.Area;
p = props.Perimeter;
c = 4*pi*a ./ (p.^2);
r_eq = p/(2*pi) + 0.5;
correction = (1 - (0.5./r_eq)).^2;
props.Circularity = min(c .* correction, 1);

John D'Errico
John D'Errico on 10 Jul 2020
Edited: John D'Errico on 10 Jul 2020
It would not be unusual for that measure to exceeds 1. For example, given a circle, we would have a perimeter of 2*pi*r. So permieter squared would be 4*pi^2*r^2. And the area of a curcle is pi*r^2. So that measure would in theory be 1 for a circle.
And people talk about how the circle has the largest area for a given perimeter. I fact though, that is incorrect. A circle has the largest possible area of all convex regions. But suppose the region is not convex?
For example, consider a circle, but then super-impose a rapidly varying sinusoidal oscillation. Small amplitude, but high frequency. Essentially think of this as creating a fuzzy circle. The perimeter will increase, yet the area stays almost unchanged.
Just for kicks. I'll create such an object, then compute both the perimeter and the area.
F = 100; % frequency of the sinusoidal fuzz
N = 2000;
theta = linspace(0,2*pi,N);
R = 1; % Nominal radius of the circle
A = 0.05; % fuzz amplitude
Xc = (R+A*sin(F*theta)).*cos(theta);
Yc = (R+A*sin(F*theta)).*sin(theta);
plot(Xc,Yc,'-')
What is the arc length of this perimeter curve?
I have a function on the file exchange that can compute the arclength of such a curve.
fuzzyperim = arclength(Xc,Yc,'spline')
fuzzyperim =
21.395
If you don't trust my code, or don't wish to download it from the file exchange, this will give a decent estimate of the perimeter too:
sum(sqrt(diff(Xc).^2 + diff(Yc).^2))
ans =
21.317
If it were really, truly a circle, the perimeter arclength would have been
2*pi*R
ans =
6.2832
What is the actual area of that region? Were it a circle, we would get:
pi*R^2
ans =
3.1416
So, for a truly circular region, we would have:
(2*pi*R)^2/(4*pi*pi*R^2)
ans =
1
What is the area of the region? Numerical integration in polar coordinates will suffice. The area is just the integral of r*r*dtheta. (Don't forget that extra r, since we are working in polar coordinates.)
radfun = @(theta) R+A*sin(F*theta);
fuzzyarea = integral(@(theta) radfun(theta).^2,0,2*pi)
fuzzyarea =
6.291
As expected, the area is almost unchanged, whereas the perimeter is significantly larger than the circle would have been. So how good is this measure of cicularity?
fuzzyperim^2/(4*pi*fuzzyarea)
ans =
5.7903
As it turns out, not very good. For something that you thought would never be larger than 1, the ratio is now almost 6. Really, it applies only for nice well behaved regions. Convexity is actually rather important. If the region is at all fuzzy or not convex, then you need to be careful.
Is there a better measure of circularity? That is a good question. You might compute the perimeter and area of the convex hull of the regions, then use those numbers to compute circularity.
  1 Comment
Steve Eddins
Steve Eddins on 22 Mar 2023
See also this updated answer regarding circularity values that are greater than 1.

Sign in to comment.


Image Analyst
Image Analyst on 10 Jul 2020
Edited: Image Analyst on 10 Jul 2020
With that definition of circularity, which is the one I use, of course it will be more than 1. THeoretically it would be 1 for a circle and greater than 1 for any non-circular shape. So that's normal. But what is unexpected is that the value can be less than 1 for some shapes. When this happens it's because of the way perimeter and area are measured for digitized shapes, and it's worse the smaller the shape is (the more the quantization error is relative to the number of pixels.)
For example, what is the distance between the endpoint 1 pixels in this shape
0 1 1 1 0
From the left 1 to the right 1, what is the distance? Is it 3 because it's 3 pixels long? Or is it 2 because if you go from the center of the left-most pixel to the center of the right-most pixel, that's a distance of 2?
What is the area and perimeter of this shape
0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
Is the area 6? Or is the area 2? Arguments can be made both ways and it could depend on the optics or physics of the situation that gave rise to the image.
What is the perimeter of this shape:
0 0 0 0 0
0 0 1 1 0
0 1 1 1 0
0 0 0 0 0
Is it 5? Or is it 4 + sqrt(2)? When you go diagonally do you consider that a distance of 1 pixel because it's the next pixel over? Or do you consider it sqrt(2) because it's the hypoteneuse of a square that is 1 unit on a side?
Well what does regionprops() and bwarea() say about the area, etc.?
mask = [...
0 0 0 0 0
0 0 1 1 0
0 1 1 1 0
0 0 0 0 0]
props = regionprops(mask, 'Area', 'Perimeter');
area = bwarea(mask);
circularity = props.Perimeter / (4 * pi * props.Area);
fprintf(' bwarea() says the Area = %f\nregionprops() says the Area = %f\nPerimeter = %f\nCircularity = %f\n',...
area, props.Area, props.Perimeter, circularity);
mask =
0 0 0 0 0
0 0 1 1 0
0 1 1 1 0
0 0 0 0 0
bwarea() says the Area = 5.125000
regionprops() says the Area = 5.000000
Perimeter = 4.962000
Circularity = 0.078973
Huh -- how about that?
By the way, there are other definitions of circularity. If you want circularities to go from 0 to 1, just invert your formula so it's (4*pi*Area)/(perimeter^2).
And there are even more definitions: Wikipedia Roundness

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!