Kadek Dwi Budi Utama Kadek Dwi Budi Utama - 22 days ago 7
Python Question

How to Get Object Centroid just from Object Boundary?

I have 2 segmented images. One is otsu binary, and the other is outer boundary obtains from otsu's.

Otsu binary:

Otsu binary

Object boundary:

Object boundary

And code to find centroids like this:

def getCentroid(img):
row, col = img.shape

xVal = 0
yVal = 0
n = 0.0

for x in range(0,row):
for y in range(0,col):
if (img[x,y] == 0):
xVal += x
yVal += y
n += 1.0

xVal /= n
yVal /= n

return [np.int64(np.round(xVal)),np.int64(np.round(yVal))]


But, it seems the code only works with otsu's. Here are the results:

Otsu binary:

Otsu binary

Object boundary:

Object boundary

So, how to find object centroid from object boundary?

Note: I don't want to use built-in function, cause i want to learn how the algorithm works.

Answer

The mistake is quite simple. Your algorithm processes only black pixels, due to this condition: if (img[x,y] == 0):. Your image of the boundary contains a large proportion of black pixels. It is safe to assume that the centroid of all the black pixels in such image would be very close to the center of the image.

The image in question is 402 pixels wide, and 302 pixels high. Your algorithm gives us (x=201, y=151) as the centroid -- this matches the expectation.

To correctly process the object boundary, we need to make the boundary black, and everything else white. This is simple, just invert the image (e.g. 255 - img). Then your algorithm returns (x=165, y=123), which makes a lot more sense.

Code

import cv2
import numpy as np

def getCentroid(img):
   row, col = img.shape

   xVal = 0
   yVal = 0
   n = 0.0

   for x in range(0,row):
      for y in range(0,col):
         if (img[x,y] == 0):
            xVal += x
            yVal += y
            n += 1.0

   xVal /= n
   yVal /= n

   return [np.int64(np.round(xVal)),np.int64(np.round(yVal))]

a = cv2.imread('cnt.png', 0)

c1 = getCentroid(a) # Original
c2 = getCentroid(255 - a) # Inverse

b = cv2.cvtColor(a, cv2.COLOR_GRAY2BGR)
cv2.circle(b, (c1[1], c1[0]), 3, (0,0,255))
cv2.circle(b, (c2[1], c2[0]), 3, (0,255,0))

cv2.imwrite('cnt_out.png', b)

Sample output

Note: Red is the centroid from original image, Green from the inverted image.

Sample output image