Ask Your Question
1

Compute moments after connected component labeling

asked 2016-02-11 14:39:51 -0600

CassioPereira gravatar image

Hi,

I'm trying to compute the moments of the pixels found in a connected component of a gray scale image.

I have in a matrix cv::Mat contMat a gray scale image. After thresholding it to another matrix binImg, I found its connected components with cv::connectedComponents(binImg, lblImg, 8); I went ahead and created a vector<cv::Point> points with the points from a specific connected component, let's say the ones labeled 1, by traversing lblImg and adding the points from contMat that have label 1 in lblImg.

Now I want to compute the image moments from those points in the contMat matrix using cv::moments(points, false), but that's not really working, because it seems moments expects a contour.

I'm wondering what's the best way to do this using OpenCV 3.1 in C++.

Thanks.

edit retag flag offensive close merge delete

Comments

It's not an opencv answer but may be you could use it

LBerger gravatar imageLBerger ( 2016-02-12 03:32:37 -0600 )edit

1 answer

Sort by » oldest newest most voted
3

answered 2016-02-12 02:54:25 -0600

pklab gravatar image

What do you mean for not really working ? cv::moments works with ...raster image (single-channel, 8-bit or floating-point 2D array) or an array (1×N or N×1 ) of 2D points (Point or Point2f ) and returns moments

You should to be sure of your points values correctness even if ...the moments computed for a contour are slightly different from the moments computed for the same rasterized contour

BTW, You can extract just the image from the i-th label and use it to calculate moment...as below

Mat src,contMat, binImg, lblImg,objImg;
src = imread("../img/test.jpg");
cvtColor(src, contMat, CV_BGR2GRAY);
cv::threshold(contMat, binImg, 50, 255, THRESH_BINARY);

int numLabels=connectedComponents(binImg, lblImg, 8);

// select one-by-one ALL labelled objects using its label values
for (int lbl = 0; lbl < numLabels; lbl++) {
    // select the image part relative to lbl
    objImg = (lblImg == lbl);              
    Moments m = cv::moments(objImg, true); // get moments as world coordinates
    // get and draw the center
    // remember m.m00 could be 0 if object has self-intersections
    if (m.m00 != 0) {
        Point center;
        center.x = m.m10 / m.m00;
        center.y = m.m01 / m.m00;
        cv::circle(contMat, center, 3, Scalar(0, 255, 0), -1);
    }
}

As alternative, you can do the same using your points vector

//get the label value related to current vector points
int lbl = lblImg.at<uchar>(points[0]);
// select the image part relative to lbl as above
objImg = (lblImg == lbl);
...same as above

3rd way optmize memory usage using your points to create a ROI around the i-th object

int lbl = lblImg.at<uchar>(points[0]); // get the label value
Rect objRect = boundingRect(points);
Mat objROI = lblImg(objRect);          // get a ROI for lbl-th object
objROI = (objROI == lbl);              // select pixel from lbl-th label only
Moments m = cv::moments(objImg, true); // get moments as ROI coordinates
if (m.m00 != 0) {
    Point center;
    center.x = m.m10 / m.m00;
    center.y = m.m01 / m.m00;
    // back to world coordinates
    center += objRect.tl;
    cv::circle(contMat, center, 3, Scalar(0, 255, 0), -1);
}
edit flag offensive delete link more

Comments

I need to compute skewness (3rd order moment) for the region in the contMat matrix. Your solution helped me. I want to use pixel intensities, so I need false in the moments call. But I think cv::Rect r = cv::boundingRect(lblImg == 1);cv::Moments moments = cv::moments(contMat(r), false); achieves what I wanted.

CassioPereira gravatar imageCassioPereira ( 2016-02-12 10:11:08 -0600 )edit

Yes it does if you clean all !=lbl. The boundingRect can include some pixels from another label... it depends from object separation/shape. For example (- are 2 objects but their boundingRect overlapping. While ( - have good separation without overlapping.

You could use copy with mask to select all pixel from gray images related to label l. Create a black background cv::Mat grayObj=cv::zeros(r,CV_8UC1); and get gray pixels only from the object contMat(r).copyTo(grayObj, objROI(r));

pklab gravatar imagepklab ( 2016-02-12 11:52:14 -0600 )edit

I see your point, but isn't contMat(r).copyTo(grayObj, objROI(r)); just using the bounding rectangle, which can include points from other labels? How are you creating objROI(r) from the lblImg matrix in this scenario?

CassioPereira gravatar imageCassioPereira ( 2016-02-12 12:03:39 -0600 )edit

Sorry should be Mat objMask = (lblImg(r)==lbl); than contMat(r).copyTo(grayObj, objMask);

pklab gravatar imagepklab ( 2016-02-12 13:44:14 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2016-02-11 14:39:51 -0600

Seen: 4,361 times

Last updated: Feb 12 '16