Opencv Grouprectangles - Getting Grouped and Ungrouped Rectangles

OpenCV groupRectangles - getting grouped and ungrouped rectangles

The solution I ended up going with was to duplicate all of the initial rectangles before calling groupRectangles. That way every input rectangle is guaranteed to be grouped with at least one other rectangle, and will appear in the output:

int size = rects.size();
for( int i = 0; i < size; i++ )
{
rects.push_back(Rect(rects[i]));
}
groupRectangles(rects, 1, 0.2);

groupRectangles on OpenCV not performing as expected

I modified the eps parameter to 0.05.

results = cv2.groupRectangles(np.concatenate((rect_old, rect_old)),1,eps=0.05)[0]
img2 = img.copy()
for r in results:
cv2.rectangle(img2,(r[0], r[1]),(r[2], r[3]),(255,0,0),1)

Sample Image

EDIT

Here is some info on the parameters take more this page:

Parameters:

  • rectList – Input/output vector of rectangles. Output vector includes retained and grouped rectangles. (The Python list is not modified in place.)

  • groupThreshold – Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it.

  • eps – Relative difference between sides of the rectangles to merge them into a group.

OpenCV: Merging overlapping rectangles

void mergeOverlappingBoxes(std::vector<cv::Rect> &inputBoxes, cv::Mat &image, std::vector<cv::Rect> &outputBoxes)
{
cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1); // Mask of original image
cv::Size scaleFactor(10,10); // To expand rectangles, i.e. increase sensitivity to nearby rectangles. Doesn't have to be (10,10)--can be anything
for (int i = 0; i < inputBoxes.size(); i++)
{
cv::Rect box = inputBoxes.at(i) + scaleFactor;
cv::rectangle(mask, box, cv::Scalar(255), CV_FILLED); // Draw filled bounding boxes on mask
}

std::vector<std::vector<cv::Point>> contours;
// Find contours in mask
// If bounding boxes overlap, they will be joined by this function call
cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
for (int j = 0; j < contours.size(); j++)
{
outputBoxes.push_back(cv::boundingRect(contours.at(j)));
}
}

Efficient way to combine intersecting bounding rectangles

To accomplish what you want we'll be using findContours. The key point here is to understand how it works when mode is set to CV_RETR_TREE. In this case, hierarchy is constructed in a way that every even depth level contains external contours, while odd depth levels contain internal contours. What we need here is to traverse the hierarchy tree printing the contours associated with even depth levels.

First we find the contours of an image called original

typedef std::vector<std::vector<cv::Point> > Contours;
typedef std::vector<cv::Vec4i> Hierarchy;

Contours contours;
Hierarchy hierarchy;
cv::findContours(original, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);

To print the external contours on an image called processed we need a recursive function.

void printExternalContours(cv::Mat img, Contours const& contours, Hierarchy const& hierarchy, int const idx)
{
//for every contour of the same hierarchy level
for(int i = idx; i >= 0; i = hierarchy[i][0])
{
//print it
cv::drawContours(img, contours, i, cv::Scalar(255));

//for every of its internal contours
for(int j = hierarchy[i][2]; j >= 0; j = hierarchy[j][0])
{
//recursively print the external contours of its children
printExternalContours(img, contours, hierarchy, hierarchy[j][2]);
}
}
}

printExternalContours(processed, contours, hierarchy, 0);

The result is shown bellow, where original and processed are displayed side by side.

original processed

If you absolutely need rectangular shapes, you just need to use boundingRect to get the minimum enclosing rectangle given a set of points (every single contour in this case) and use rectangle for the drawing. In other words, substitute

cv::drawContours(img, contours, i, cv::Scalar(255));

by

cv::rectangle(img, cv::boundingRect(contours[i]), cv::Scalar(255));

findContours expects a single 8-bit image, sou you could either make a gray image from your originals and then threshold it to get a perfect black background or, perhaps it would suffice to use the red channel in your case, just make sure the background is perfectly black.

Regarding the complexity of findContours, I can't attest it is any better than O(N^2), nor haven't I found any input on that after a quick google search, but I trust OpenCV implements the best known algorithm.

Merging Overlapping Rectangle in OpenCV

Problem

Seems as if you are displaying each contour you are getting. You don't have to do that. Follow the algorithm and code given below.

Algorithm

In this case what you can do is iterate through each contour that you detect and select the biggest boundingRect. You don't have to display each contour you detect.

Here is a code that you can use.

Code

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
}

}

Regards

How to remove the CvSeq points which results in overlapping Squares/rectangles in opencv?

Find overlapping rectangles and apply cv::groupRectangles()

More here.



Related Topics



Leave a reply



Submit