findcontour function giving me incorrect number of contours - opencv

I am trying to get contours for an image using OpenCV4 and C++. I have defined the contours as vector of vector of points. When I use the function it is only giving me one set of contours.
vector<vector<cv::Point> > contours;
vector<cv::Vec4i> hierarchy;
findContours(border_image, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
cout<<"Size of contours -->"<< contours.size()<<endl;
border_image is a cv::Mat and has each pixel as CV_8UC1
Above code is returning size(length of vector of vector of points) as 1.
If I do the same in Python, I am getting the required number of contours. What am I doing wrong?
Python Version:
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
Above returns contours of length 112
Expected contours(Vector of Vector of Points) of length 112. I only got contours of length 1 which is nothing but first element(Vector of Points) in result obtained by Python.
What am I doing wrong? Someone help me out.

Related

opencv structured forest edge detection and findcontours

I am trying to use opencv via visual c++ to extract contours of an image. I was able to do that using the opencv tutorial for findcontours.
findcontours works in two steps
Detect edges using canny edge detector.
Feed the output of canny to findcontours.
I want to try out the same with 'Structured Forest Edge Detection' (Zitnick et al). I am able to extract the edges and display them, but when I try to feed the output to findcontours. I am getting a 'cv::Exception at memory location 0x0020EE9C' error. (see code below). What am I doing wrong?
Mat src = imread("image.jpg");
src.convertTo(src, CV_32F, 1.0 / 255.0);
Mat edges(src.size(), src.type());
Ptr<StructuredEdgeDetection> pDollar = createStructuredEdgeDetection("model.yml.gz");
pDollar->detectEdges(src, edges);
findContours(edges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
pDollar->detectEdges(src, edges);
edges type is CV_32F. you must convert it to 8-bit single-channel image

Finding small contours in OpenCV

I am using EmguCV (a C# wrapper of OpenCV) and I can find contours using FindContours as:
Contour<Point> cnts;
cnts = imgLineMask.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST);
for (; cnts != null; cnts = cnts.HNext)
{
double ar = cnts.Area;
}
However, their area and moments are all zero if the contours are just one or two pixels big. Is there anyway to make it work with such small contours? Or it just simply can not work with very small contours?
Thanks
No, I don't think there's a way to make it work, using Findcontours.
The reason is that the OpenCV method, is a contour finding method and not a blob finding method. The area is calculated from the perimeter and not just a sum of pixels.
The perimeter is a sum of the distance between neighboor pixels on the contour. Therefore the perimeter of a 2x2 pixel blob is 4, but the area will be 1 times 1 = 1. And a single pixel will have a perimeter of 0 and thus also an area of 0.
If you want to find single pixel blobs, you can have a look at the Recursive Grass-Fire algoritm or the Connected-Component algorithm. The latter is probably the easiest to implement.

findContours for blob finding

I'm using OpenCV's findContours() for blob-finding, by floodfilling at an arbitrary seed point in the contour and taking the bounding rectangle of the floodfill. However, when two blobs touch at a corner, e.g.
they share a contour, so only one of the two blobs will be floodfilled, depending on which seed point was chosen.
I could change the floodfill connectivity setting from 4 to 8, so that the blobs are fused in the floodfill. What I'd really like to do instead is ignore the small defect and count only the big blob. Can this be done without substantially changing the algorithm?
Unlike for floodfill, there's no way to use findContours with 4-connectivity natively in OpenCV.
You should take a look at findContours() documentation.
findContours can return multiple contours if they appear in the image, in your case, if you choose 4-connectivity, you should get 2 contours, and then you can compare their bounding box size to decide which one to keep.
cv::Mat img = cv::imread('test.png', 0);
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(img, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
for (size_t i = 0;i < contours.size(); ++i) {
cv::Rect bbox = cv::boundingRect(contours[i]);
std::cout<<"Contour"<<i<<" Area"<<bbox.area()<<std::endl;
}
Hope this helps.

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.
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.
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.
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.

findContours in opencv with "System.AccessViolationException"

I am trying to use the findContours function in Opencv2.4.4 with VS2010express(C++) the code is below.
Mat canny_output;
std::vector > contours;
/// Detect edges using canny
Canny( src_gray, canny_output, 100, 200, 3 );
/// Find contours
threshold(canny_output,canny_output,0,255,THRESH_BINARY);
findContours( canny_output, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE );
but the program will always trigger breakpoint at the last line with the system error System.AccessViolationException.
Anyone has any idea?
Suggestions:
make sure contours is vector< vector<Point> >
After a Canny operation you can directly feed the edges to findContour..why are you doing a thresholding? that too with a threshold value of zero...skip that line...because the output of canny is a binary image.
Make sure cannny_output is also a gray image.
EDIT: try this ..although this gives external contours..check wether findcontour is orking or not..
findContours(canny_output,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE,Point())

Resources