Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV converting Canny edges to contours

I have an OpenCV application fed from a webcam stream of an office interior (lot's of details) where I have to find an artificial marker. The marker is a black square on white background. I use Canny to find edges and cvFindContours for contouring, then approxPolyDP and co. for filtering and finding candidates, then use local histogram to filter further, bla bla bla...

This works more or less, but not exactly how I want. FindContours always returns a closed loop, even if Canny creates a non-closed line. I get a contour walking on both sides of the line forming a loop. For closed edges on the Canny image (my marker), I get 2 contours, one on the inside, and an other on the outside. I have to problems with this operation:

  • I get 2 contours for each marker (not that serious)

  • the most trivial filtering is not usable (reject non-closed contours)

So my question: is it possible to get non-closed contours for non-closed Canny edges? Or what is the standard way to solve the above 2 issues?

Canny is a very good tool, but I need a way convert the 2D b/w image, into something easily process-able. Something like connected components listing all pixels in walking order of the component. So I can filter for loops, and feed it into approxPolyDP.

Update: I missed some important detail: the marker can be in any orientation (it's not front facing the camera, no right angles), in fact what I'm doing is 3D orientation estimation, based on the 2D projection of the marker.

like image 720
Gyorgy Szekely Avatar asked Apr 01 '13 21:04

Gyorgy Szekely


People also ask

How do you draw contours in OpenCV?

To draw the contours, cv. drawContours function is used. It can also be used to draw any shape provided you have its boundary points. Its first argument is source image, second argument is the contours which should be passed as a Python list, third argument is index of contours (useful when drawing individual contour.

What is contour detection in OpenCV?

Contour Detection using OpenCV (Python/C++) Using contour detection, we can detect the borders of objects, and localize them easily in an image. It is often the first step for many interesting applications, such as image-foreground extraction, simple-image segmentation, detection and recognition.

What does Canny return OpenCV?

Canny() Function in OpenCV is used to detect the edges in an image.


2 Answers

I found a clean and easy solution for the 2 issues in the question. The trick is enable 2 level hierarchy generation (in findCountours) and look for contours which have a parent. This will return the inner contour of closed Canny edges and nothing more. Non-closed edges are discarded automatically, and each marker will have a single contour.

vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(CannyImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0,0) );
for (unsigned int i=0; i<contours.size(); i++)
    if (hierarchy[i][3] >= 0)   //has parent, inner (hole) contour of a closed edge (looks good)
        drawContours(contourImage, contours, i, Scalar(255, 0, 0), 1, 8);

It also works the other way around, that is: look for contours which have a child (hierarchy[i][2] >= 0), but in my case the parent check yields better results.

like image 186
Gyorgy Szekely Avatar answered Jan 04 '23 10:01

Gyorgy Szekely


I had the same problem with duplicate contours and even dilate and erode could not solve it:

Mat src=imread("E:\\test.bmp"),gry,bin,nor,dil,erd;
GaussianBlur( src, nor, Size(5,5),0 );  
cvtColor(nor,gry,CV_BGR2GRAY);  
Canny(gry,bin,100,150,5,true);
dilate(bin,dil,Mat());
erode(dil,erd,Mat());
Mat tmp=bin.clone();
vector<vector<Point>> conts;
vector<Vec4i> hier;
findContours(tmp,conts,hier,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);

This image (test.bmp) contains 3 contours but findContours returned 6! I used threshold and problem solved:

Mat src=imread("E:\\test.bmp"),gry,bin,nor,dil,erd;
GaussianBlur( src, nor, Size(5,5),0 );  
cvtColor(nor,gry,CV_BGR2GRAY);
threshold(gry,bin,0,255,THRESH_BINARY+THRESH_OTSU);     
vector<vector<Point>> conts;
vector<Vec4i> hier;
findContours(bin,conts,hier,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);

Now it returns 4 contours which the 1st one is the image boundary(contour with index 0) and can be easily skipped.

like image 37
Majid Daryadel Avatar answered Jan 04 '23 09:01

Majid Daryadel