Python merges multiple intersecting rectangular boxes

Python merges multiple intersecting rectangular boxes

  • Preface
  • Prerequisites
  • Related introduction
  • lab environment
  • Python merges multiple intersecting rectangular boxes
    • Code


Foreword

  • Due to my limited level, errors and omissions will inevitably occur. Please criticize and correct me.
  • For more exciting content, you can click to view the Python daily operations column, YOLO series column, natural language processing column or my personal homepage.
  • Face disguise detection based on DETR
  • YOLOv7 trains its own data set (mask detection)
  • YOLOv8 trains its own data set (football detection)
  • YOLOv5: TensorRT accelerates YOLOv5 model inference
  • YOLOv5: IoU, GIoU, DIoU, CIoU, EIoU
  • Playing with Jetson Nano (5): TensorRT accelerates YOLOv5 target detection
  • YOLOv5: Add SE, CBAM, CoordAtt, ECA attention mechanism
  • YOLOv5: Interpretation of yolov5s.yaml configuration file and adding small target detection layer
  • Python converts COCO format instance segmentation data set to YOLO format instance segmentation data set
  • YOLOv5: Use version 7.0 to train your own instance segmentation model (instance segmentation of vehicles, pedestrians, road signs, lane lines, etc.)
  • Use Kaggle GPU resources to experience the Stable Diffusion open source project for free

Prerequisite

  • Familiar with Python

Related introduction

  • Python is a cross-platform computer programming language. It is a high-level scripting language that combines interpretation, compilation, interactivity and object-oriented. It was originally designed for writing automated scripts (shells). As the version is constantly updated and new language features are added, it is increasingly used for the development of independent, large-scale projects.

Experimental environment

  • Python 3.x (object-oriented high-level language)

Python merges multiple intersecting rectangular boxes

Code implementation

import cv2
import numpy as np


def xyxy2xywh(rect):
    '''
    (x1,y1,x2,y2) -> (x,y,w,h)
    '''
    return [rect[0],rect[1],rect[2]-rect[0],rect[3]-rect[1]]

def xywh2xyxy(rect):
    '''
    (x,y,w,h) -> (x1,y1,x2,y2)
    '''
    return [rect[0],rect[1],rect[0] + rect[2],rect[1] + rect[3]]


def is_RecA_RecB_interSect(RecA, RecB): # Rec = [xmin,ymin,xmax,ymax]
    # Get [xmin, ymin, xmax, ymax] of the intersection area
    x_A_and_B_min = max(RecA[0], RecB[0])
    y_A_and_B_min = max(RecA[1], RecB[1])
    x_A_and_B_max = min(RecA[2], RecB[2])
    y_A_and_B_max = min(RecA[3], RecB[3])
    # Calculate the area of the intersection. When (xmax - xmin) is negative, it means that the A and B boxes have no intersection and are set to 0 directly. (ymax - ymin)Similarly.
    interArea = max(0, x_A_and_B_max - x_A_and_B_min) * max(0, y_A_and_B_max - y_A_and_B_min)
    return interArea > 0

def merge_RecA_RecB(RecA, RecB): # Rec = [xmin,ymin,xmax,ymax]
    # Get [xmin,ymin,xmax,ymax] of the merged area
    xmin = min(RecA[0], RecB[0])
    ymin = min(RecA[1], RecB[1])
    xmax = max(RecA[2], RecB[2])
    ymax = max(RecA[3], RecB[3])
    return [xmin,ymin, xmax,ymax]

# def merge_rect(box,box_len):
# if box_len== 1:
# return box

# for i in range(box_len):
# RecA_xywh = box[i]
# RecA_xyxy = xywh2xyxy(RecA_xywh)
# for j in range(i + 1,box_len):
# RecB_xywh = box[j]
# RecB_xyxy = xywh2xyxy(RecB_xywh)
# print(is_RecA_RecB_interSect(RecA_xyxy, RecB_xyxy))
# if is_RecA_RecB_interSect(RecA_xyxy, RecB_xyxy):
#rect_xyxy = merge_RecA_RecB(RecA_xyxy, RecB_xyxy)
#rect_xywh = xyxy2xywh(rect_xyxy)
# box.remove(RecA_xywh)
# box.remove(RecB_xywh)
# box.append(rect_xywh)
# box_len = len(box)
# merge_rect(box,box_len)
# # If the return box is missing here, an error will be reported.
# return box


# def merge_rect(box, box_len):
    
# if box_len == 1:
# return box

# for i in range(box_len):
# RecA_xywh = box[i]
# RecA_xyxy = xywh2xyxy(RecA_xywh)
# for j in range(i + 1, box_len):
# RecB_xywh = box[j]
# RecB_xyxy = xywh2xyxy(RecB_xywh)
# if is_RecA_RecB_interSect(RecA_xyxy, RecB_xyxy):
#rect_xyxy = merge_RecA_RecB(RecA_xyxy, RecB_xyxy)
#rect_xywh = xyxy2xywh(rect_xyxy)
# # Use remove(elem) to remove elements
# box.remove(RecA_xywh)
# box.remove(RecB_xywh)

# box.append(rect_xywh)
# box_len = len(box)
# merge_rect(box, box_len)
# # Return to the previous loop to avoid repeated processing of merged rectangles
# return box
# return box



'''
Recursion is a way for a procedure or function to call itself directly or indirectly in its definition or specification.
It usually transforms a large and complex problem into a smaller problem similar to the original problem to solve.
Therefore, the most important thing in the recursive process is to see whether the original problem can be decomposed into smaller sub-problems. This is the key to using recursion.


    Termination condition: The number of rectangular boxes is 1 or empty.
    Return value: The newly merged rectangular box
    Tasks at this level: What you need to do at each level is to traverse the subsequent rectangles starting from it and look for rectangles that can be merged with it.

'''
def merge_rect(box):
    '''
    Merge overlapping boxes

    Input parameters: box :[[x,y,w,h],...]

    return:
        Merged box:[[x,y,w,h],...]
    '''
    if len(box) == 1 or len(box) == 0 : # The number of rectangular boxes is 1 or empty
        return box

    for i in range(len(box)):
        RecA_xywh = box[i]
        RecA_xyxy = xywh2xyxy(RecA_xywh)
        for j in range(i + 1, len(box)):
            RecB_xywh = box[j]
            RecB_xyxy = xywh2xyxy(RecB_xywh)
            if is_RecA_RecB_interSect(RecA_xyxy, RecB_xyxy):
                rect_xyxy = merge_RecA_RecB(RecA_xyxy, RecB_xyxy)
                rect_xywh = xyxy2xywh(rect_xyxy)
                # Use remove(elem) to remove elements
                box.remove(RecA_xywh)
                box.remove(RecB_xywh)
                box.append(rect_xywh)
                merge_rect(box)
                # Return to the previous loop to avoid repeated processing of merged rectangles
                return box
    return box


if __name__=="__main__":
    # original
    box = [[256,256,10,10],[10,10,15,15],[20,20,10,10],[100,100,150,150],
           [200,200,100,100],[400,400,15,15],[410,410,15,15],[420,420,10,10]] # (x,y,w,h)
    print("Original rectangular box:",box)
    
    img = np.ones([512, 512, 3], np.uint8)
    for x,y,w,h in box:
        img = cv2.rectangle(img, (x,y), (x + w,y + h), (0, 255, 0), 2)
    cv2.imshow('origin', img)

    # After merging
    merged_box = merge_rect(box)
    print("Merge rectangular box:",merged_box)

    img = np.ones([512, 512, 3], np.uint8)
    for x,y,w,h in merged_box:
        img = cv2.rectangle(img, (x,y), (x + w,y + h), (0, 0, 255), 2)
    cv2.imshow('merged', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

  • Due to my limited level, errors and omissions will inevitably occur. Please criticize and correct me.
  • For more exciting content, you can click to view the Python daily operations column, YOLO series column, natural language processing column or my personal homepage.
  • Face disguise detection based on DETR
  • YOLOv7 trains its own data set (mask detection)
  • YOLOv8 trains its own data set (football detection)
  • YOLOv5: TensorRT accelerates YOLOv5 model inference
  • YOLOv5: IoU, GIoU, DIoU, CIoU, EIoU
  • Playing with Jetson Nano (5): TensorRT accelerates YOLOv5 target detection
  • YOLOv5: Add SE, CBAM, CoordAtt, ECA attention mechanism
  • YOLOv5: Interpretation of yolov5s.yaml configuration file and adding small target detection layer
  • Python converts COCO format instance segmentation data set to YOLO format instance segmentation data set
  • YOLOv5: Use version 7.0 to train your own instance segmentation model (instance segmentation of vehicles, pedestrians, road signs, lane lines, etc.)
  • Use Kaggle GPU resources to experience the Stable Diffusion open source project for free