13

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.

karlphillip
  • 87,606
  • 33
  • 227
  • 395
Gyorgy Szekely
  • 2,304
  • 3
  • 18
  • 30

3 Answers3

16

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.

Dhaivat Pandya
  • 6,389
  • 4
  • 27
  • 41
Gyorgy Szekely
  • 2,304
  • 3
  • 18
  • 30
  • 2
    I don't really agree with the edit, as findContours() and drawContours() not necessarily operate on the same image, unless you want to immediately trash the contour image by overdrawing it. The edit also removed the information that the input of findContours() is coming from Canny(). – Gyorgy Szekely May 08 '13 at 13:25
2

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.

1

This how I would do it 1. Canny for edge detection 2. Use houghtransform to detect the edges. 3. Detect the two edges that do an angle of 90.

Ahmed Saleh
  • 1,934
  • 1
  • 29
  • 65
  • Thanks, it's a good idea for front facing rectangles, but unfortunatelly I have to detect them in any orientation. In fact the task I'm doing is to estimate marker orientation in 3D space (with known intrinsic cam parameters). – Gyorgy Szekely Apr 02 '13 at 07:00
  • You can use OpenSURF for any orientation to detect the marker with a matched templates – Ahmed Saleh Apr 02 '13 at 08:06