# Does findContours create duplicates?

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 close merge delete

Sort by » oldest newest most voted

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.

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.

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

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

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);
g = dx.mul(dx) + dy.mul(dy);
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

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?

( 2016-07-31 19:08:14 -0500 )edit
1

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

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

Official site

GitHub

Wiki

Documentation