is there some good and better way to find centroid of contour in opencv, without using built in functions?
-
If you have a bunch of points(2d vectors), you should be able to get the centroid by averaging those points: create a point to add all the other points' positions into and then divide the components of that point with accumulated positions by the total number of points. I've just written a basic illustration [here](http://hascanvas.com/contourCentroid). Not entirely sure how well it works with convex hulls though. – George Profenza Nov 15 '11 at 22:26
3 Answers
While Sonaten's answer is perfectly correct, there is a simple way to do it: Use the dedicated opencv function for that: moments()
It does not only returns the centroid, but some more statistics about your shape. And you can send it a contour or a raster shape (binary image), whatever best fits your need.
EDIT
example (modified) from "Learning OpenCV", by gary bradsky
CvMoments moments;
double M00, M01, M10;
cvMoments(contour,&moments);
M00 = cvGetSpatialMoment(&moments,0,0);
M10 = cvGetSpatialMoment(&moments,1,0);
M01 = cvGetSpatialMoment(&moments,0,1);
centers[i].x = (int)(M10/M00);
centers[i].y = (int)(M01/M00);

- 19,708
- 4
- 59
- 82
-
thanks, i have tried to do it, using following code, for( ; contours != 0; contours = contours->h_next ) {CvMoments *moments; cvMoments(cc_color, moments, 1); double M01 = cvGetSpatialMoment(moments, 0, 1); double M00 = cvGetSpatialMoment(moments, 0, 0); double yc = M01 / M00; } but it still gives errors.. – Ayesha Khan Nov 16 '11 at 08:43
-
Isn't it supposed to first alloc some space for *moments? Or better use the C++ syntax, it manags the memory fot you. I do not remember the constructor for cvMoments – Sam Nov 16 '11 at 09:39
-
i have even put CvMoments* moments = (CvMoments*) malloc(sizeof(CvMoments)); but it still crashes. – Ayesha Khan Nov 16 '11 at 10:38
-
-
Now, I have done: for( ; contours != 0; contours = contours->h_next ) {std::vector
vec(2); CvMoments * myMoments = (CvMoments*)malloc( sizeof(CvMoments) ); cvMoments( contours, myMoments ); vec[0]=(double) myMoments->m10/myMoments->m00; vec[1]=(double) myMoments->m01/myMoments->m00; } is it the correct method? – Ayesha Khan Nov 16 '11 at 11:08
What you get in your current piece of code is of course the centroid of your bounding box.
"If you have a bunch of points(2d vectors), you should be able to get the centroid by averaging those points: create a point to add all the other points' positions into and then divide the components of that point with accumulated positions by the total number of points." - George Profenza mentions
This is indeed the right approach for the exact centroid of any given object in two-dimentionalspace.
On wikipedia we have some general forms for finding the centroid of an object. http://en.wikipedia.org/wiki/Centroid
Personally, I would ask myself what I needed from this program. Do I want a thorough but performance heavy operation, or do I want to make some approximations? I might even be able to find an OpenCV function that deals with this correct and efficiently.
Don't have a working example, so I'm writing this in pseudocode on a simple 5 pixel example on a thorough method.
x_centroid = (pixel1_x + pixel2_x + pixel3_x + pixel4_x +pixel5_x)/5
y_centroid = (pixel1_y + pixel2_y + pixel3_y + pixel4_y +pixel5_y)/5
centroidPoint(x_centroid, y_centroid)
Looped for x pixels
Loop j times *sample (for (int i=0, i < j, i++))*
{
x_centroid = pixel[j]_x + x_centroid
y_centroid = pixel[j]_x + x_centroid
}
x_centroid = x_centroid/j
y_centroid = y_centroid/j
centroidPoint(x_centroid, y_centroid)
Essentially, you have the vector contours of the type
vector<vector<point>>
in OpenCV 2.3. I believe you have something similar in earlier versions, and you should be able to go through each blob on your picture with the first index of this "double vector", and go through each pixel in the inner vector.
Here is a link to documentation on the contour function http://opencv.itseez.com/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=contours#cv.DrawContours
note: you've tagged your question as c++ visual. I'd suggest that you use the c++ syntax in OpenCV 2.3 instead of c. The first and good reason to use 2.3 is that it is more class based, which in this case means that the class Mat (instead of IplImage) does leak memory. One does not have to write destroy commands all the live long day :)
I hope this shed some light on your problem. Enjoy.

- 502
- 2
- 14
I've used Joseph O'Rourke excellent polygon centroid algorithm to great success.
See http://maven.smith.edu/~orourke/Code/centroid.c
Essentially:
- For each point in the contour, find the triangle area from the current index polygon xy to the next 2 polygon xy points e.g.: Math.Abs(((X1 - X0) * (Y2 - Y0) - (X2 - X0) * (Y1 - Y0)) / 2)
- Add this triangle area to a list TriAreas
- Sum the triangle area, and store in SumT
- Find the centroid CTx and CTy from this current triangle: CTx = (X0 + X1 + X2) / 3 and CTy = (Y0 + Y1 + Y2) / 3;
- Store these 2 centroid values in 2 other lists CTxs CTys.
Finally after performing this with all points in the contour, find the contours centroid x and y using the 2 triangle x and y lists in 5 which is a weighted sum of signed triangle areas, weighted by the centroid of each triangle:
for (Int32 Index = 0; Index < CTxs.Count; Index++) { CentroidPointRet.X += CTxs[Index] * (TriAreas[Index] / SumT); } // now find centroid Y value for (Int32 Index = 0; Index < CTys.Count; Index++) { CentroidPointRet.Y += CTys[Index] * (TriAreas[Index] / SumT); }

- 3,689
- 5
- 28
- 45