OpenCV 2 Centroid
Asked Answered
E

4

11

I am trying to find the centroid of a contour but am having trouble implementing the example code in C++ (OpenCV 2.3.1). Can anyone help me out?

Egbert answered 31/1, 2012 at 4:3 Comment(0)
J
16

To find the centroid of a contour, you can use the method of moments. And functions are implemented OpenCV.

Check out these moments function (central and spatial moments).

Below code is taken from OpenCV 2.3 docs tutorial. Full code here.


/// Find contours
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_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 mass centers:
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 ); } 

Also check out this SOF, although it is in Python, it would be useful. It finds all parameters of a contour.

Justification answered 31/1, 2012 at 5:18 Comment(0)
H
6

If you have the mask of the contour area, you can find the centroid location as follows:

cv::Point computeCentroid(const cv::Mat &mask) {
    cv::Moments m = moments(mask, true);
    cv::Point center(m.m10/m.m00, m.m01/m.m00);
    return center;
}

This approach is useful when one has the mask but not the contour. In that case the above method is computationally more efficient vs. using cv::findContours(...) and then finding mass center.

Here's the source

Hulsey answered 4/3, 2013 at 19:12 Comment(1)
how do you get a mask of the contour area, given a Contour?Westphal
B
1

Given the contour points, and the formula from Wikipedia, the centroid can be efficiently computed like this:

template <typename T> 
cv::Point_<T> computeCentroid(const std::vector<cv::Point_<T> >& in) {
    if (in.size() > 2) {
         T doubleArea = 0;
         cv::Point_<T> p(0,0);
         cv::Point_<T> p0 = in->back();
         for (const cv::Point_<T>& p1 : in) {//C++11
             T a = p0.x * p1.y - p0.y * p1.x; //cross product, (signed) double area of triangle of vertices (origin,p0,p1)
             p += (p0 + p1) * a;
             doubleArea += a;
             p0 = p1;
         }

         if (doubleArea != 0)
             return p * (1 / (3 * doubleArea) ); //Operator / does not exist for cv::Point
    }

    ///If we get here,
    ///All points lies on one line, you can compute a fallback value,
    ///e.g. the average of the input vertices
    [...]
}

Note:

  • This formula works with vertices given both in clockwise and counterclockwise order.
  • If the points have integer coordinates, it might be convenient to adapt the type of p and of the return value to Point2f or Point2d, and to add a cast to float or double to the denominator in the return statement.
Bile answered 9/12, 2015 at 16:12 Comment(0)
H
0

If all you need is an approximation of the centroid here are a couple of simple ways to do it:

sumX = 0; sumY = 0;
size = array_points.size;
if(size > 0){

    foreach(point in array_points){
        sumX += point.x;
        sumY += point.y;
    }
            
 centroid.x = sumX/size;
 centroid.y = sumY/size;
}

Or with the help of Opencv's boundingRect:

//pseudo-code:

Rect bRect = Imgproc.boundingRect(array_points);

centroid.x = bRect.x + (bRect.width / 2);
centroid.y = bRect.y + (bRect.height / 2);
Hypochondriac answered 15/12, 2012 at 0:7 Comment(2)
This doesn't find the centroid of a contour. The first, for example, is just an average of the corner points.Bile
Yes I should have mentioned it is an aproximation. Useful if you only need a rough result.Hypochondriac

© 2022 - 2024 — McMap. All rights reserved.