Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Computing the circularity of a binary image

I am trying to compute the circularity of a given binary image. After some research its clear to me that the formula for circularity is

4π*area/perimeter^2

Which should range from 0 to 1, 1 being most circular.

Given the binary matrix im

Computing the area is trivial

area = sum(im)

I am computing the perimeter following this rule: A pixel is part of the perimeter if it is nonzero and it is connected to at least one zero-valued pixel

per = matrix(0, nrow(im), ncol(im))
for(i in 2:(nrow(im)-1)){
  for(j in 2:(ncol(im)-1)){
    if(im[i,j] != 0){
      x=c(im[i-1,j],im[i+1,j],im[i,j-1], im[i,j+1])
      if(0 %in% x) per[i,j] = 1
    }
  }
}
perimeter = sum(per)

Then i compute circularity like this:

circ = (4*pi*area)/(perimiter^2)

However, I get values larger than 1 sometimes and things dont add up. For example:

this image gives me circ=1.155119

enter image description here

and this image gives me circ=1.148728

enter image description here

Any idea whats going on? Shouldn't the values be more like 0.95 and 0.7

like image 347
Omar Wagih Avatar asked Apr 09 '13 21:04

Omar Wagih


People also ask

How do you calculate circularity?

A common definition used in digital image processing (image analysis) for characterizing 2-D shapes is: Circularity = Perimeter2/4π × Area. This ratio will be 1 for a circle and greater than 1 for non-circular shapes.

What is circularity in image processing?

The circularity measure is used extensively in image analysis to sort or identify objects. It has also been used to analyze the gerrymandering of legislative districts and proposed as a way to zone irregular land parcels.

How do you find the circularity in Matlab?

Direct link to this answer allPerimeters = [props. Perimeter]; circularity = (allPerimeters . ^ 2) ./ (4 * pi * allAreas);

What is binary images in image processing?

A binary image is one that consists of pixels that can have one of exactly two colors, usually black and white. Binary images are also called bi-level or two-level, Pixelart made of two colours is often referred to as 1-Bit or 1bit. This means that each pixel is stored as a single bit—i.e., a 0 or 1.


2 Answers

Your definition for the "binary perimeter" is not a good approximation of the smooth perimeter.

# Sample data
n <- 100
im <- matrix(0, 3*n, 3*n+1)
x <- ( col(im) - 1.5*n ) / n
y <- ( row(im) - 1.5*n ) / n
im[ x^2 + y^2 <= 1 ] <- 1
image(im)

# Shift the image in one direction
s1 <- function(z) cbind(rep(0,nrow(z)), z[,-ncol(z)] )
s2 <- function(z) cbind(z[,-1], rep(0,nrow(z)) )
s3 <- function(z) rbind(rep(0,ncol(z)), z[-nrow(z),] )
s4 <- function(z) rbind(z[-1,], rep(0,ncol(z)) )

# Area, perimeter and circularity
area <- function(z) sum(z)
perimeter <- function(z) sum( z != 0 & s1(z)*s2(z)*s3(z)*s4(z) == 0)
circularity <- function(z) 4*pi*area(z) / perimeter(z)^2

circularity(im)
# [1] 1.241127

area(im)
# [1] 31417
n^2*pi
# [1] 31415.93

perimeter(im)
# [1] 564
2*pi*n
# [1] 628.3185

One worrying feature is that this perimeter is not rotation-invariant: when you rotate a square of side 1 (with sides parallel to the axes) by 45 degrees, its area remains the same, but its perimeter is divided by sqrt(2)...

square1 <- -1 <= x & x <= 1 & -1 <= y & y <= 1
c( perimeter(square1), area(square1) )
# [1]   800 40401

square2 <- abs(x) + abs(y) <= sqrt(2)
c( perimeter(square2), area(square2) )
# [1]   564 40045

Here is a slightly better approximation of the perimeter. For each point on the perimeter, look at which points in its 8-neighbourhood are also in the perimeter; if they form a vertical or horizontal segment, the contribution of the pair to the perimeter is 1, if they are in diagonal, the contribution is sqrt(2).

edge <- function(z) z & !(s1(z)&s2(z)&s3(z)&s4(z))
perimeter <- function(z) {
  e <- edge(z)
  ( 
    # horizontal and vertical segments
    sum( e & s1(e) ) + sum( e & s2(e) ) + sum( e & s3(e) ) + sum( e & s4(e) ) + 
    # diagonal segments
    sqrt(2)*( sum(e & s1(s3(e))) + sum(e & s1(s4(e))) + sum(e & s2(s3(e))) + sum(e & s2(s4(e))) )
  ) / 2  # Each segment was counted twice, once for each end
}

perimeter(im)
# [1] 661.7544
c( perimeter(square1), area(square1) )
# [1]   805.6569 40401.0000
c( perimeter(square2), area(square2) )
# [1]   797.6164 40045.0000

circularity(im)
# [1] 0.9015315
circularity(square1)
# [1] 0.7821711
circularity(square2)
# [1] 0.7909881
like image 195
Vincent Zoonekynd Avatar answered Sep 24 '22 04:09

Vincent Zoonekynd


Let me suggest a different algorithm.

You should be able to get the area of the blob with a high degree of accuracy, just by counting the pixels. You can also find the center by taking an average of the coordinates of each inside pixel. Now you can find the radius with sqrt(area/pi). With the radius and the center you can draw a perfect circle that has nearly the same area - count the number of pixels that are part of both the blob and the perfect circle, and divide by the area calculated earlier.

like image 36
Mark Ransom Avatar answered Sep 21 '22 04:09

Mark Ransom