Why cv::findContours return so many contours? - c++

I've read this post, but even after using cv::threshold to create a really binarry image, I still get ~500 contours. What am I doing wrong?
Shouldn't cv::findContours return only 13 contours since there are clear 13 blobs?
Mat img = imread("img.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat img_thresh;
threshold(img, img_thresh, 0, 255, CV_THRESH_BINARY);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::findContours(img_thresh, contours, hierarchy, RetrievalModes::RETR_TREE, ContourApproximationModes::CHAIN_APPROX_SIMPLE);
RNG rng(12345);
Mat drawing = Mat::zeros(img_thresh.size(), CV_8UC3);
for (int i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
imshow("drawing", drawing);
waitKey();
UPDATE1
Using cv::RETR_EXTERNAL instead of cv::RETR_TREE, but still return much more contours than should be.

If you check your binary image you will see there are a lot of independent contours:
So you first need to clean up them by eroding and dilating as below code:
And you will get this result:
Which is cleaner than the original.
It is all the code:
cv::namedWindow("result", cv::WINDOW_FREERATIO);
cv::Mat img = cv::imread(R"(rUYLL.png)");
// to gray
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::threshold(gray, gray, 0, 255, cv::THRESH_BINARY);
cv::erode(gray, gray, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
cv::dilate(gray, gray, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
std::vector<std::vector<cv::Point> > contours;
cv::findContours(gray, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
cv::drawContours(img, contours, -1, cv::Scalar(0, 255, 0), 2, 8);
cv::imshow("result", img);
cv::waitKey();
And it is the output:
Hope it helps!
And one simplest way which you can also consider if it works for you, just increase the lower threshold from 0 to 80, and DONE
cv::threshold(gray, gray, 80, 255, cv::THRESH_BINARY);
JUST PLAY WITH THRESHOLD and check the result.
The same output just with changing the threshold value:

Related

OpenCV C++ drawContours Error

Mat frame;
Mat frame2;
Mat output_frame;
Mat imgray;
Mat imgCanny;
vector<vector<Point> > contours;
vector<Point> approx;
Mat img = imread("abc.jpg");
cvtColor(img, imgray, COLOR_BGR2GRAY);
Canny(imgray, imgCanny, 10, 100, 3, false);
findContours(imgCanny, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
double eps = 0.1 * arcLength(contours[0], true);
approxPolyDP(contours[0], approx, 1, true);
drawContours(img, approx, 0, (0, 255, 0), 1); // Here has Error..
I studied about OpenCV, but the drawContours method(?) is strange.
I mean other drawContours is done (drawContours(img,contours,0,(0,255,0),1);)
But drawContours(img, approx, 0, (0, 255, 0), 1); has error.
Why?
I confirm that the approx has data (4 dot points)
input of drawContours should be vector<vector<Point> > .
here is minor modification of your code.
Mat frame;
Mat frame2;
Mat output_frame;
Mat imgray;
Mat imgCanny;
vector<vector<Point> > contours;
vector<Point> approx;
Mat img = imread("abc.jpg");
cvtColor(img, imgray, COLOR_BGR2GRAY);
Canny(imgray, imgCanny, 10, 100, 3, false);
findContours(imgCanny, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
double eps = 0.1 * arcLength(contours[0], true);
approxPolyDP(contours[0], approx, 1, true);
vector<vector<Point> > approx_t;
approx_t.push_back(approx);
drawContours(img, approx_t, 0, (0, 255, 0), 1);

how to close the edge of an object near the end of picture (open cv)

I am working on a code to detect the edges of boxes in a picture, but the boxes attached to the edge of the picture have missing lines.
as seen in the previous above the first square have only 2 lines appears.
I am using open cv C++
here is my code
cv::Mat src;
cv::Mat gray;
cv::Mat dst;
std::vector<std::vector<cv::Point> > contours;
std::vector<std::vector<cv::Point> > contours2;
std::vector<cv::Point> approx;
Mat img = imread("shapes-noisy.jpg", CV_LOAD_IMAGE_COLOR);
Mat _color = img.clone();
threshold(img, img, 250, 255, 0);
src = img;
cv::cvtColor(src, gray, CV_BGR2GRAY);
Mat img6, img7, img8, img9, img10;
cv::threshold(gray, img6, 250, 255.0, THRESH_BINARY);
Mat element = getStructuringElement( MORPH_RECT,Size(3, 3 ), Point( 1, 1 ) );
dilate( img6, img6, element);
Laplacian(img6, img7, CV_16S, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(img7, img8);
vector<Vec4i> hierarchy;
RNG rng(0);
Scalar value;
cv::findContours(img8.clone(), contours, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
Mat drawing,drawing2;
medianBlur(img8, drawing, 3);
threshold(drawing, drawing, 255, 0, 1);
threshold(drawing, drawing2, 255, 0, 1);
cv::findContours(img8.clone(), contours2, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
for( int i = 0; i< contours.size(); i++ )
{
cv::approxPolyDP(cv::Mat(contours[i]), approx, 10, true);
if(approx.size() > 3){
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing2, contours2, i, color, 2, 8, hierarchy, 0, Point(0,0) );
}
}
imshow("approx", drawing2);

OpenCV remove/cover specific contours

I have a video that I want to remove some specific contours on, I capture the video and then draw all the contours;
//Prepare the image for findContours
cv::cvtColor(frame, frame, CV_BGR2GRAY);
cv::threshold(frame, frame, 128, 255, CV_THRESH_BINARY);
//Find the contours. Use the contourOutput Mat so the original image doesn't get overwritten
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = frame.clone();
cv::findContours( contourOutput, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE );
//Draw the contours
cv::Mat contourImage(frame.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Scalar colors[3];
colors[0] = cv::Scalar(255, 0, 0);
colors[1] = cv::Scalar(0, 255, 0);
colors[2] = cv::Scalar(0, 0, 255);
for (size_t idx = 0; idx < contours.size(); idx++) {
cv::drawContours(contourImage, contours, idx, colors[idx % 3]);
}
The result looks like this:
Contours
So what I want to do now is to remove or cover objects like the trees and the tall house, basically contours that have a more complex shape than a road sign etc.
A desired result would look like this:
Removed/covered contours
It doesn't have to be exactly like on the picture as long as I can get something I can play with.

Opencv findcontours CV_RETR_EXTERNAL not working

I've this image:
EDIT
Sorry but I had to remove the images!
I need to extract the contour of the non-black picture, so I used findcontour with the CV_RETR_EXTERNAL parameter, but I obtain this:
Here's the code:
static Mat canny_output, grey,draw;
vector<vector<Point>> contours;
cvtColor(final_img, grey, CV_BGR2GRAY);
Canny(grey, canny_output, 100, 200);
findContours(canny_output, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
draw = Mat::zeros(canny_output.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++)
{
drawContours(draw, contours, i, Scalar(255, 0, 0));
}
how can I resolve?
Simply add a binarization with minimal threshold, and remove Canny:
cvtColor(final_img, grey, CV_BGR2GRAY);
//Threshold=1: very low value, anyway the rest of the image is pure black
threshold(grey, binary, 1, 255, CV_THRESH_BINARY);
findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

Combine multiple threshold for inRange

I want to combine multiple threshold for detecting different type of red colour. I tried to make 4 type of thresholding and then I combine the result of 4 image into 1. but the result is always pitch black. is there any other way to do it? I guess the way I'm combining the 2 image into 1 is not correct using the addWeighted
Mat img = imread (nameImg);
cvtColor(img , hsv, CV_BGR2HSV);
Mat bw,bw2,bw3,bw4;
inRange(hsv, Scalar(0,28,192), Scalar(4,67,219),bw); // detecting acne 1
inRange(hsv, Scalar(0,40,152), Scalar(8,85,243),bw2); // acne 2
inRange(hsv, Scalar(0,85,202), Scalar(6,146,247),bw3); // acne 3
inRange(hsv, Scalar(156,93,176), Scalar(82,130,255),bw4); // acne 4
vector<vector<Point> > contours;
vector<vector<Point> > contours2;
vector<vector<Point> > contours3;
vector<vector<Point> > contours4;
findContours(bw.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
findContours(bw2.clone(), contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
findContours(bw3.clone(), contours3, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
findContours(bw4.clone(), contours4, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
Mat dst = Mat::zeros(img.size(), img.type());
drawContours(dst, contours, -1, Scalar::all(255), CV_FILLED);
Mat dst2 = Mat::zeros(img.size(), img.type());
drawContours(dst2, contours2, -1, Scalar::all(255), CV_FILLED);
Mat dst3 = Mat::zeros(img.size(), img.type());
drawContours(dst3, contours4, -1, Scalar::all(255), CV_FILLED);
Mat dst4 = Mat::zeros(img.size(), img.type());
drawContours(dst4, contours4, -1, Scalar::all(255), CV_FILLED);
Mat dst5, dst6;
imshow("dst",dst);
imshow("dst2",dst2);
imshow("dst3",dst3);
imshow("dst4",dst4);
addWeighted(dst, 0.5, dst2, 0.5, 0,dst5);//combine acne 1 with 2
addWeighted(dst4, 0.5, dst3, 0.5, 0,dst6); //combine acne 3 with 4
addWeighted(dst5, 0.5, dst6, 0.5, 0,out);
imshow("result",out); //the result always black