Assertion error when running `rotatedRectangleIntersection`

Hi,

I’m currently working on computing iou on rotated rectangles, and I’m encountering an assertion error when executing rotatedRectangleIntersection:

cv2.error: OpenCV(4.2.0) /io/opencv/modules/imgproc/src/intersection.cpp:271: error: (-215:Assertion failed) fabs(normL2Sqr<float>(intersection[minI] - intersection[minJ]) - minD) < 1e-6 in function 'rotatedRectangleIntersection'

The rotated rectangles passed on to the function:

((2.46805033e+05, 4.00232694e+06), (2.64058744e+01, 6.20025761e+00), -6.21015556e+01)
((2.46805122e+05, 4.00232659e+06), (2.74820953e+01, 8.53610030e+00), -5.63376146e+01)

After plotting them, I noticed that some of box A’s corners are nearly blended with box B’s sides, maybe this confuses the algorithm (heavy doubts)

Reading the intersection.cpp file didn’t bring much information other than ptDistRemap (?) might be “corrupted”. :man_shrugging:

EDIT: the function runs fine one several rotated rect couples before raising an assertion on those 2.

Didn’t find much topics on the internet of people encountering the same assertion, any clue would be helpful !
Thanks !

hi,
A minimal code will help to reproduce assertion

Indeed

import cv2
import numpy as np


def compute_overlap_oriented_bbox(box, query_box):
    """
    Args:
        box: (5) ndarray of float
        query_box: (5) ndarray of float

    Notes:
        boxes should be in (x, y, w, h, theta) format

    Returns:
        overlap: overlap between box and query_box
    """
    area1 = box[2] * box[3]
    area2 = query_box[2] * query_box[3]

    rect_1 = ((box[0], box[1]), (box[2], box[3]), box[4])
    rect_2 = ((query_box[0], query_box[1]), (query_box[2], query_box[3]), query_box[4])

    inter_points = cv2.rotatedRectangleIntersection(rect_1, rect_2)[1]  # <-- crashes here

    if inter_points is not None:
        order_points = cv2.convexHull(inter_points, returnPoints=True)
        inter_area = cv2.contourArea(order_points)
        iou = inter_area *1.0 / (area1 + area2 - inter_area)
        return iou
    else:
        return 0.0


box_1 = np.array([2.46805033e+05, 4.00232694e+06, 2.64058744e+01, 6.20025761e+00, -6.21015556e+01])
box_2 = np.array([2.46805122e+05, 4.00232659e+06, 2.74820953e+01, 8.53610030e+00, -5.63376146e+01])

iou = compute_overlap_oriented_bbox(box_1, box_2)

print(iou)
1 Like

Here is a picture of the 2 guilty rectangles
obbox

I stripped it down some more. I can reproduce it.

import cv2 as cv
import numpy as np

def box(box):
    return ((box[0], box[1]), (box[2], box[3]), box[4])

box_1 = np.array([ 246805.033  , 4002326.94   ,      26.40587,       6.20026,     -62.10156])
box_2 = np.array([ 246805.122  , 4002326.59   ,      27.4821 ,       8.5361 ,     -56.33761])

try:
    print(cv.rotatedRectangleIntersection(box(box_1), box(box_2)))
except Exception as e:
    print(e)

offset = (246805, 4002326, 0, 0, 0)

box_1 -= offset
box_2 -= offset

print(cv.rotatedRectangleIntersection(box(box_1), box(box_2))) # that works

I’d say (1) it’s not designed for very “out there” values (2) it’s calculating some type of error based on this assumption

worth an issue on the github! no matter if you can figure out what the implementation does or not.

the implementation shouldn’t be doing this kind of check at all. it shouldn’t rely on some arbitrary, hardcoded “epsilon”.

1 Like

Thanks for investigating !
It might be a solution to offset the rectangles closer to the (0, 0) before calculating the iou.

I’ll open an issue on github (that was my idea at first, but wanted to check out the forum first)

EDIT: GitHub issue

2 Likes

This is happening because of the types used by the function RotatedRect::points(Point2f pt[]).

I opened a pull request to solve the issue: Change type used in points function from RotatedRect by gasparitiago · Pull Request #19842 · opencv/opencv · GitHub

May be I’m wrong but I don’t think that a good way to solve this issue. Wait and see reviewer advice

the math happens inside the function, doesn’t affect the API, so I’d say this is okay.

however it doesn’t do much. results are still just FP32. for center coordinates this far away from the origin, results will suck no matter what.