In middle school, we learned about various shapes in geometry. It was relatively easy to find the centers of standard shapes like the circle, square, triangle, ellipse, etc.

But when it came to finding the centroid of an arbitrary shape, the methods were not straightforward. Some nerdy friends said it would require calculus. Other practical friends suggested intersecting plumblines.

The same problem of finding centroid is relevant when you work in Computer Vision — except, you are dealing with pixels instead of atoms! In this post, we will first discuss how to find the center of an arbitrarily shaped blob and then we will move to the case of multiple blobs.

## What is a blob?

A blob is a group of connected pixels in an image that shares some common property ( e.g grayscale value ). In this post, our goal is to find the center of a binary blob using OpenCV in Python and C++. If the shape we are interested in is not binary, we have to binarize it first.

## What is the centroid of a shape?

The centroid of a shape is the arithmetic mean (i.e. the average) of all the points in a shape. Suppose a shape consists of distinct points , then the centroid is given by

In the context of image processing and computer vision, each shape is made of pixels, and the centroid is simply the weighted average of all the pixels constituting the shape.

## Image Moments

We can find the center of the blob using **moments** in OpenCV. But first of all, we should know what exactly **Image moment** is all about. Image Moment is a particular weighted average of image pixel intensities, with the help of which we can find some specific properties of an image, like radius, area, centroid etc. To find the centroid of the image, we generally convert it to binary format and then find its center.

The centroid is given by the formula:-

is the x coordinate and is the y coordinate of the centroid and denotes the Moment.

## Steps for finding Centroid of a Blob in OpenCV

To find the center of the blob, we will perform the following steps:-

1. Convert the Image to grayscale.

2. Perform Binarization on the Image.

3. Find the center of the image after calculating the moments.

### Center of a single blob in an image :

**Download Code**

To easily follow along with this tutorial, please download code by clicking on the button below. It’s FREE!

### C++ Code

// declare Mat variables, thr, gray and src Mat thr, gray, src; // convert image to grayscale cvtColor( src, gray, COLOR_BGR2GRAY ); // convert grayscale to binary image threshold( gray, thr, 100,255,THRESH_BINARY ); // find moments of the image Moments m = moments(thr,true); Point p(m.m10/m.m00, m.m01/m.m00); // coordinates of centroid cout<< Mat(p)<< endl; // show the image with a point mark at the centroid circle(src, p, 5, Scalar(128,0,0), -1); imshow("Image with center",src); waitKey(0);

### Python Code

# convert image to grayscale image gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # convert the grayscale image to binary image ret,thresh = cv2.threshold(gray_image,127,255,0) # calculate moments of binary image M = cv2.moments(thresh) # calculate x,y coordinate of center cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) # put text and highlight the center cv2.circle(img, (cX, cY), 5, (255, 255, 255), -1) cv2.putText(img, "centroid", (cX - 25, cY - 25),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2) # display the image cv2.imshow("Image", img) cv2.waitKey(0)

The figure below shows the center of a single blob in an Image.

### Center of multiple blobs in an Image

Finding the center of only one blob is quite easy, but what if there are multiple blobs in the Image? Well then, we will have to use **findContours** to find the number of contours in the Image and find the center of each of them. Let us see how it works!

### C++ Code

Mat canny_output; vector<vector<Point> > contours; vector<Vec4i> hierarchy; // detect edges using canny Canny( gray, canny_output, 50, 150, 3 ); // find contours findContours( canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) ); // get the moments vector<Moments> mu(contours.size()); for( int i = 0; i<contours.size(); i++ ) { mu[i] = moments( contours[i], false ); } // get the centroid of figures. vector<Point2f> mc(contours.size()); for( int i = 0; i<contours.size(); i++) { mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); } // draw contours Mat drawing(canny_output.size(), CV_8UC3, Scalar(255,255,255)); for( int i = 0; i<contours.size(); i++ ) { Scalar color = Scalar(167,151,0); // B G R values drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point()); circle( drawing, mc[i], 4, color, -1, 8, 0 ); } // show the resultant image namedWindow( "Contours", WINDOW_AUTOSIZE ); imshow( "Contours", drawing ); waitKey(0);

### Python Code

# read image through command line img = cv2.imread(args["ipimage"]) # convert the image to grayscale gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # convert the grayscale image to binary image ret,thresh = cv2.threshold(gray_image,127,255,0) # find contours in the binary image im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) for c in contours: # calculate moments for each contour M = cv2.moments(c) # calculate x,y coordinate of center cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) cv2.circle(img, (cX, cY), 5, (255, 255, 255), -1) cv2.putText(img, "centroid", (cX - 25, cY - 25),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2) # display the image cv2.imshow("Image", img) cv2.waitKey(0)

Observe that, while finding the center in case of multiple blobs, we use the function **findContours**, which outputs contours and hierarchy, where the contour is the list of all the contours present in the image.

**Error Message**

After running python code for multiple blobs in an Image, you may get

**cX = int(M[“m10”] / M[“m00”])**

**ZeroDivisionError: float division by zero**

The above error occurs in case If M[“m00”] is zero, i.e when segmentation did not happen perfectly.

### Solution:-

You can include, the below code snippet to prevent getting errors, this simply neglects the contours which are not segmented properly.

if M["m00"] != 0: cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) else: cX, cY = 0, 0

### References:-

1.https://docs.opencv.org/3.1.0/dd/d49/tutorial_py_contour_features.html

## Subscribe & Download Code

If you liked this article and would like to download code (C++ and Python) and example images used in this post, please subscribe to our newsletter. You will also receive a free Computer Vision Resource Guide. In our newsletter, we share OpenCV tutorials and examples written in C++/Python, and Computer Vision and Machine Learning algorithms and news.

Amit says

If you could do an article on moments it would be great. High order moments are hard to grasp.

Plus, centroid formula mentioned initially seems to be a typo mistake.

Masque du Furet says

I agree : cy = M01/M00 would be better (but is typo hunting a referendum matter); this is confirmed by wikipedia (image moments) and by the python and c++ codes;

BTW : wikipedia -if you do not live in Turkey- can be of some help and improve w/r image processing (google search wikipedia + image + moment and you ll get some intersting links and theory; within 3-4 years, results are likely to be even better)

Satya Mallick says

Thanks Amit & Masque. The typo is fixed.

Masque du Furet says

I am somewhat sorry : typo was very easy to detect from consistency between two codes and text (or from symetry), and typo hunting is very unpleasant for authors -takes days- .

I know it is somewhat felt rude to link with wikipedia, but extra needs can be dealt with/answered from their links

羅宇廷 says

Hello,

How find the maximum-area-rectangle inside a convex polygon?

Thank you!