Remove bounding rect with an area < n OpenCV - c++

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();
}));

Related

How to find coordinates of largest rectangle in OpenCV?

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.

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;
}

Get List of Black Pixel of a cv::Mat

I' am actually working with a cv::Mat with B&W pixels.
I'am searching for a way to get a list of my black point in this Mat.
Does someone know how to do such thing ?
I want to do that because I want to detect the bounding rect of this points.
(The best is to get them back in a vector)
somekind of :
cv::Mat blackAndWhite;
std::vector<cv::Point> blackPixels = MAGIC_FUNCTION(blackAndWhite);
Thanks for your help.
Edit: I want to precise that I want the best practices, the more Opencv compliant as possible.
You can traverse the cv::Mat to check the pixels that are 0, and get the x and y coordinates from the linear index if the matrix is continuous in memory:
// assuming your matrix is CV_8U, and black is 0
std::vector<cv::Point> blackPixels;
unsigned int const *p = blackAndWhite.ptr<unsigned char>();
for(int i = 0; i < blackAndWhite.rows * blackAndWhite.cols; ++i, ++p)
{
if(*p == 0)
{
int x = i % blackAndWhite.cols;
int y = i / blackAndWhite.cols;
blackPixels.push_back(cv::Point(x, y));
}
}
This example from OpenCV shows how to do exactly what you want: Creating Bounding boxes and circles for contours. Basically, it this:
// ...
/// Find contours
findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, 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]) );
minEnclosingCircle( (Mat)contours_poly[i], center[i], radius[i] );
}

C++ OpenCV Eliminate smaller Contours

I am developing a OpenCV project.
I am currently working on detecting contours of particular ROI (Regoin Of Interest). What I want to achieve is to eliminate all the smaller contours in other words I do not want these smaller contours to be drown at all.
So Far if I have coded this algorithm to do this job:
CODE:
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(mBlur, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
//----------------------------------------------------------------------------->
//Contours Vectors
vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect (contours.size());
vector<Point2f> ContArea(contours.size());
Mat drawing = Mat::zeros( range_out.size(), CV_8UC3 );
//----------------------------------------------------------------------------->
//Detecting Contours
for( int i = 0; i < contours.size(); i++ )
{
ContArea.clear();
ContArea.push_back(Point2f(boundRect[i].x, boundRect[i].y));
ContArea.push_back(Point2f(boundRect[i].x + boundRect[i].width, boundRect[i].y));
ContArea.push_back(Point2f(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height));
ContArea.push_back(Point2f(boundRect[i].x, boundRect[i].y + boundRect[i].height));
double area = contourArea(ContArea);
if(area > 2000)
{
approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]));
cout<<"The area of Contour: "<<i<< " is: " <<area<<endl;
}
}
/// Draw polygonal contour + bonding rects
//////////////////////////////////////////////////////////////////////////////////
for( int i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar(255,255,255);
drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
fillPoly(drawing, contours, Scalar(255,0,0));
}
The problem here is that it looks like the if(area > 2000) statement is not executed even dough i know some of areas present in image are way bigger than this.
I have been trying a lot of different solutions but this looks to be the most appropriate one to me.
THE KEY QUESTIONS:
Is it Possible to achieve what I want with the given code....?
If so can anyone see where I am going wrong with this
Else Could someone suggest some kind of solution or a good online source....?
If you want to remove based on the size of ROIs, you can do as follow (based on the example of bounding box of opencv):
vector< vector< Point > > contours_poly( contours.size() );
vector< Rect > boundRect (contours.size() );
vector< Point2f > centeres ( contours.size() );
vector< float > radiuses ( contours.size() );
// finding the approximate rectangle and circle of contours
for( int i = 0; i < contours.size(); i++ )
{
approxPolyDP( Mat ( contours[i] ), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat ( contours_poly[i] ) );
minEnclosingCircle( ( Mat ) contours_poly[i], centeres[i], radiuses[i] );
}
The above code snippet gives you the approximate rectangles of contours (boundRect), and approximate circles (centers, radiuses). After this you must be able to call contourArea(); and if it's smaller than certain threshold, you can eliminate it.
If you just want to remove contours that are smaller certain length, you can calculate it's length, look at the answer to similar question, and it seems you can use arcLength() function. I think something like this: double perimeter = arcLength ( Mat ( contours[i] ), true );.

OpenCV Draw draw contours of 2 largest objects

I am doing a OpenCV software that detects boxing gloves therefore i want to detect and draw only 2 largest contours (one for each boxing glove).
My software draws contours for everything and some things are noise only which ofcourse i dont want
My code for drawing contours:
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(mBlur, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
//----------------------------------------------------------------------------->
vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect (contours.size());
vector<Point2f> boundingBoxArea(boundRect.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(0,0,255);
drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
fillPoly(drawing, contours, Scalar(255,0,0));
}
Here is an Image Example:
My program already segments the gloves by color, The problelem is that at times small contours are drawn in random locations due to noise. Now of course the gloves contours are by far dominant and this is why i want to keep only the contours of these. Hope this makes my question clearer
Could someone suggest a solution please
I am coding in C++ environment
regards
The easiest way to look at the two largest contours is to simply look at the contours size. Something like this should do the trick:
int largestIndex = 0;
int largestContour = 0;
int secondLargestIndex = 0;
int secondLargestContour = 0;
for( int i = 0; i< contours.size(); i++ )
{
if(contours[i].size() > largestContour){
secondLargestContour = largestContour;
secondLargestIndex = largestIndex;
largestContour = contours[i].size();
largestIndex = i;
}else if(contours[i].size() > secondLargestContour){
secondLargestContour = contours[i].size();
secondLargestIndex = i;
}
}
Scalar color = Scalar(0,0,255);
drawContours( drawing, contours, largestIndex, color, CV_FILLED, 8);
drawContours( drawing, contours, secondLargestIndex, color, CV_FILLED, 8);
It seems that vector<vector<Point> > contours stores all your contours. What you need to do is iterate on this vector and do a little arithmetics with it elements, to be able to detect the 2 largest contours in the vector.
On this answer I shared code that detects the largest contour in a vector<vector<Point> >, so you are half way there.