OpenCV 2 中心点

11

我正在尝试寻找轮廓的质心,但在实现C++(OpenCV 2.3.1)中的示例代码时遇到了问题。有人能帮帮我吗?

4个回答

16
使用矩的方法可以找到轮廓的质心。OpenCV中已经实现了相应的函数。
查看这些矩函数(中心矩和空间矩)。
以下代码摘自OpenCV 2.3文档教程。完整代码在此处。
/// 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 ); } 

此外,看看这个SOF,虽然是用Python编写的,但也很有用。它可以找到轮廓的所有参数。

6
如果您有轮廓区域的掩模,可以按如下方式找到质心位置:
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;
}

当我们拥有掩模但没有轮廓时,使用这种方法非常有用。在这种情况下,与使用cv::findContours(...)然后查找质心相比,上述方法在计算效率上更高。

这里是来源


给定一个轮廓,如何获取其面积的掩码? - dinosaur

1

给定轮廓点和维基百科上的公式,可以通过以下方式高效地计算重心:

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
    [...]
}

注意:

  • 此公式适用于顶点按照顺时针或逆时针顺序给出的情况。
  • 如果点的坐标为整数,可以将p和返回值的类型调整为Point2fPoint2d,并在返回语句中分母处添加floatdouble的转换。

0
如果你只需要一个质心的近似值,这里有几种简单的方法可以实现:
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;
}

或者通过使用Opencv的boundingRect函数:

//pseudo-code:

Rect bRect = Imgproc.boundingRect(array_points);

centroid.x = bRect.x + (bRect.width / 2);
centroid.y = bRect.y + (bRect.height / 2);

这并不能找到轮廓的质心。例如,第一个只是角点的平均值。 - Antonio
是的,我应该提到这只是一个近似值。如果你只需要一个粗略的结果,它会很有用。 - Rui Marques

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接