Home > Mobile >  How to remove box overlapping 90% with other bounding box
How to remove box overlapping 90% with other bounding box

Time:01-04

I have list of bounding boxes. When I plot them, there are some boxes overlapping with other. How can I identify them?

Here is attached example

enter image description here

Here we can see box 3,4 is overlapping, 10,11 is overlapping and box 7 is inside box 6. So I want to remove box which are 80% overlapping or 80% inside other box.

Here are the coordinates

boxes=[(0, (156.52566528320312, 411.3934326171875, 508.0946350097656, 445.0401611328125)),
 (1, (153.34573364257812, 447.56744384765625, 1044.3194580078125, 612.4976196289062)),
 (2, (150.6321258544922, 662.0474243164062, 1076.75439453125, 899.3271484375)),
 (3, (154.38674926757812, 945.8661499023438, 1060.038330078125, 1026.8682861328125)),
 (4, (138.6205596923828, 951.3151245117188, 1035.56884765625, 1027.590087890625)),
 (5, (1245.50048828125, 410.4453430175781, 1393.0701904296875, 445.3376770019531)),
 (6, (1240.206787109375, 456.7169189453125, 2139.934326171875, 659.1046752929688)),
 (7, (1236.478759765625, 568.0098876953125, 2145.948486328125, 654.7606201171875)),
 (8, (1244.784912109375, 702.7620239257812, 2121.079345703125, 736.1748046875)),
 (9, (1244.885986328125, 746.2633666992188, 2151.534423828125, 991.8198852539062)),
 (10, (1251.84814453125, 1031.8487548828125, 2134.333251953125, 1153.9320068359375)),
 (11, (1254.38330078125, 1035.0196533203125, 2163.969970703125, 1153.2939453125))]

Here is the code, I used to generate above image

import cv2
import matplotlib.pyplot as plt
import numpy as np
img = np.ones([1654,2339,3],dtype=np.uint8)*255
for i in boxes:
    box=[int(i) for i in i[1]]
    image = cv2.rectangle(img, (box[0],box[1]), (box[2],box[3]), (0,0,0), 5)
    cv2.putText(
          img = image,
          text = str(i[0]),
          org = (box[0] int(np.random.randint(0, high=500, size=1)),box[1]),
          fontFace = cv2.FONT_HERSHEY_DUPLEX,
          fontScale = 3.0,
          color = (0, 0, 0),
          thickness = 3
        )
    plt.imshow(img)

The output I want is box 0,1,2,3 or 4 (the larger one), 5,6 ,8,9,10 or 11 (the larger one)

The solution I found work but not for all cases Here is my solution

#x_1, y_1, x_2, y_2
for i in range(len(boxes)-1):
    x_min_1,y_min_1,x_max_1,y_max_1=boxes[i][1][0],boxes[i][1][1],boxes[i][1][2],boxes[i][1][3]
    x_min_2,y_min_2,x_max_2,y_max_2=boxes[i 1][1][0],boxes[i 1][1][1],boxes[i 1][1][2],boxes[i 1][1][3]
    box_1_in_box_2 = ((x_max_2> x_min_1 >= x_min_2) or \
                      (x_max_2>= x_max_1 >x_min_2)) and \
                        ((y_max_2> y_min_1 >= y_min_2) or \
                         (y_max_2>= y_max_1 > y_min_2))
    box_2_in_box_1 = ((x_max_1> x_min_2 >= x_min_1) or (x_max_1>= x_max_2 >x_min_1)) and ((y_max_1> y_min_2 >= y_min_1) or (y_max_1>= y_max_2 > y_min_1))

    overlap = box_1_in_box_2 or box_2_in_box_1
    print(i,overlap)

CodePudding user response:

For each box, calculate it's area. Then, for each other box, calculate how much that box overlaps in the x axis, and if it's greater than 0, calculate how much it overlaps in the y axis.

remove = []
areas = [(b[1][2] - b[1][0]) * (b[1][3] - b[1][2]) for b in boxes]
for i in range(len(boxes)-1):
  if i in remove:
    # don't check boxes you're already removing
    continue
  # Get x_1_1, y_1_1, x_2_1, y_2_1 for box i
  j = i   1
  while j < len(boxes):
    if j in remove:
      j  = 1
      continue
    # Get x_1_2, y_1_2, x_2_2, y_2_2 for box i
    x_overlap = min(x_2_1, x_2_2) - max(x_1_1, x_1_2)
    # if there's no overlap, x_overlap will be negative or zero
    x_overlap = max(0, x_overlap)
    if not x_overlap:
      j  = 1
      continue
    y_overlap = min(y_2_1, y_2_2) - max(y_1_1, y_1_2)
    # if there's no overlap, y_overlap will be negative or zero
    y_overlap = max(0, y_overlap)

    overlap_area = x_overlap * y_overlap

    # if overlap area is greater than 90% of either box area
    # add that index to remove
    if overlap_area > areas[i] or overlap_area > areas[j]:
      remove.append(i if areas[i] < areas[j] else j)
    j  = 1
  

CodePudding user response:

For this we can use the "non-maximum suppresion" method. We will iterate each box by comparing it with the others, calculating the air of each one, then calculating the air of a possible intersection. Then we will get their percenage of overlap and depending on the threshold we will delete them or not.

from typing import TypeAlias

Box: TypeAlias = tuple[float, float, float, float]

def calcul_area(box: Box):
    x1, y1, x2, y2 = box
    return abs(x1 - x2) * abs(y1 - y2)

def nms_area(box_lhs: Box, box_rhs: Box, thresh_iou: float) -> float:
    x1_lhs, y1_lhs, x2_lhs, y2_lhs = box_lhs
    x1_rhs, y1_rhs, x2_rhs, y2_rhs = box_rhs

    area_lhs = calcul_area(box_lhs)
    area_rhs = calcul_area(box_rhs)

    # Determines the coordinates of the intersection box
    x1_inter = max(x1_lhs, x1_rhs)
    y1_inter = max(y1_lhs, y1_rhs)
    x2_inter = min(x2_lhs, x2_rhs)
    y2_inter = min(y2_lhs, y2_rhs)

    # Determines if the boxes overlap or not
    # If one of the two is equal to 0, the boxes do not overlap
    inter_w = max(0.0, x2_inter - x1_inter)
    inter_h = max(0.0, y2_inter - y1_inter)

    if inter_w == 0.0 or inter_h == 0.0:
        return 0.0

    intersection_area = inter_w * inter_h
    union_area = area_lhs   area_rhs - intersection_area

    # See if the smallest box is not mostly in the largest one
    if intersection_area / area_rhs >= thresh_iou:
        return area_rhs / intersection_area

    iou: float = intersection_area / union_area

    return iou


# thresh_iou is a value between 0 and 1 determining the minimum percentage of overlap for a box to be deleted
# For example 90% = 0.9
def nms_algorithm(
    boxes: list[tuple[int, Box]], thresh_iou: float
) -> list[tuple[int, Box]]:
    if len(boxes) == 0:
        return []

    keeped_box_id, keeped_box = boxes.pop(0)

    tmp_boxes: list[tuple[int, Box]] = []
    for (id, box) in boxes:
        if nms_area(keeped_box, box, thresh_iou) < thresh_iou:
            tmp_boxes.append((id, box))
        else:
            print(f"Delete box {id}. Overlapping with {keeped_box_id}.")

    return [
        (keeped_box_id, keeped_box),
        *nms_algorithm(tmp_boxes, thresh_iou),
    ]


boxes: list[tuple[int, Box]] = [
    (0, (156.52566528320312, 411.3934326171875, 508.0946350097656, 445.0401611328125)),
    (1, (153.34573364257812, 447.56744384765625, 1044.3194580078125, 612.4976196289062)),
    (2, (150.6321258544922, 662.0474243164062, 1076.75439453125, 899.3271484375)),
    (3, (154.38674926757812, 945.8661499023438, 1060.038330078125, 1026.8682861328125)),
    (4, (138.6205596923828, 951.3151245117188, 1035.56884765625, 1027.590087890625)),
    (5, (1245.50048828125, 410.4453430175781, 1393.0701904296875, 445.3376770019531)),
    (6, (1240.206787109375, 456.7169189453125, 2139.934326171875, 659.1046752929688)),
    (7, (1236.478759765625, 568.0098876953125, 2145.948486328125, 654.7606201171875)),
    (8, (1244.784912109375, 702.7620239257812, 2121.079345703125, 736.1748046875)),
    (9, (1244.885986328125, 746.2633666992188, 2151.534423828125, 991.8198852539062)),
    (10, (1251.84814453125, 1031.8487548828125, 2134.333251953125, 1153.9320068359375)),
    (11, (1254.38330078125, 1035.0196533203125, 2163.969970703125, 1153.2939453125)),
]

if __name__ == "__main__":
    print(f"Entry boxes:{[id for id, _ in boxes]}\n")

    # Sort the boxes from the largest to the smallest
    # Will be useful to check if a smaller box is in the other one later
    boxes.sort(key=lambda x: calcul_area(x[1]), reverse=True)

    keeped_boxes = nms_algorithm(boxes, 0.9)
    keeped_boxes.sort(key=lambda x: x[0])

    print(f"\nKeeped boxes:{[id for id, _ in keeped_boxes]}")

And for your example I get:

Entry boxes:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

Delete box 7. Overlapping with 6.
Delete box 11. Overlapping with 10.
Delete box 4. Overlapping with 3.

Keeped boxes:[0, 1, 2, 3, 5, 6, 8, 9, 10]
  • Related