Question

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:

enter image description here

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.

Was it helpful?

Solution 2

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

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top