I'm currently working on an image with a lot of detected contours.
My goal is to narrow down the number of contours to end up with only the one I'm looking for.
For that I conduct a bunch of tests based on area and bounding box.
For now I do after every step a drawContours for the contours that I want to keep followed by a findContours.
My problem is that I would like to do findContours only once and then just erase the contours I don't want, is this possible?
Current way :
Mat src;
Mat BW;
src = imread("img.bmp", 0);
if( src.channels() > 1)
{
cvtColor(src, src, CV_BGR2GRAY);
}
threshold(src, BW, 100, 255, CV_THRESH_OTSU);
imshow( "Tresh", BW );
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);
Mat dstP = Mat::zeros(src.rows, src.cols, CV_8UC3);
Mat dst1 = Mat::zeros(src.rows, src.cols, CV_8UC3);
Mat dst2 = Mat::zeros(src.rows, src.cols, CV_8UC3);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( BW, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
for( int i = 0; i < contours.size(); i++ )
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( dst, contours, i, color, 2/*CV_FILLED*/, 8, hierarchy );
}
/// Test on area ******************************************************************
for( int i = 0; i < contours.size(); i++ )
{
if ( contourArea(contours[i], false) > 100 && contourArea(contours[i], false) < 200000)
{
Scalar color( rand()&255, rand()&255, rand()&255 );
drawContours( dst1, contours, i, color, CV_FILLED, 8, hierarchy );
}
}
/// Next test **********************************************************************
cvtColor(dst1, dstP, CV_BGR2GRAY);
findContours( dstP, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
etc
Wanted way :
if ( contourArea(contours[i], false) < 100 && contourArea(contours[i], false) > 200000)
{
contours.erase(i); // Doesn't work
}
Does anyone now how to erase those contours?
PS : I don't care about inner contours, I want all of them to go trough my tests.
EDIT, the solution (pointed out by limonana) is : contours.erase(contours.begin()+i);
Related
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);
I would be very thankful to know further on post: Finding Minimum Distance between Contours
I am using FindContours and as required getting multiple contours.
The problem is that I want to find min distance between each adjacent pair,
e.g. between yellow contour and dark green, between dark green and cyan, between cyan and purple.
I understood the general method from above post.
But can anyone please tell me how can i select them one after another(automaticaly)?
void thresh_function(int, void*)
{
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// Detect edges using canny
Canny( roiImg, canny_output, threshold_value, threshold_value*2, 3 );
/// Find contours
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// 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() );//Scalar(255,255,255)
}
erode(drawing,drawing,erodeElement2);
erode(drawing,drawing,erodeElement1);
dilate(drawing,drawing,dilateElement);
/// Show in a window
//namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
resize(drawing, enlargeD0, Size(), 2, 2, CV_INTER_CUBIC);
done = 1;
imshow("Contours", enlargeD0);
}
vector<vector<Point> > all_contours;
...
findContours(... all_contours ... CV_CHAIN_APPROX_NONE ...);
...
// Remove small contours
int minSize = 20;
vector<vector<Point> > contours;
contours.reserve(all_contours.size());
for(int i=0; i<all_contours.size(); ++i)
{
if(all_contours[i].size() > minSize)
{
contours.push_back(all_contours[i]);
}
}
Mat1f dist(contours.size(), contours.size(), 0.f);
for(int i=0; i<contours.size()-1; ++i)
{
const vector<Point>& firstContour = contours[i];
for(int j=i+1; j<contours.size(); ++j)
{
const vector<Point>& secondContour = contours[j];
float d = compute_pairwise_distance(firstContour, secondContour); // You should implement this
dist(i,j) = d;
dist(j,i) = d; // distance from first to second is equal
// to distance from second to first
}
}
// Now dist contains the pairwise distances between i-th and j-th contours
I am trying a number recognition. However after contour finding. I get bounding boxes inside the main bounding box for numbers 0,6,8 ... as shown in figure. Please help me with this initial step of image processing.
I have tried using group rectangles but they are not working. Please check the code below. Thank you.
Image: http://tinypic.com/r/1twx05/5
int main()
{
Mat inimage, gray;
inimage = imread("sample.jpg");
cvtColor(inimage, gray, COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(5,5), 0);
adaptiveThreshold(gray, gray, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 11, 0);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( gray, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
vector<vector<Point> > contours_poly( contours.size() );
vector<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]) );
}
//groupRectangles(boundRect, 1, 0.2);
Scalar color = Scalar(0,0,255);
for( int i = 0; i< contours.size(); i++ )
{
//drawContours( inimage, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
rectangle( inimage, boundRect[i].tl(), boundRect[i].br(), color, 1, 8, 0 );
}
namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
imshow( "Contours", inimage );
waitKey(0);
return 0;
}
try to use the flag: CV_RETR_EXTERNAL instead of CV_RETR_TREE
as stated in the docs it tells to take only outer contours.
Or follow the tree hierarchy to drop nested contours (read the docs for how-to)
This question already has an answer here:
Convert Vector<Point> to Mat
(1 answer)
Closed 8 years ago.
I want to get every vector of points into a matrix like :
std::vector<std::vector<cv::Point>> vec;
......................
for (int i ; i < vec.size();i++){
imshow("stuff", cv::Mat(vec[i]); /// this crashes !!!
}
any idea how to do that?
thanks in advance
imshow looks for a complete picture.
by casting a contour to a Mat you won't have a picture.
what you can do is: (src is your picture)
src = imread( argv[1], 1 );
/// Convert image to gray and blur it
cvtColor( src, src_gray, CV_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// Detect edges using canny
Canny( src_gray, canny_output, thresh, thresh*2, 3 );
/// Find contours
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Draw contours
for( int i = 0; i< contours.size(); i++ )
{
Mat test_image = Mat::zeros( canny_output.size(), CV_8UC3 );
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( test_image, contours, i, color, 2, 8, hierarchy, 0, Point() );
imshow("test",test_image);
waitKey();
}
You can do it like this:
void draw_contour(cv::Mat &dst_img, const std::vector<cv::Point> &contour, const cv::Scalar &color)
{
for (auto &point: contour)
{
dst_img.at<unsigned char>(point) = color;
}
}
Or if you are using approximation of contours:
void draw_contour(cv::Mat &dst_img, const std::vector<cv::Point> &contour, const cv::Scalar &color)
{
for (unsigned i = 0; i < contour.size(); ++i)
{
cv::line(dst_img, contour[i], contour[(i + 1) % contour.size(), color);
}
}
From the code below, I am able to draw the biggest contour with the centroid marked as a small circle and the hull as a yellow line. How do I draw the convexity defects? Should I use the circle() function or the drawContours() function?
Mat bw;
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( bw, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
int s = getBiggestContour(contours);
Mat drawing = Mat::zeros( src.size(), CV_8UC3 ); //UC1
Point2f mc = getCentroidPoint(contours[s]);
drawContours( drawing, contours, s, Scalar(255,255,255), -1, 8, hierarchy, 0, Point() );
circle( drawing, mc, 4, Scalar(0,0,255), 1, 8, 0 );
vector<vector<Point> >hull( contours[s].size() );
convexHull( Mat(contours[s]), hull[s], false );
drawContours( drawing, hull, s, Scalar(0,255,255), 1, 8, vector<Vec4i>(), 0, Point() );
The code above works but there's only one contour to be used which is the biggest contour so I think using vector> for hull is too much. How do I simplify that?
The code below is from another stackoverflow question but it doesn't show how to use the defects variable in drawing the convexity defect onto the Mat image. How can this be achieved?
vector<vector<int> > hullsI(contours.size());
vector<vector<Point> > hullsP(contours.size());
vector<vector<Vec4i> > defects(contours.size());
for(int i = 0; i <contours.size(); ++i){
//find the hulls
convexHull(contours[i], hullsI[i], false, false);
convexHull(contours[i], hullsP[i], false, true);
//find the defects
if (contours[i].size() >3 ){
convexityDefects(contours[i], hullsI[i], defects[i]);
}
}
I do not want to use IplImage. I prefer Mat.
You can draw the result of a convex hull operation with 'cvDrawContours()', but you do need to set your parameters correct for that to be possible. I have an example but it uses 'cvConvexHull2()' and IplImages but it should work the same way for a Mat and the other convexHull operation:
IplImage* src; //the image where the contours are detected on
IplImage frm; //the image you want the results to be drawn on
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours = NULL;
cvFindContours(src, storage, &contours, sizeof (CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
for (CvSeq* c = contours; c != NULL; c = c->h_next) {
CvSeq* dest = NULL;
CvMemStorage* hullStorage = cvCreateMemStorage(0);
dest = cvConvexHull2(c, hullStorage, CV_CLOCKWISE, 1);
cvDrawContours(frm, dest, cvScalarAll(255), cvScalarAll(255), 0, 2, 8);
cvReleaseMemStorage(&hullStorage);
}
cvReleaseMemStorage(&storage);