findcontours finds too many contours - opencv

Is it normal that find contours finds so many contours where there are obviously only 3 contours ?
The original image and after drawing found contours are shown below :
The code is :
Mat image;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
image = imread(argv[1], 0); // Read the file
findContours(image, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
cout << contours.size();
RNG rng(12345);
Mat drawing = Mat::zeros(image.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());
}
/// Show in a window
namedWindow("Contours", CV_WINDOW_AUTOSIZE);
imshow("Contours", drawing);
namedWindow("Display window", WINDOW_AUTOSIZE);// Create a window for display.
imshow("Display window", image); // Show our image inside it.

I think you are Reading a Jpeg or any other compressed version of your actual input image which adds these noise. Find contours needs Binary image. If you want to process this image simple apply threshold before finding contours! Note that your source image is working fine for me!

Related

Draw lines connecting centroids of objects using OpenCV

I have found out the centroid of multiple objects in my image using the code provided here OpenCV examples
Here is the code which found the centroid and stored them in a vector.
cv::Mat InputImage;
cv::Mat CannyOutput;
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
RNG rng(12345);
InputImage = cv::imread("Untitled.jpg");
//Edge detection
Canny(InputImage, CannyOutput, 100, 150);
//Contour detection
cv::findContours(CannyOutput, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
//Finding Moments
vector<Moments> mu(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mu[i] = moments(contours[i], false);
}
//Calculating Centroid
vector<Point2f> mc(contours.size());
for (int i = 0; i < contours.size(); i++)
{
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
}
// Drawing
Mat drawing = Mat::zeros(CannyOutput.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));
//Drawing contour
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point());
//Drawing circles with centroid as centre
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
What I want to do is draw a polygon with the centroid as the vertices. I used drawcontours, polyline and line functions but not getting the desired result. Is there a way to achieve this? . I need it to be achieved in C++
Output image
Desired image
Also, on another note, the code doesnt seem to be displaying the centroid if am replacing the 'color' variable with BGR value. Seems like both contour and centroid has to be of same color if i want to see the centroid. When i gave (0,255,255) for contour and (255,255,0) for centroid, the centroid was not displaying .
For the given sample image, you can use convexhull to obtain the order of centers, and then draw them with polylines.

Contour results are not accurate with detection binary mask edges [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Now I have this mask generated based on the size of an image and I have a set of rectangles to know where are these rectangles so I tried to extract contours to know where are these rectangles ...
mask = np.zeros((image.shape[0], image.shape[1]), dtype=np.uint8) # Mask of original image
#some process to draw these rectangles merged...
_, bw = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)
img, contours, hierarchy = cv2.findContours(bw.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for j in range(len(contours)):
outputBoxes.append(cv2.boundingRect(contours[j]))
Another trial
_, bws = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)
img, contours, hierarchy = cv2.findContours(bws, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE, offset=(0,0))
cv2.drawContours(bws, contours,-1,(255,255,0))
img, contours, hierarchy = cv2.findContours(bws, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE, offset=(0,0))
but with no vain
I can't get the rect dimensions exactly or even approximately
** NOTE This generated image is small part of long process
first one I detected some rects so I merged them as one in the second one then I should take this white one dimensions to be determined in the original one
As seen, it is not accurate too
Python or c++ are good for me
opencv 3.x
Here is the results I get:
Obviously from here you have pretty clear contours and you can just print out the lengths.
With this code:
Mat origImage = mat;
Mat canny_output = mat;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::Mat greyMat, colorMat;
cv::cvtColor(mat, greyMat, CV_BGR2GRAY);
int thresh = 100;
RNG rng(12345);
///// Detect edges using canny
Canny(greyMat, canny_output, thresh, thresh * 2, 3);
/// Find contours
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
int largest_area = 0;
int largest_contour_index = 0;
Rect bounding_rect;
/// Draw contours
Mat drawing = Mat::zeros(canny_output.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());
double a=contourArea( contours[i],false); // Find the area of contour
if(a>largest_area){
largest_area=a;
largest_contour_index=i; //Store the index of largest contour
bounding_rect=boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
}
rectangle(origImage, bounding_rect, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)),2);
/// Show in a window
namedWindow("Contours", CV_WINDOW_AUTOSIZE);
resize(drawing, drawing, Size(640, 480));
resize(mat, mat, Size(640, 480));
resize(origImage, origImage, Size(640, 480));
imshow("Contours", drawing);
cv::namedWindow("img");
cv::imshow("mat", mat);
cv::imshow("mat", origImage);
cv::imshow("mat123", drawing);
cv::waitKey(0);

OpenCV: How can I remove unwanted blobs or how can I copy wanted parts into an empty image?

From the following image, how could I find the result image?
The images shown here are threshold images. I have tried using morphological operators but they even remove the blob I want. How could I solve this problem?
Any hints?
Following is the result image I am interested to get/find:
import cv2
diff = cv2.imread('Image.png',0)
ret, thresh = cv2.threshold(diff, 12.5, 255, cv2.THRESH_BINARY)
thresh = cv2.dilate(thresh, None, iterations = 1)
cv2.imshow('img', thresh) # This is the first picture I have shown
cv2.waitKey(0)
You are most of the way there, all you need to do now is find the blobs, add some contours and find the biggest one. Easy! below is the code in C++, ill leave it up to you to work out how to convert it to Python:
cv::Mat mat = imread("g0cVU.png");
Mat origImage = mat;
Mat canny_output = mat;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::Mat greyMat, colorMat;
cv::cvtColor(mat, greyMat, CV_BGR2GRAY);
int thresh = 100;
RNG rng(12345);
///// Detect edges using canny
Canny(greyMat, canny_output, thresh, thresh * 2, 3);
/// Find contours
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
int largest_area = 0;
int largest_contour_index = 0;
Rect bounding_rect;
/// Draw contours
Mat drawing = Mat::zeros(canny_output.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());
double a=contourArea( contours[i],false); // Find the area of contour
if(a>largest_area){
largest_area=a;
largest_contour_index=i; //Store the index of largest contour
bounding_rect=boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
}
rectangle(origImage, bounding_rect, Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)),2);
/// Show in a window
namedWindow("Contours", CV_WINDOW_AUTOSIZE);
imshow("Contours", drawing);
cv::namedWindow("img");
cv::imshow("mat", mat);
cv::imshow("mat", origImage);
cv::imshow("mat123", drawing);
cv::waitKey(0);
Which gives this results:
You can see in the bottom image the largest contor has a brown rectangle drawn around it.
o and obviously once you have the largest blob (or whatever blob you deem "the correct one") you can just set everything else to black which is fairly straightforward.

OpenCV iOS Drawing Bounding Box

I'm trying to draw a box around a foot in a photo. I can do it with other objects, including ones that aren't fully in the image (I have pasted my code below). However, when I try to use it with a foot, it draws the contour perfectly but the rectangle is always the around the full image. I have attached pictures below of the result. Is there any way to change my code to fix this, or is there any other method possible of drawing a box around the foot?
Original Image
Contour and Bounding Box
RNG rng(12345);
Mat threshold_output;
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
Mat srcImage;
UIImageToMat(image, srcImage);
cvtColor(srcImage, srcImage, CV_BGR2GRAY);
blur(srcImage, srcImage, cv::Size(3,3));
threshold(srcImage, threshold_output, 100, 255, THRESH_BINARY );
findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
vector<vector<cv::Point> > contours_poly( contours.size() );
vector<cv::Rect> boundRect( contours.size() );
for( int i = 0; i < contours.size(); i++ ) {
approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]) );
}
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
if (contourArea(contours[i]) < 1000) {
continue;
}
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, cv::Point());
rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
cout << "Height:" << boundRect[i].height << " Width: " << boundRect[i].width << endl;
}
return MatToUIImage(drawing);
Since the bottom part of the foot reaches the image boundary, the contour seems to wrap around the perimeter of the image and thereby create a large bounding box that includes the entire image.
A quick and dirty solution will be to set the border pixels of the image to zero before applying findContours(). For a better solution, you have to think about the actual problem that you are trying to solve.

How to draw a rectangle around the contours?

I am just starting out with opencv and I am trying to make a program that puts squares around a picture of rocks on some sand. The documentation for the function here includes an example of how to use it.
findContours( src, contours, hierarchy,
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
The prototype of findContours is
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point()) ;
I have two questions.
1. The third argument in the example hierarchy is a vector<Vec4i> does not match the type findContours expects. Why is that?
2. How does one use the data stored in contours to find where the contours are to create a bounding box?
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( mask, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_TC89_KCOS);
for ( size_t i=0; i<contours.size(); ++i )
{
cv::drawContours( img, contours, i, Scalar(200,0,0), 1, 8, hierarchy, 0, Point() );
cv::Rect brect = cv::boundingRect(contours[i]);
cv::rectangle(img, brect, Scalar(255,0,0));
}

Resources