How to find coordinates of largest rectangle in OpenCV? - c++

I have the following code:
findContours( src, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );
Mat drawing = Mat::zeros( src.size(), CV_8UC3 );
double largest_area = 0;
for( int i = 0; i < contours.size(); i++) { // get the largest contour
area = fabs( contourArea( contours[i] ) );
if( area >= largest_area ){
largest_area = area;
largest_contours.clear();
largest_contours.push_back( contours[i] );
}
}
if( largest_area >= 3000 ){ // draw the largest contour if exceeded minimum largest area
drawContours( drawing, largest_contours, -1, Scalar(0,0,255), 2 );
}
... which produces the following output image:
I want to get coordinates of four points (marked with green), is that possible?

Do you trying to find corners of rectangle in perspective?
You may want to try several solutions:
Use HoughLines for line detection and find their intersection.
Use Generalized Hough Transform
Use Harris corner detector. But you need to filter extra corners.
For similar task I used following procedure (it works fine in my case):
do cv::approxPolyDP for input contour with increasing epsilon parameter until it returns 4 or less polylines. If it returns 4 polylines you may get 4 corner points exact what you need. If it returns less than 4 polylines most probably something is wrong.

Related

Search for contours within a contour / OpenCV c++

I am trying to track a custom circular marker in an image, and I need to check that a circle contains a minimum number of other circles/objects. My code for finding circles is below:
void findMarkerContours( int, void* )
{
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
vector<Point> approx;
cv::Mat dst = src.clone();
cv::Mat src_gray;
cv::cvtColor(src, src_gray, CV_BGR2GRAY);
//Reduce noise with a 3x3 kernel
blur( src_gray, src_gray, Size(3,3));
//Convert to binary using canny
cv::Mat bw;
cv::Canny(src_gray, bw, thresh, 3*thresh, 3);
imshow("bw", bw);
findContours(bw.clone(), contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
Mat drawing = Mat::zeros( bw.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) );
// contour
drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
//Approximate the contour with accuracy proportional to contour perimeter
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true) *0.02, true);
//Skip small or non-convex objects
if(fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
continue;
if (approx.size() >= 8) //More than 6-8 vertices means its likely a circle
{
drawContours( dst, contours, i, Scalar(0,255,0), 2, 8);
}
imshow("Hopefully we should have circles! Yay!", dst);
}
namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
imshow( "Contours", drawing );
}
As you can see the code to detect circles works quite well:
But now I need to filter out markers that I do not want. My marker is the bottom one. So once I have found a contour that is a circle, I want to check if there are other circular contours that exist within the region of the first circle and finally check the color of the smallest circle.
What method can I take to say if (circle contains 3+ smaller circles || smallest circle is [color] ) -> do stuff?
Take a look at the documentation for
findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
You'll see that there's an optional hierarchy output vector which should be handy for your problem.
hierarchy – Optional output vector, containing information about the image topology. It has as many elements as the number of contours.
For each i-th contour contours[i] , the elements hierarchy[i][0] ,
hiearchyi , hiearchyi , and hiearchyi are set to
0-based indices in contours of the next and previous contours at the
same hierarchical level, the first child contour and the parent
contour, respectively. If for the contour i there are no next,
previous, parent, or nested contours, the corresponding elements of
hierarchy[i] will be negative.
When calling findCountours using CV_RETR_TREE you'll be getting the full hierarchy of each contour that was found.
This doc explains the hierarchy format pretty well.
You are already searching for circles of a certain size
//Skip small or non-convex objects
if(fabs(cv::contourArea(contours[i])) < 100 || !cv::isContourConvex(approx))
continue;
So you can use that to look for smaller circles than the one youve got, instead of looking for < 100 look for contours.size
I imagine there is the same for color also...

HOW TO get corners in a contour in opencv

I am working in C++ and opencv
I am detecting the big contour in an image because I have a black area in it.
In this case, the area is only horizontally, but it can be in any place.
Mat resultGray;
cvtColor(result,resultGray, COLOR_BGR2GRAY);
medianBlur(resultGray,resultGray,3);
Mat resultTh;
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Canny( resultGray, canny_output, 100, 100*2, 3 );
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
Vector<Point> best= contours[0];
int max_area = -1;
for( int i = 0; i < contours.size(); i++ ) {
Scalar color = Scalar( 0, 0, 0 );
if(contourArea(contours[i])> max_area)
{
max_area=contourArea(contours[i]);
best=contours[i];
}
}
Mat approxCurve;
approxPolyDP(Mat(best),approxCurve,0.01*arcLength(Mat(best),true),true);
Wiht this, i have the big contour and it approximation (in approxCurve). Now, I want to obtain the corners of this approximation and get the image inside this contour, but I dont know how can I do it.
I am using this How to remove black part from the image?
But the last part I dont understad very well.
Anyone knows how can I obtain the corners? It is another way more simple that this?
Thanks for your time,
One much simpler way you could do that is to check the image pixels and find the minimum/maximum coordinates of non-black pixels.
Something like this:
int maxx,maxy,minx,miny;
maxx=maxy=-std::numeric_limits<int>::max();
minx=miny=std::numeric_limits<int>::min();
for(int y=0; y<img.rows; ++y)
{
for(int x=0; x<img.cols; ++x)
{
const cv::Vec3b &px = img.at<cv::Vec3b>(y,x);
if(px(0)==0 && px(1)==0 && px(2)==0)
continue;
if(x<minx) minx=x;
if(x>maxx) maxx=x;
if(y<miny) miny=y;
if(y>maxy) maxy=y;
}
}
cv::Mat subimg;
img(cv::Rect(cv::Point(minx,miny),cv::Point(maxx,maxy))).copyTo(subimg);
In my opinion, this approach is more reliable since you don't have to detect any contour, which could lead to false detections depending on the input image.
In a very efficient way, you can sample the original image until you find a pixel on, and from there move along a row and along a column to find the first (0,0,0) pixel. It will work, unless in the good part of the image you can have (0,0,0) pixels. If this is the case (e.g.: dead pixel), you can add a double check checking the neighbourhood of this (0,0,0) pixel (it should contain other (0,0,0) pixels.

Remove bounding rect with an area < n OpenCV

I am eroding an image with text blocks on it then using findContours() to find all of the text blocks, then drawing their bounding rect. However sometimes there are very small rects created by noise in the image which either are in a bigger rect or in a place where there is no text.
I am using this code to find the contours and draw them.
double element_size = 20;
RNG rng(12345);
Mat element = getStructuringElement( cv::MORPH_ELLIPSE,cv::Size( 2*element_size + 1, 2*element_size+1 ),cv::Point( element_size, element_size ) );
erode(quad, quad, element);
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
quad.convertTo(quad, CV_8UC1);
findContours( quad, 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( quad.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar(0,255, 0 );
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
}
After a sample run here is what I get back:
How can I modify my code so that I can remove any rects that are not greater than n so I can only keep complete text blocks, I also need to remove the largest contour which surrounds the entire card.
Yo could use contourArea to eliminate contour, use below code before finding bounding rect, this will remove all the contour with area less than a threshold.
double min_area=100; // area threshold
for( int i = 0; i< contours.size(); i++ ) // iterate through each contour.
{
double area=contourArea( contours[i],false); // Find the area of contour
if(area<min_area)
contours.erase(contours.begin() + i);
}
Edit:-
For any one who is going to use the above code, please see below comment.
For a safer method of removing cv::Rect elements from your vector, you could use the erase-remove idiom to remove the elements which are below a certain area threshold. This method is much safer than deleting elements one-by-one by their index, as in Haris' answer, since you do not risk running off the end of the vector.
boundRect.erase(std::remove_if(boundRect.begin(), boundRect.end(),
[] (cv::Rect r)
{
const int min_area = 100;
return r.area() < min_area;
}), boundRect.end());
Here I use a C++11 lambda for the comparison. If you don't have C++11, it's easy enough to create a functor class instead.
As for removing the largest contour, you can use another function from the standard library, max_element (Again using a lambda for comparison):
boundRect.erase(std::max_element(boundRect.begin(), boundRect.end(),
[] (cv::Rect left, cv::Rect right)
{
return left.area() < right.area();
}));

Opencv How to detect 4 corners of a rectangle after applied cv::findContours and cv::drawContour

My problems is I want to find 4 corners of rectangles after using cvfindcontours ?
Here is my result:
image:
How can I find 4 corners of each rectangle?
I think the building function Cv find and draw contours have their information of the corners, so how can I read it?
(modified from here)
// Find the rotated rectangles for each contour
vector<cv::RotatedRect> cv::minRect( contours.size() );
for( size_t = 0; i < contours.size(); i++ )
{
minRect[i] = minAreaRect( cv::Mat(contours[i]) );
}
If you want the actual points on the contour, you can later find for each contour the points that is closest to each of the corners of the RotatedRect

Draw the biggest element conncted using areaContours (OpenCV )

i m new in this topic.i'm trying to use some openCV library to make a project but i have some problems with findContour,drawContours.After i read an input image and make a tresholding , i use findContours as in the code
cv::Mat cont; // i create a matrix
result2.copyTo(cont); // this is the copy of the input image tresholded
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(cont,contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0,0 ));
eiid::imshow("Im_Find", cont); // I watch the points
for( int s = 0; s< contours.size(); s++ )
{
printf(" * Contour[%d]= Area OpenCV: %.2f \n", s,cv::contourArea(contours[s])) ;
drawContours( cont, contours, s, cv::Scalar(255,255), 10, 1, hierarchy, 0, cv::Point() );
}
eiid::imshow ( "FINE" , cont);
I have 7 elements connected, ( so 7 areaContours)
This is my objective: knowing the areas of my all elemets, I want to draw, to paint, only the biggest area (and so exclude the other 6 areas )
I can't do it , someone can help me please?
(I tried to store my 7 areas in double array but i can't go on :( )
Thanks to answer me man, but i still have a problem. Your code works but i don't know why some pixels are still in the image... When i watch the final image i see my biggest contour but also some white pixels :( .. The problem is that i have to use this algoritm for more pictures, so my code has to be standard for all pictures..Maybe i use uncorrectly the functions??
My image is black and white but Color(255,255) i think is good in my case. Howerver i understood my problem is that findContours put pixels in my image,so i copyed in other image and now i don t have problems. HOwever i still have a problem :( Now i have the biggest contour,but i need to apply it on my original image to exclude some text from it..The problem is that the inside of the biggest contour is black , but in my original image , the inside is white...so i can 't get a operation like " less - " (interseption) ....:( how can i do man?
Just find the biggest one first, then draw.
int idx = -1;
double maxArea = 0.0;
for( int s = 0; s< contours.size(); s++ )
{
double area = cv::contourArea(contours[s]);
if( area > maxArea){
maxArea = area;
idx = s;
}
}
cv::drawContours( cont, contours, idx , cv::Scalar(255,255), 10, 1, hierarchy, 0, cv::Point() );
There we go.
(FYI: try not to be lazy and figure out what happens in my function below.
cv::Mat findBiggestBlob(cv::Mat & matImage){
int largest_area=0;
int largest_contour_index=0;
vector< vector<Point> > contours; // Vector for storing contour
vector<Vec4i> hierarchy;
findContours( matImage, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image
for( int i = 0; i< contours.size(); i++ ) {// iterate through each contour.
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
}
}
drawContours( matImage, contours, largest_contour_index, Scalar(255), CV_FILLED, 8, hierarchy ); // Draw the largest contour using previously stored index.
return matImage;
}