Ask Your Question
10

finding centroid of a mask

asked 2012-07-17 17:16:10 -0600

nkint gravatar image

updated 2020-11-28 06:46:42 -0600

hi, i have a mask obtained by threshold function. i wonder if i can find its centroid with builtin function, now i'm doing manual:

    float sumx=0, sumy=0;
    float num_pixel = 0;
    for(int x=0; x<difference.cols; x++) {
        for(int y=0; y<difference.rows; y++) {
            int val = difference.at<uchar>(y,x);
            if( val >= 50) {
                sumx += x;
                sumy += y;
                num_pixel++;
            }
        }
    }
    Point p(sumx/num_pixel, sumy/num_pixel);

but probabilly is there a better way..

edit retag flag offensive close merge delete

3 answers

Sort by ยป oldest newest most voted
10

answered 2012-07-17 17:39:36 -0600

AlexanderShishkov gravatar image

You should use moments() function.

Moments m = moments(difference, false);
Point p1(m.m10/m.m00, m.m01/m.m00);

Code of sample:

int main(int argc, char *argv[])
{
    Mat box = imread("box.png",0);
    Mat difference;
    threshold(box, difference, 200, 255, CV_8UC1);
    float sumx=0, sumy=0;
    float num_pixel = 0;
    for(int x=0; x<difference.cols; x++) {
        for(int y=0; y<difference.rows; y++) {
            int val = difference.at<uchar>(y,x);
            if( val >= 50) {
                sumx += x;
                sumy += y;
                num_pixel++;
            }
        }
    }
    Point p(sumx/num_pixel, sumy/num_pixel);
    cout << Mat(p) << endl;

    Moments m = moments(difference, false);
    Point p1(m.m10/m.m00, m.m01/m.m00);
    cout << Mat(p1) << endl;

    circle(difference, p, 5, Scalar(128,0,0), -1);
    imshow("difff", difference);
    waitKey(0);
    return 0;
}
edit flag offensive delete link more

Comments

hi there!!this code is perfect!!but i cant understand this line if( val >= 50)..val means intensity..why does it need to be 50? thanks

yum gravatar imageyum ( 2013-09-19 09:06:45 -0600 )edit

It is an arbitrary threshold.

Doombot gravatar imageDoombot ( 2014-11-07 10:11:02 -0600 )edit

Which method is faster? Manually computing the center of mass or by using moments? I tried to compute it by iterating over the matrix and the fps of the video stream dropped from 16 to 4.

andunhill gravatar imageandunhill ( 2015-01-05 01:34:50 -0600 )edit

hi sorry for the my English but i'm Italian,First of all congratulations to the code but one thing I'm not going in circle past p1 instead of p? Thanks for the answer previosly!

gabriele92 gravatar imagegabriele92 ( 2015-09-26 04:45:21 -0600 )edit
12

answered 2012-07-18 01:48:33 -0600

Michael Burdinov gravatar image

updated 2012-07-22 02:28:22 -0600

Not only you are calculating moments here but you also perform threshold operation. Both operations can be done by OpenCV. First by function compare() and second by function moments().

Your code will be:

Mat tmp;
compare(difference,Scalar(50),tmp,CMP_GE);
Moments m = moments(tmp,true);
Point p(m.m10/m.m00, m.m01/m.m00);

Note that you should use function moments() with flag 'true', i.e. each non-zero value in image should be treated as '1'. Otherwise moments() will use original values from image for calculation of moments. In this case it will result in factor of 255 for all moments. It is not a problem when you are calculating centroids because when you divide two values this factor will cancel itself, but if you use it for anything else you will get wrong results. For example m.m00 will be 255 times bigger than actual area.

And on other topic, you are traversing pixels of image column by column, intead of row by row. This may incur huge penalty on your time performance (up to order of magnitude in some cases).

Edit: Just thought that this can be done in even cleaner way:

Moments m = moments((difference>=50),true);
Point2d p(m.m10/m.m00, m.m01/m.m00);
edit flag offensive delete link more
2

answered 2013-12-10 02:46:13 -0600

pklab gravatar image

updated 2013-12-10 08:56:19 -0600

You are talking about mask than above code is working fine but generally speaking you should take care about difference between geometric centre and centre of mass.

In geometric centre all points has same weight while in centre of mass each point is weighted with its intensity.

You should note that image moments calculates centre of mass.

For an image mask, geometric and centre of mass are the same because relevant pixels in mask has same intensity but generally speaking this is not true.

here is the code:

// GET GEOMETRIC CENTRE
//---------------------
Mat mask;
compare(difference,Scalar(50),mask,CMP_GE);

// You could use inRange function 
//inRange(difference,Scalar(50),Scalar(255),mask);

//mask is now logical image
Moments m = moments(mask); // true flag is not needed because is a mask
Point geometricCentre(m.m10/m.m00, m.m01/m.m00);

// GET CENTRE OF MASS
//---------------------
Mat tmp(difference.size(),difference.type());
tmp.setTo(0);
difference.copyTo(tmp,mask);  // copy only wanted pixels
//matrix multiplication might be used if mask is 0/1
//Mat tmp = difference.mul(mask);

// tmp is a gray mask now
m = moments(tmp,false);   // avoid true flag for centre of mass
Point massCentre(m.m10/m.m00, m.m01/m.m00);

In addition true flag can be used to calculate geometric center on a gray scale mask

m = moments(tmp,true);   // true flag is needed because is a gray mask
Point geometricCentreAgain(m.m10/m.m00, m.m01/m.m00);
edit flag offensive delete link more

Question Tools

2 followers

Stats

Asked: 2012-07-17 17:16:10 -0600

Seen: 20,379 times

Last updated: Dec 10 '13