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);
Related
After reading some stack overflow posts on image processing, I made a program to detect and a paper in an image and remove the background. But currently the program only detects the contours. How can I crop out the biggest contour? Eventually, I will use this region and use tesseract for character recognition. I am stuck at the preprocessing part.
the image I used:
And this is what I got after running my script:
How can I approximate the big blue contour and crop it out for things like character detection?
Here is the code I used:
static void on_canny( int,void* ){
blur( dst, detected_edges, Size(3,3) );
Canny(detected_edges, detected_edges, cannyThreshold, cannyThreshold*r, kernel_size);
findContours(detected_edges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
Mat drawing = Mat::zeros( detected_edges.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("Contour",drawing);
imshow("Canny", detected_edges);
}
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)
I am trying to convert contour to set of polygonal curves, but I am stuck when I try to use approxPolyDP function. First I tested if findContours works properly and try to draw contours in my image - It works for contourIdx = 0. Then I try to use approxPolyDp as shown in example: http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/bounding_rects_circles/bounding_rects_circles.html
But during execution I have error "Acces violation" connected with vector class and funciton size(). Here is my code:
IplImage* image = cvLoadImage("F:\\triangle.png");
waitKey(5000);
//Mat img = imread("triangle.png");
Mat img(image,true);
if(!img.data)
{
cout <<"image file not found";
cv::waitKey(5000);
return -1;
}
//namedWindow( "window", 0 );
//imshow( "window", img );
cvNamedWindow("window");
cvShowImage("window",image);
Mat imgGray;
Mat imgEdges;
cvtColor(img,imgGray,CV_BGR2GRAY);
blur(imgGray,imgGray,Size(3,3));
threshold(imgGray,imgEdges,128,255,CV_THRESH_BINARY);
Mat canny_output;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
/// Detect edges using canny
Canny( imgGray, canny_output,100, 100*2, 3 );
/// Find contours
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Draw contours
RNG rng(12345);
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
if (drawing.type() != CV_8UC3)
{
cout << "Error: image type different then CV_8UC3";
}
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, 0, color, 2, 8, hierarchy, 0, Point() );
IplImage img3 = drawing;
cvNamedWindow( "Contours", CV_WINDOW_AUTOSIZE );
cvShowImage( "Contours", &img3 );
vector<vector<Point>> contoursOUT/*(contours.size())*/;
approxPolyDP(Mat(contours[0]),contoursOUT,3,true );
waitKey(0);
return 0;
Has anyone any idea what's wrong here?
The OpenCV-doc says about approxPolyDP:
void approxPolyDP (InputArray curve,
OutputArray approxCurve,
double epsilon,
bool closed)
approxCurve - ... The type should match the type of the input curve. ...
So the last part of your implementation should be:
vector<Point> contoursOUT;
approxPolyDP( Mat(contours[0]), contoursOUT, 3, true );
I teste your code with this small change, and it compiles and outputs resonable results.
I would recommend you to first study the documentation of approxPolyDP in opencv properly.
Now talking about your code, you have done a wrong declaration.
vector<vector<Point>> contoursOUT;
Use this one which is correct,
vector<Point> contoursOUT;
Also, in case of multiple object use for loop.
Hello StackOverflowers
I have created an application that Segments an image on the basis of a predefined color using inRange function. I then draw bounding box around the detected object.
My question here is how do I determine region properties such as: area, size, height and with, center point.
Here i placed a screen dump example.....
How should i approach to retrieve region properties of these bounding boxes or any other bounding boxes that get drown.......?
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(mBlur, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// Approximate contours to polygons + get bounding rects and circles
vector<vector<Point> > contours_poly( contours.size() );
vector<Rect> boundRect( contours.size() );
vector<Point2f>center( contours.size() );
vector<float>radius( 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]) );
}
/// Draw polygonal contour + bonding rects
Mat drawing = Mat::zeros( range_out.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar(255,0,255);
drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
}
Regards
You can get the area by using the built in OpenCV function. There are other functions there too to get everything you need.
Just iterate over the 2D coordinates of the segmented shape (the thin pink line in your pictures, you can found this just checking which pixels are not black and looking into its coordinates) and store maximum and minimum X and Y found. Then, width of is maxX - minX and height is maxY - minY
void visualizeSegments(Mat& img, Mat& dst)
{
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(img, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
dst=Mat::zeros(img.size(), CV_8UC3);
for(int i = 0; i < contours.size(); i++)
{
//Moments mu = moments(contours[i], true );
//Point2f centroid(mu.m10/mu.m00,mu.m01/mu.m00);
//double area = fabs(contourArea(Mat(contours[i])));
//vector<Point> contours_poly;
//approxPolyDP(Mat(contours[i]), contours_poly, 3, true);
//Rect boundRect = boundingRect(Mat(contours_poly));
drawContours(dst, contours, i, Scalar(255,0,0), -1, 8, hierarchy);
}
}
As stated befor there are a set of usefull functions in OpenCV
1. double contourArea(InputArray contour, bool oriented=false ) : to comute the area
2. double arcLength(InputArray curve, bool closed) : to compute the perimeter
3. Moments moments(InputArray array, bool binaryImage=false ) : to compute the center of gravity
4. void HuMoments(const Moments& m, OutputArray hu) : if you want additional properties that is usefull for classification
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);