Ask Your Question
0

Does findContours create duplicates?

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

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
2

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

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

Comments

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 -0600 )edit
0

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

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++)
        {
        x.at<uchar>(i,j)=255;
        result.at<Vec3b>(i,j)=Vec3b(255,255,255);
        }
    imwrite("square.png",x);
    Mat idx;
    findNonZero(x,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)
            xx[i]=x.clone();
        else
            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 );
        namedWindow(format("canny%d",i),WINDOW_NORMAL);
        namedWindow(format("result%d",i),WINDOW_NORMAL);
        imshow(format("canny%d",i),dst[i]);
        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] << "*";
            cout<<endl;
            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<<endl;
            }

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

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

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

Comments

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 -0600 )edit
1

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 -0600 )edit

Question Tools

1 follower

Stats

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

Seen: 5,259 times

Last updated: Jul 31 '16