Центроид OpenCV 2

Я пытаюсь найти центр тяжести контура, но у меня возникли проблемы с реализацией примера кода на С++ (OpenCV 2.3.1). Может кто-нибудь мне помочь?


person keshavdv    schedule 31.01.2012    source источник


Ответы (4)


Чтобы найти центроид контура, вы можете использовать метод моментов. А функции реализованы 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, хотя он находится в Питон, было бы полезно. Он находит все параметры контура.

person Abid Rahman K    schedule 31.01.2012

Если у вас есть маска области контура, вы можете найти местоположение центроида следующим образом:

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(...) и последующим поиском центра масс.

Вот источник

person Alexey    schedule 04.03.2013
comment
как получить маску области контура, учитывая контур? - person dinosaur; 08.07.2016

Вы также можете использовать следующие алгоритмы для поиска центроида:

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;
}

Или с помощью boundingRect Opencv:

//pseudo-code:

Rect bRect = Imgproc.boundingRect(array_points);

centroid.x = bRect.x + (bRect.width / 2);
centroid.y = bRect.y + (bRect.height / 2);
person Rui Marques    schedule 15.12.2012
comment
Это не находит центроид контура. Первый, например, представляет собой просто среднее значение угловых точек. - person Antonio; 09.12.2015
comment
Да, я должен был упомянуть, что это приближение. Полезно, если вам нужен только грубый результат. - person Rui Marques; 09.12.2015

Учитывая точки контура и формулу из Википедии, центроид можно эффективно вычислить следующим образом. :

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 и возвращаемого значения к Point2f или Point2d, а также добавить приведение к float или double к знаменателю в операторе return.
person Antonio    schedule 09.12.2015