Calculate the area of intersection of two rotated rectangles in python

后端 未结 3 1530
粉色の甜心
粉色の甜心 2021-02-12 18:02

I have two 2D rotated rectangles, defined as an (center x,center y, height, width) and an angle of rotation (0-360°). How would I calculate the area of intersection of these two

3条回答
  •  清歌不尽
    2021-02-12 18:27

    intersection, pnt = contourIntersection(rect1, rect2)
    

    After looking at the possible duplicate page for this problem I couldn't find a completed answer for python so here is my solution using masking. This function will work with complex shapes on any angle, not just rectangles

    You pass in the 2 contours of your rotated rectangles as parameters and it returns 'None' if no intersection occurs or an image of the intersected area and the left/top position of that image in relation to the original image the contours were taken from

    Uses python, cv2 and numpy

    import cv2
    import math
    import numpy as np
    
    
    def contourIntersection(con1, con2, showContours=False):
    
        # skip if no bounding rect intersection
        leftmost1 = tuple(con1[con1[:, :, 0].argmin()][0])
        topmost1 = tuple(con1[con1[:, :, 1].argmin()][0])
        leftmost2 = tuple(con2[con2[:, :, 0].argmin()][0])
        topmost2 = tuple(con2[con2[:, :, 1].argmin()][0])
    
        rightmost1 = tuple(con1[con1[:, :, 0].argmax()][0])
        bottommost1 = tuple(con1[con1[:, :, 1].argmax()][0])
        rightmost2 = tuple(con2[con2[:, :, 0].argmax()][0])
        bottommost2 = tuple(con2[con2[:, :, 1].argmax()][0])
    
        if rightmost1[0] < leftmost2[0] or rightmost2[0] < leftmost1[0] or bottommost1[1] < topmost2[1] or bottommost2[1] < topmost1[1]:
            return None, None
    
        # reset top / left to 0
        left = leftmost1[0] if leftmost1[0] < leftmost2[0] else leftmost2[0]
        top = topmost1[1] if topmost1[1] < topmost2[1] else topmost2[1]
    
        newCon1 = []
        for pnt in con1:
    
            newLeft = pnt[0][0] - left
            newTop = pnt[0][1] - top
    
            newCon1.append([newLeft, newTop])
        # next
        con1_new = np.array([newCon1], dtype=np.int32)
    
        newCon2 = []
        for pnt in con2:
    
            newLeft = pnt[0][0] - left
            newTop = pnt[0][1] - top
    
            newCon2.append([newLeft, newTop])
        # next
        con2_new = np.array([newCon2], dtype=np.int32)
    
        # width / height
        right1 = rightmost1[0] - left
        bottom1 = bottommost1[1] - top
        right2 = rightmost2[0] - left
        bottom2 = bottommost2[1] - top
    
        width = right1 if right1 > right2 else right2
        height = bottom1 if bottom1 > bottom2 else bottom2
    
        # create images
        img1 = np.zeros([height, width], np.uint8)
        cv2.drawContours(img1, con1_new, -1, (255, 255, 255), -1)
    
        img2 = np.zeros([height, width], np.uint8)
        cv2.drawContours(img2, con2_new, -1, (255, 255, 255), -1)
    
        # mask images together using AND
        imgIntersection = cv2.bitwise_and(img1, img2)
    
        if showContours:
            img1[img1 > 254] = 128
            img2[img2 > 254] = 100
    
            imgAll = cv2.bitwise_or(img1, img2)
            cv2.imshow('Merged Images', imgAll)
    
        # end if
    
        if not imgIntersection.sum():
            return None, None
    
        # trim
        while not imgIntersection[0].sum():
            imgIntersection = np.delete(imgIntersection, (0), axis=0)
            top += 1
    
        while not imgIntersection[-1].sum():
            imgIntersection = np.delete(imgIntersection, (-1), axis=0)
    
        while not imgIntersection[:, 0].sum():
            imgIntersection = np.delete(imgIntersection, (0), axis=1)
            left += 1
    
        while not imgIntersection[:, -1].sum():
            imgIntersection = np.delete(imgIntersection, (-1), axis=1)
    
        return imgIntersection, (left, top)
    # end function
    

    To complete the answer so you can use the above function with the values of CenterX, CenterY, Width, Height and Angle of 2 rotated rectangles I have added the below functions. Simple change the Rect1 and Rect2 properties at the bottom of the code to your own

    def pixelsBetweenPoints(xy1, xy2):
        X = abs(xy1[0] - xy2[0])
        Y = abs(xy1[1] - xy2[1])
    
        return int(math.sqrt((X ** 2) + (Y ** 2)))
    # end function
    
    
    def rotatePoint(angle, centerPoint, dist):
        xRatio = math.cos(math.radians(angle))
        yRatio = math.sin(math.radians(angle))
        xPotted = int(centerPoint[0] + (dist * xRatio))
        yPlotted = int(centerPoint[1] + (dist * yRatio))
        newPoint = [xPotted, yPlotted]
    
        return newPoint
    # end function
    
    
    def angleBetweenPoints(pnt1, pnt2):
        A_B = pixelsBetweenPoints(pnt1, pnt2)
    
        pnt3 = (pnt1[0] + A_B, pnt1[1])
        C = pixelsBetweenPoints(pnt2, pnt3)
    
        angle = math.degrees(math.acos((A_B * A_B + A_B * A_B - C * C) / (2.0 * A_B * A_B)))
    
        # reverse if above horizon
        if pnt2[1] < pnt1[1]:
            angle = angle * -1
        # end if
    
        return angle
    # end function
    
    
    def rotateRectContour(xCenter, yCenter, height, width, angle):
        # calc positions
        top = int(yCenter - (height / 2))
        left = int(xCenter - (width / 2))
        right = left + width
    
        rightTop = (right, top)
        centerPoint = (xCenter, yCenter)
    
        # new right / top point
        rectAngle = angleBetweenPoints(centerPoint, rightTop)
        angleRightTop = angle + rectAngle
        angleRightBottom = angle + 180 - rectAngle
        angleLeftBottom = angle + 180 + rectAngle
        angleLeftTop = angle - rectAngle
    
        distance = pixelsBetweenPoints(centerPoint, rightTop)
        rightTop_new = rotatePoint(angleRightTop, centerPoint, distance)
        rightBottom_new = rotatePoint(angleRightBottom, centerPoint, distance)
        leftBottom_new = rotatePoint(angleLeftBottom, centerPoint, distance)
        leftTop_new = rotatePoint(angleLeftTop, centerPoint, distance)
    
        contourList = [[leftTop_new], [rightTop_new], [rightBottom_new], [leftBottom_new]]
        contour = np.array(contourList, dtype=np.int32)
    
        return contour
    # end function
    
    
    # rect1
    xCenter_1 = 40
    yCenter_1 = 20
    height_1 = 200
    width_1 = 80
    angle_1 = 45
    
    rect1 = rotateRectContour(xCenter_1, yCenter_1, height_1, width_1, angle_1)
    
    # rect2
    xCenter_2 = 80
    yCenter_2 = 25
    height_2 = 180
    width_2 = 50
    angle_2 = 123
    
    rect2 = rotateRectContour(xCenter_2, yCenter_2, height_2, width_2, angle_2)
    
    intersection, pnt = contourIntersection(rect1, rect2, True)
    
    if intersection is None:
        print('No intersection')
    else:
        print('Area of intersection = ' + str(int(intersection.sum() / 255)))
        cv2.imshow('Intersection', intersection)
    # end if
    
    cv2.waitKey(0)
    

提交回复
热议问题