0
votes

Hello I have an array of rectangles rect that represent the bounding boxes for detected objects. Often, these rectangles overlap and I would like to merge all rectangles that are intersecting into one big rectangle (and have multiple rectangles on screen if there are two separate clusters for example). Is there a way to do this?

I have already tried cv2.groupRectangles(rect, 1, .5), but clusters of rectangles are grouped into one rectangle in the middle that is the same size as the original rectangles.

EDIT:

Here is the pic without grouping: enter image description here

Here is the pic with grouping and the method described below enter image description here

To clarify a little more about what I am looking for I would just want red boxes over the individual blocks (Or ideally one box over the entire row), instead of in the middle, just so can condense the amount of boxes that I need to do computations for to speed up the program

2

2 Answers

3
votes

You are to go through groupped rectangles and check them with source rectangles and to rewrite their bounds if they intersects while anything changes (see flag):

def get_intersection_area(box_a, box_b):
    x_a = max(box_a[0], box_b[0])
    y_a = max(box_a[1], box_b[1])
    x_b = min(box_a[2], box_b[2])
    y_b = min(box_a[3], box_b[3])
    return max(0, x_b - x_a) * max(0, y_b - y_a)


groupped = cv2.groupRectangles(rect, 1, 0.5)[0]
groupped = [(x, y, x+w, y+h) for x, y, w, h in groupped]
rect = [(x, y, x+w, y+h) for x, y, w, h in rect]
flag = True
while flag:
    flag = False
    for i in range(len(groupped)):
        for r in rect:
            if 0 < get_intersection_area(groupped[i], r) <\
                    (r[2]-r[0])*(r[3]-r[1]):
                groupped[i] = (
                    min(groupped[i][0], r[0]), min(groupped[i][1], r[1]),
                    max(groupped[i][2], r[2]), max(groupped[i][3], r[3]),)
                flag = True

(I have converted boxes from tlwh format to tlrb as it more convenient for me)

Here is what it gives now:

result

To keep rects what aren't intersected don't use cv2.groupRectangles()
and just say groupped = np.copy(rect), though it may do an extra job this way and some optimization is needed.

1
votes

An easy approach is to draw all rectangles filled on a mask. Then perform findContours on that mask to find the combined shapes. To make the result a square you can use boundingRect to find the x, y, width and height. Note that this combines all rectangles completely, not just the overlapping part.

You can find sample code for combining rectangles example here