Complex-YOLOv4-Pytorch icon indicating copy to clipboard operation
Complex-YOLOv4-Pytorch copied to clipboard

intersection of rotate bounding box error

Open JJangD opened this issue 4 years ago • 2 comments

https://github.com/maudzung/Complex-YOLOv4-Pytorch/blob/564e8e35ad81f5a9f1a24ca4ceaf10908a100bfd/src/utils/cal_intersection_rotated_boxes.py#L78

I think there should be limitations to the range of intersection points.

In the following case, intersection values are calculated as 400.0 even though the boxes do not intersect.

box1 = torch.tensor([100, 100, 40, 10, np.pi / 2], dtype=torch.float).cuda()
box2 = torch.tensor([200, 100, 40, 20, 0], dtype=torch.float).cuda()

Shapely- box1_area: 400.00, box2_area: 800.00, inter: 0.00, iou: 0.0000
intersection from intersection_area(): 400.0

image

JJangD avatar Nov 11 '20 04:11 JJangD

@JJangD you can try this

`import torch import cv2 import numpy as np from shapely.geometry import Polygon

class Line: # ax + by + c = 0 def init(self, p1, p2): """

    Args:
        p1: (x, y)
        p2: (x, y)
    """
    self.a = p2[1] - p1[1]
    self.b = p1[0] - p2[0]
    self.c = p2[0] * p1[1] - p2[1] * p1[0]  # cross
    self.device = p1.device

def cal_values(self, pts):
    return self.a * pts[:, 0] + self.b * pts[:, 1] + self.c

def find_intersection(self, other):
    # See e.g.     https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Using_homogeneous_coordinates
    if not isinstance(other, Line):
        return NotImplemented
    w = self.a * other.b - self.b * other.a
    return torch.tensor([(self.b * other.c - self.c * other.b) / w, (self.c * other.a - self.a * other.c) / w],
                        device=self.device)

def intersection_area(rect1, rect2): """Calculate the inter

Args:
    rect1: vertices of the rectangles (4, 2)
    rect2: vertices of the rectangles (4, 2)

Returns:

"""

# Use the vertices of the first rectangle as, starting vertices of the intersection polygon.
intersection = rect1

# Loop over the edges of the second rectangle
roll_rect2 = torch.roll(rect2, -1, dims=0)
roll_rect2second = torch.roll(roll_rect2, -1, dims=0)

for p, q in zip(roll_rect2, roll_rect2second):
    if len(intersection) <= 2:
        break  # No intersection

    line = Line(p, q)
    # Any point p with line(p) <= 0 is on the "inside" (or on the boundary),
    # any point p with line(p) > 0 is on the "outside".
    # Loop over the edges of the intersection polygon,
    # and determine which part is inside and which is outside.
    new_intersection = []
    line_values = line.cal_values(intersection)

    roll_intersection = torch.roll(intersection, -1, dims=0)

    roll_line_values = torch.roll(line_values, -1, dims=0)

    for s, t, s_value, t_value in zip(intersection, roll_intersection, line_values, roll_line_values):
        if s_value <= 0:
            new_intersection.append(s)
        if s_value * t_value < 0:
            # Points are on opposite sides.
            # Add the intersection of the lines to new_intersection.
            intersection_point = line.find_intersection(Line(s, t))
            new_intersection.append(intersection_point)

    if len(new_intersection) > 0:
        intersection = torch.stack(new_intersection)
    else:
        for i in range(4):
            canditorch = torch.tensor([0, 0], device = p.device)
            new_intersection.append(canditorch)
        intersection = torch.stack(new_intersection)

# Calculate area
if len(intersection) <= 2:
    return 0.

return PolyArea2D(intersection)

def PolyArea2D(pts): roll_pts = torch.roll(pts, -1, dims=0) area = (pts[:, 0] * roll_pts[:, 1] - pts[:, 1] * roll_pts[:, 0]).sum().abs() * 0.5 return area

if name == "main":

def cvt_box_2_polygon(box):
    """
    :param array: an array of shape [num_conners, 2]
    :return: a shapely.geometry.Polygon object
    """
    # use .buffer(0) to fix a line polygon
    box.reshape(-1, 2)
    # more infor: https://stackoverflow.com/questions/13062334/polygon-intersection-error-in-shapely-shapely-geos-topologicalerror-the-opera
    return Polygon(box).buffer(0)



def get_corners_torch(x, y, w, l, yaw):

    device = x.device
    bev_corners = torch.zeros((4, 2), dtype=torch.float, device=device)
    cos_yaw = torch.cos(yaw)
    sin_yaw = torch.sin(yaw)
    # front left
    bev_corners[0, 0] = x - w / 2 * cos_yaw - l / 2 * sin_yaw
    bev_corners[0, 1] = y - w / 2 * sin_yaw + l / 2 * cos_yaw

    # rear left
    bev_corners[1, 0] = x - w / 2 * cos_yaw + l / 2 * sin_yaw
    bev_corners[1, 1] = y - w / 2 * sin_yaw - l / 2 * cos_yaw

    # rear right
    bev_corners[2, 0] = x + w / 2 * cos_yaw + l / 2 * sin_yaw
    bev_corners[2, 1] = y + w / 2 * sin_yaw - l / 2 * cos_yaw

    # front right
    bev_corners[3, 0] = x + w / 2 * cos_yaw - l / 2 * sin_yaw
    bev_corners[3, 1] = y + w / 2 * sin_yaw + l / 2 * cos_yaw

    return bev_corners


# Show convex in an image

img_size = 300
img = np.zeros((img_size, img_size, 3))
img = cv2.resize(img, (img_size, img_size))

box1 = torch.tensor([100, 100, 40, 5, np.pi / 2], dtype=torch.float).cuda()
box2 = torch.tensor([200, 200, 50, 20, 0], dtype=torch.float).cuda()

box1_conners = get_corners_torch(box1[0], box1[1], box1[2], box1[3], box1[4])
box1_polygon = cvt_box_2_polygon(box1_conners)
box1_area = box1_polygon.area

box2_conners = get_corners_torch(box2[0], box2[1], box2[2], box2[3], box2[4])
box2_polygon = cvt_box_2_polygon(box2_conners)
box2_area = box2_polygon.area

intersection = box2_polygon.intersection(box1_polygon).area
union = box1_area + box2_area - intersection
iou = intersection / (union + 1e-16)

print('Shapely- box1_area: {:.2f}, box2_area: {:.2f}, inter: {:.2f}, iou: {:.4f}'.format(box1_area, box2_area,
                                                                                         intersection, iou))

print('intersection from intersection_area(): {}'.format(intersection_area(box1_conners, box2_conners)))


img = cv2.polylines(img, [box1_conners.cpu().numpy().astype(np.int)], True, (255, 0, 0), 2)
img = cv2.polylines(img, [box2_conners.cpu().numpy().astype(np.int)], True, (0, 255, 0), 2)

while True:
    cv2.imshow('img', img)
    if cv2.waitKey(0) & 0xff == 27:
        break`

Shapely- box1_area: 400.00, box2_area: 800.00, inter: 0.00, iou: 0.0000 intersection from intersection_area(): 0.0

wangx1996 avatar Jan 15 '21 02:01 wangx1996

@wangx1996 Can you describe what the issue was and what the solution was? Is your insertion of the second roll operation necessary?

JonathanCMitchell avatar Mar 25 '21 22:03 JonathanCMitchell