Ask Your Question

Does findContours create duplicates?

asked 2016-07-30 11:33:26 -0500

Izzy88 gravatar image

Hi, I'm considering a binary image from which I extract its edges using cv::Canny. Consequently, I perform cv::findContours, storing all the contours points coordinates in a

vector < vector < Point > >


I noticed that the number of pixels (Points) in this structure is greater than the number of pixels I get by computing

vector<point> white_pixels;
findNonZero(silhouette, white_pixels);

on the same image.

Therefore I'm wondering if this happens because findContours includes duplicate points in its result or because findNonZero is less precise.

E.g. on a 200x200 sample image with the first method I get 1552 points while with the second I get 877.

In case the first hypothesis is correct, is there a way to either ignore the duplicates or remove them?

edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted

answered 2016-07-31 02:30:56 -0500

matman gravatar image

In case of a Canny edge image findContours will find most of the contour points twice, because the line width is only 1 and findContours is looking for closed contours.

To remove duplicates you can map the contour points to an empty (zero) image. For every contour point add 1 at the image position and only copy the contour point to your new contour vector if the value at this position is zero. That would be the easiest way I think.

edit flag offensive delete link more


Thanks! Actually, I did what you suggested using the same sample image and also counting how many pixels were set to 1 (without repetitions). I ended up with having exactly 877 white pixels in the new Mat::zeros image. At this point, i think the result I get by using "findNonZero" is correct, accurate and in this case more efficient since I can avoid the double for loop I used for mapping the contour points for this test.

Izzy88 gravatar imageIzzy88 ( 2016-07-31 05:32:11 -0500 )edit

answered 2016-07-31 10:54:44 -0500

LBerger gravatar image

My first answer was wrong. @matman answer is good but I think problem is in blurred image.

If you don't blur enough image you will have many double points. You shape become a line but at beginning your shape could be a rectangle.

if you find double point in your contour may be you have to increase blurring (shape like 8 could be a problem). For fractal shape (or with a large rugosity) it is difficult to delete those points.

In this example I use : surface rectangle 6 (width 3 and height 2)

canny witout blurring Image 0

canny with blur size 3 image 1

Canny with blur size 5 image 2

image description

Source file

    Mat x = Mat::zeros(20,20,CV_8UC1);
    Mat result = Mat::zeros(20,20,CV_8UC3);
    vector<Vec3b> c = { Vec3b(255, 0, 0), Vec3b(0,255,0),Vec3b(0,0,255),Vec3b(255, 255, 0),Vec3b(255, 0, 255),Vec3b(0, 255, 255) };
    for (int i=9;i<=10;i++)
        for (int j = 9; j <= 11; j++)
    Mat idx;
    cout << "Square surface " << idx.rows<<endl;
    vector<vector<vector<Point> >> contours(3);
    vector<Vec4i> hierarchy;
    double thresh=1;
    int aperture_size=3;
    vector<Mat> xx(3);
    vector<Mat> dst(3);
    for (size_t i = 0; i < xx.size(); i++)
        if (i==0)
            blur(x, xx[i], Size(static_cast<int>(2*i+1),static_cast<int>(2*i+1)));
        Canny(xx[i], dst[i],thresh, thresh, 3,true );
        findContours(dst[i],contours[i], hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE, Point(0, 0));

    namedWindow("original image",WINDOW_NORMAL);
    imshow("original image",x);
    Mat dx,dy,g;
    Sobel(x, dx, CV_16S, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE);
    Sobel(x, dy, CV_16S, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE);
    namedWindow("gradient modulus",WINDOW_NORMAL);
    g = dx.mul(dx) + dy.mul(dy);
    imshow("gradient modulus",g);
    findContours(x,contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE, Point(0, 0));
    cout << "#contours : " << contours.size()<<endl;
    if (contours.size() > 0)
        for (size_t i = 0; i < contours.size(); i++)
            cout << "#pixel in contour of original image :"<<contours[i].size()<<endl;
            for (size_t j=0;j<contours[i].size();j++)
                cout << contours[i][j] << "*";
            drawContours(result, contours, i, c[i]);

    size_t maxContour=0;
    for (size_t k = 0; k < 3; k++)
        cout << "#contours("<<k<<") : " << contours[k].size()<<endl;;
        if (maxContour<contours[k].size())
            maxContour= contours[k].size();
        if (contours[k].size() > 0)
            for (size_t i = 0; i<contours[k].size();i++)
                cout << "#pixel in contour using canny with original image :"<<contours[i].size()<<endl;
                for (size_t j=0;j<contours[k][i].size();j++)
                    cout << contours[k][i][j] << "*";

            cout << "No contour found "<<endl;

    int index=0;
    while (true)
        char key = (char)waitKey();
        if( key == 27 )

        if (key == '+')
            index = (index+1)%maxContour;
        if (key == '-')
            index = (index-1);
            if (index<0 ...
edit flag offensive delete link more


thank you, can you explain me what's the point of blurring the image? Is it something recommended in this case also when you work on more "complex" shapes?

Izzy88 gravatar imageIzzy88 ( 2016-07-31 19:08:14 -0500 )edit

Yes it is recommended because you have to reduce noise before apply canny method. Reduce noise too mean enlarge thin line...

LBerger gravatar imageLBerger ( 2016-08-01 02:15:10 -0500 )edit

Question Tools

1 follower


Asked: 2016-07-30 11:33:26 -0500

Seen: 2,886 times

Last updated: Jul 31 '16