Ask Your Question
0

Problem with getting information on contours

asked 2015-09-10 11:32:51 -0600

unxnut gravatar image

I am writing a function that takes a binary 8-bit image and trying to find the average area of blobs in there. My function is:

void rm_noise(cv::Mat& img, cv::Mat& clean_img)
{
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours ( img, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    if ( contours.empty() )
        return;

    clean_img = cv::Mat::zeros(img.size(), CV_8UC1);
    for (size_t i = 0; i < hierarchy.size(); i++)
    {
        cv::drawContours(clean_img, contours, i, cv::Scalar(0XFF), cv::FILLED, cv::LINE_AA, hierarchy);
    }
    cv::imshow("edges", clean_img);
    cv::waitKey();

    double area = 0;
    //for (std::vector<std::vector<cv::Point>>::const_iterator c = contours.begin(); c != contours.end(); c++)
    for (size_t i = 0; i >= 0; i = hierarchy[i][0])
    {
        const std::vector<cv::Point>& c = contours[i];
        double a = std::fabs(cv::contourArea(cv::Mat(c)));
        area += a;
    }
    double avg_area = area /= contours.size();
}

findContours seems to work correctly as I can see the contours drawn properly using a loop with drawContours. However, when I try to get the information on contours such as contourArea or even bounding box, I get strange results. The results seem to give me contour area as very large (something of the order of 10^19 pixels) which is obviously wrong. And the code seg faults. Can someone help me with this as I have already wasted about five days trying different combinations to no avail.

edit retag flag offensive close merge delete

Comments

1

I've got no error, no seg faults and average area of 7400pixels in my test image using this:

for (size_t i = 0; i < contours.size(); i++)
    {
        const std::vector<cv::Point>& c = contours[i];
        double a = std::fabs(cv::contourArea(cv::Mat(c)));
        area += a;
    }
    double avg_area = area / contours.size();

Have a careful look at the last line! There's an extra = in your code

I also got successful results with this (no need to use an extra variable):

for (size_t i = 0; i < contours.size(); i++)
    {
        double a = std::fabs(cv::contourArea(contours[i]));
        area += a;
    }
    double avg_area = area / contours.size();
LorenaGdL gravatar imageLorenaGdL ( 2015-09-10 13:01:37 -0600 )edit

/= is not an error. However, in my 640x480 image, I get 71 contours. As I go through the contours, the first one has 4 points and an area of 56 (reasonable), the second one has 516 points and an area of 5.69e+19 (not right) and the third one has 120195045 points and issues an exception when computing area. I am perplexed.

unxnut gravatar imageunxnut ( 2015-09-10 13:25:02 -0600 )edit

I'll be happy to test your image and see if I can find anything wrong with it. Btw, which version are you using?

LorenaGdL gravatar imageLorenaGdL ( 2015-09-10 13:27:50 -0600 )edit

I have the image here. I am using OpenCV 3.0 on Visual Studio 2013.

unxnut gravatar imageunxnut ( 2015-09-10 15:20:01 -0600 )edit

can you please post your image? (and maybe also a plot of the unfilled contours detected by OpenCV)

As per the documentation, contourArea does not actually count pixels, it uses a green formula which may give nonsensical results for contours that are not well behaved.

Incidentally, you don't need the std::fabs, contourArea has an optional flag for returning signed or unsigned area. The default is unsigned.

guy

Guyygarty gravatar imageGuyygarty ( 2015-09-10 15:26:54 -0600 )edit
1

This is what I use and I've never had a problem with weird areas:

vector<cv::Point> contour_poly;
approxPolyDP( Mat(contours[i]), contour_poly, 3, true );
Area=contourArea(contour_poly);

guy

Guyygarty gravatar imageGuyygarty ( 2015-09-10 15:39:28 -0600 )edit

@unxnut: using the provided image I get 2 contours corresponding to the 2 white blobs. But, important thing is that your image is in .jpg format, so due to compression issues your image is not longer binary - some pixels which should be 255 are now 254 or 253, those which should be 0 can take values of 1, 2 or 5 maybe. Therefore, findContours doesn't run as expected. Just put threshold(image, image, 128, 255, CV_THRESH_BINARY); to binarize image before calling your function.

LorenaGdL gravatar imageLorenaGdL ( 2015-09-10 16:24:00 -0600 )edit
2

@unxnut: extra comment, if you just want to compute average area, you might find useful the new 3.0 function connectedComponentsWithStats:

Mat image = imread("img.jpg",0);
threshold(image, image, 128, 255, CV_THRESH_BINARY);
Mat labels, stats, centroids;
connectedComponentsWithStats(image, labels, stats, centroids);
double area = 0;
for (int i = 1; i < stats.rows; i++){       //Start from i=1 to avoid background label
    area += stats.at<__int32>(i, CC_STAT_AREA);
}
double avg_area = area /= stats.rows;
LorenaGdL gravatar imageLorenaGdL ( 2015-09-10 16:25:32 -0600 )edit

approxPoly didn't help either :-(

unxnut gravatar imageunxnut ( 2015-09-10 16:28:19 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
3

answered 2015-09-17 15:18:17 -0600

unxnut gravatar image

I feel obligated to answer my question as I figured out the problem after a lot of wasted time. I had originally compiled OpenCV using Visual Studio 2010 and had been working fine. About a month ago, I needed to develop something using C++-11 features due to a collaboration and so, I installed Visual 2013 but did not recompile OpenCV to work with 2013. Things had been working fine till I encountered this issue with findContours even though I did not use any C++-11 features in this code. I compiled my code using 2010 and things worked as expected. Lesson learned: do not mix code from different versions of Visual Studio, even on the same machine. Sorry to waste your time.

edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2015-09-10 11:32:51 -0600

Seen: 2,191 times

Last updated: Sep 17 '15