Sorry, this content is no longer available

Ask Your Question
10

finding centroid of a mask

asked Jul 17 '12

nkint gravatar image

updated Nov 28 '0

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..

Preview: (hide)

3 answers

Sort by » oldest newest most voted
10

answered Jul 17 '12

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;
}
Preview: (hide)

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 (Sep 19 '13)edit

It is an arbitrary threshold.

Doombot gravatar imageDoombot (Nov 7 '14)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 (Jan 5 '15)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 (Sep 26 '15)edit
12

answered Jul 18 '12

Michael Burdinov gravatar image

updated Jul 22 '12

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);
Preview: (hide)
2

answered Dec 10 '13

pklab gravatar image

updated Dec 10 '13

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);
Preview: (hide)

Question Tools

2 followers

Stats

Asked: Jul 17 '12

Seen: 21,207 times

Last updated: Dec 10 '13