Ask Your Question
1

Why do I get different Length and Area for identical objects

asked 2015-10-27 05:29:40 -0600

vitruvius gravatar image

updated 2015-10-27 05:59:12 -0600

Hello,

I would like to compare two objects' area and length. I just simply find the contours and use arcLength() and contourArea() functions but the results are way too different.

Why would that be?

Also, mass center of the upper one seems a little off from the center. What would the reason be?

I see contours are well-drawn, and the shapes are rectangle-ish. Therefore, I don't see what the reason could be.

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace cv;
using namespace std;

Mat src; Mat srcGray;
RNG rng(12345);

int main(int argc, char** argv)
{
    // Load source image and convert it to gray
    src = imread("source.png", 1);

    // Convert image to gray and blur it
    cvtColor(src, srcGray, CV_BGR2GRAY);
    blur(srcGray, srcGray, Size(3, 3));

    Mat cannyOut;
    Canny(srcGray, cannyOut, 187, 187, 3, 1);

    // Find contours
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    findContours(cannyOut, contours, hierarchy, CV_RETR_EXTERNAL, 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);
    }

    // Draw contours
    Mat drawing = Mat::zeros(cannyOut.size(), CV_8UC3);
    for (int i = 0; i< contours.size(); i++)
    {
        Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
        drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
        circle(drawing, mc[i], 4, color, -1, 8, 0);
    }

    for (int i = 0; i < contours.size(); i++)
    {
        cout << "Contour: " << i << " Area: " << contourArea(contours[i]) << " Length: " << arcLength(contours[i], true) << "\n";
    }

    imshow("Contours", drawing);

    waitKey(0);
    return(0);
}

Output:

Contour: 0 Area: 39951.5 Length: 837.124
Contour: 1 Area: 16 Length: 1645.37

Source:

image description

Contours and mass center: image description

edit retag flag offensive close merge delete

Comments

1

Update your code to show how you draw the contours and how you compute center mass, to see if there's any problem with that.

LorenaGdL gravatar imageLorenaGdL ( 2015-10-27 05:39:32 -0600 )edit

@LorenaGdL I forgot to mention that I used tutorials at opencv.org but anyway, I did.

vitruvius gravatar imagevitruvius ( 2015-10-27 06:00:45 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
3

answered 2015-10-27 05:58:42 -0600

theodore gravatar image

that's because from your code the upper contour is open. Look below:

image description

so just modify the canny thresholds in order to get the whole contour ;-)

edit flag offensive delete link more

Comments

Hmm, so would it be safer to draw bounding rotated rectangle boxes around each contour and try to get the length and area from that box? But that wouldn't work for non-regular shapes. Maybe I should dilate the canny output a little bit.

vitruvius gravatar imagevitruvius ( 2015-10-27 06:02:35 -0600 )edit

you could do that as well, Opencv gives you many alternatives just choose the one that fits you best...As I told you, you can just modify your canny thresholds.

theodore gravatar imagetheodore ( 2015-10-27 06:06:28 -0600 )edit

Since I am looking for a general solution I use otsu value to set canny's threshold. Something like this:

Mat srcThresh;
    double otsu;

    otsu = threshold(srcGray, srcThresh, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    cout << "Otsu value: " << otsu << "\n";

    Mat cannyOut;
    Canny(srcGray, cannyOut, otsu, otsu, 3, 1);

The value was 128 in this case, so I deleted that part I typed 128 to shorten the code :)

vitruvius gravatar imagevitruvius ( 2015-10-27 06:09:07 -0600 )edit

But, how does arcLength() work? How does that tiny gap affect the outcome of arcLength() that much? Same for contourArea().

vitruvius gravatar imagevitruvius ( 2015-10-27 06:10:49 -0600 )edit

Okay I have changed my Canny function as:

Mat srcThresh;
    double otsu;

    otsu = threshold(srcGray, srcThresh, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    cout << "Otsu value: " << otsu << "\n";

    Mat cannyOut;
    Canny(srcGray, cannyOut, otsu, otsu*1/2, 3, 1);

Now it is doing fine. Thanks for that. But If you could explain my previous questions, I would very much appreciate it.

vitruvius gravatar imagevitruvius ( 2015-10-27 06:14:11 -0600 )edit

@vitruvius the arcLength() is actually the perimeter of your contour points so if you notice in your initial post the fist contour had lalf the perimeter of the second because it was calculating only the outer part. However the other one was bigger because it was calculating also the inner part due to that gap. For the contour areas again more or less the explanation is the same. In the first case it was calculating the area of a box while in the other case the area of a line in the form of a wanna be box but still just a line.

theodore gravatar imagetheodore ( 2015-10-27 06:21:16 -0600 )edit

Thank you very much @theodore

vitruvius gravatar imagevitruvius ( 2015-10-27 06:25:27 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2015-10-27 05:29:40 -0600

Seen: 585 times

Last updated: Oct 27 '15