Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Add] Add rotate iou cpu evaluation on kitti dateset #2882

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions mmdet3d/evaluation/functional/kitti_utils/eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import numba
import numpy as np
import torch


@numba.jit
Expand Down Expand Up @@ -115,8 +116,12 @@ def image_box_overlap(boxes, query_boxes, criterion=-1):


def bev_box_overlap(boxes, qboxes, criterion=-1):
from .rotate_iou import rotate_iou_gpu_eval
riou = rotate_iou_gpu_eval(boxes, qboxes, criterion)
if torch.cuda.is_available():
from .rotate_iou import rotate_iou_gpu_eval
riou = rotate_iou_gpu_eval(boxes, qboxes, criterion)
else:
from .rotate_iou_cpu import rotate_iou_cpu_eval
riou = rotate_iou_cpu_eval(boxes, qboxes, criterion)
return riou


Expand Down Expand Up @@ -153,9 +158,14 @@ def d3_box_overlap_kernel(boxes, qboxes, rinc, criterion=-1):


def d3_box_overlap(boxes, qboxes, criterion=-1):
from .rotate_iou import rotate_iou_gpu_eval
rinc = rotate_iou_gpu_eval(boxes[:, [0, 2, 3, 5, 6]],
qboxes[:, [0, 2, 3, 5, 6]], 2)
if torch.cuda.is_available():
from .rotate_iou import rotate_iou_gpu_eval
rinc = rotate_iou_gpu_eval(boxes[:, [0, 2, 3, 5, 6]],
qboxes[:, [0, 2, 3, 5, 6]], 2)
else:
from .rotate_iou_cpu import rotate_iou_cpu_eval
rinc = rotate_iou_cpu_eval(boxes[:, [0, 2, 3, 5, 6]],
qboxes[:, [0, 2, 3, 5, 6]], 2)
d3_box_overlap_kernel(boxes, qboxes, rinc, criterion)
return rinc

Expand Down
254 changes: 254 additions & 0 deletions mmdet3d/evaluation/functional/kitti_utils/rotate_iou_cpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
# Copyright (c) OpenMMLab. All rights reserved.
#####################
# Based on https://github.com/hongzhenwang/RRPN-revise
# Licensed under The MIT License
# Author: yanyan, [email protected]
#####################
import math

import numpy as np


def div_up(m, n):
return m // n + (m % n > 0)


def trangle_area(a, b, c):
return ((a[0] - c[0]) * (b[1] - c[1]) - (a[1] - c[1]) *
(b[0] - c[0])) / 2.0


def area(int_pts, num_of_inter):
area_val = 0.0
for i in range(num_of_inter - 2):
area_val += abs(
trangle_area(int_pts[:2], int_pts[2 * i + 2:2 * i + 4],
int_pts[2 * i + 4:2 * i + 6]))
return area_val


def sort_vertex_in_convex_polygon(int_pts, num_of_inter):
if num_of_inter > 0:
center = np.zeros((2, ), dtype=np.float32)
center[:] = 0.0
for i in range(num_of_inter):
center[0] += int_pts[2 * i]
center[1] += int_pts[2 * i + 1]
center[0] /= num_of_inter
center[1] /= num_of_inter
v = np.zeros((2, ), dtype=np.float32)
vs = np.zeros((16, ), dtype=np.float32)
for i in range(num_of_inter):
v[0] = int_pts[2 * i] - center[0]
v[1] = int_pts[2 * i + 1] - center[1]
d = math.sqrt(v[0] * v[0] + v[1] * v[1])
v[0] = v[0] / d
v[1] = v[1] / d
if v[1] < 0:
v[0] = -2 - v[0]
vs[i] = v[0]
j = 0
temp = 0
for i in range(1, num_of_inter):
if vs[i - 1] > vs[i]:
temp = vs[i]
tx = int_pts[2 * i]
ty = int_pts[2 * i + 1]
j = i
while j > 0 and vs[j - 1] > temp:
vs[j] = vs[j - 1]
int_pts[j * 2] = int_pts[j * 2 - 2]
int_pts[j * 2 + 1] = int_pts[j * 2 - 1]
j -= 1

vs[j] = temp
int_pts[j * 2] = tx
int_pts[j * 2 + 1] = ty


def line_segment_intersection(pts1, pts2, i, j, temp_pts):
A = np.zeros((2, ), dtype=np.float32)
B = np.zeros((2, ), dtype=np.float32)
C = np.zeros((2, ), dtype=np.float32)
D = np.zeros((2, ), dtype=np.float32)

A[0] = pts1[2 * i]
A[1] = pts1[2 * i + 1]

B[0] = pts1[2 * ((i + 1) % 4)]
B[1] = pts1[2 * ((i + 1) % 4) + 1]

C[0] = pts2[2 * j]
C[1] = pts2[2 * j + 1]

D[0] = pts2[2 * ((j + 1) % 4)]
D[1] = pts2[2 * ((j + 1) % 4) + 1]
BA0 = B[0] - A[0]
BA1 = B[1] - A[1]
DA0 = D[0] - A[0]
CA0 = C[0] - A[0]
DA1 = D[1] - A[1]
CA1 = C[1] - A[1]
acd = DA1 * CA0 > CA1 * DA0
bcd = (D[1] - B[1]) * (C[0] - B[0]) > (C[1] - B[1]) * (D[0] - B[0])
if acd != bcd:
abc = CA1 * BA0 > BA1 * CA0
abd = DA1 * BA0 > BA1 * DA0
if abc != abd:
DC0 = D[0] - C[0]
DC1 = D[1] - C[1]
ABBA = A[0] * B[1] - B[0] * A[1]
CDDC = C[0] * D[1] - D[0] * C[1]
DH = BA1 * DC0 - BA0 * DC1
Dx = ABBA * DC0 - BA0 * CDDC
Dy = ABBA * DC1 - BA1 * CDDC
temp_pts[0] = Dx / DH
temp_pts[1] = Dy / DH
return True
return False


def line_segment_intersection_v1(pts1, pts2, i, j, temp_pts):
a = np.zeros((2, ), dtype=np.float32)
b = np.zeros((2, ), dtype=np.float32)
c = np.zeros((2, ), dtype=np.float32)
d = np.zeros((2, ), dtype=np.float32)

a[0] = pts1[2 * i]
a[1] = pts1[2 * i + 1]

b[0] = pts1[2 * ((i + 1) % 4)]
b[1] = pts1[2 * ((i + 1) % 4) + 1]

c[0] = pts2[2 * j]
c[1] = pts2[2 * j + 1]

d[0] = pts2[2 * ((j + 1) % 4)]
d[1] = pts2[2 * ((j + 1) % 4) + 1]

area_abc = trangle_area(a, b, c)
area_abd = trangle_area(a, b, d)

if area_abc * area_abd >= 0:
return False

area_cda = trangle_area(c, d, a)
area_cdb = area_cda + area_abc - area_abd

if area_cda * area_cdb >= 0:
return False
t = area_cda / (area_abd - area_abc)

dx = t * (b[0] - a[0])
dy = t * (b[1] - a[1])
temp_pts[0] = a[0] + dx
temp_pts[1] = a[1] + dy
return True


def point_in_quadrilateral(pt_x, pt_y, corners):
ab0 = corners[2] - corners[0]
ab1 = corners[3] - corners[1]

ad0 = corners[6] - corners[0]
ad1 = corners[7] - corners[1]

ap0 = pt_x - corners[0]
ap1 = pt_y - corners[1]

abab = ab0 * ab0 + ab1 * ab1
abap = ab0 * ap0 + ab1 * ap1
adad = ad0 * ad0 + ad1 * ad1
adap = ad0 * ap0 + ad1 * ap1

return abab >= abap and abap >= 0 and adad >= adap and adap >= 0


def quadrilateral_intersection(pts1, pts2, int_pts):
num_of_inter = 0
for i in range(4):
if point_in_quadrilateral(pts1[2 * i], pts1[2 * i + 1], pts2):
int_pts[num_of_inter * 2] = pts1[2 * i]
int_pts[num_of_inter * 2 + 1] = pts1[2 * i + 1]
num_of_inter += 1
if point_in_quadrilateral(pts2[2 * i], pts2[2 * i + 1], pts1):
int_pts[num_of_inter * 2] = pts2[2 * i]
int_pts[num_of_inter * 2 + 1] = pts2[2 * i + 1]
num_of_inter += 1
temp_pts = np.zeros((2, ), dtype=np.float32)
for i in range(4):
for j in range(4):
has_pts = line_segment_intersection(pts1, pts2, i, j, temp_pts)
if has_pts:
int_pts[num_of_inter * 2] = temp_pts[0]
int_pts[num_of_inter * 2 + 1] = temp_pts[1]
num_of_inter += 1

return num_of_inter


def rbbox_to_corners(corners, rbbox):
# generate clockwise corners and rotate it clockwise
angle = rbbox[4]
a_cos = math.cos(angle)
a_sin = math.sin(angle)
center_x = rbbox[0]
center_y = rbbox[1]
x_d = rbbox[2]
y_d = rbbox[3]
corners_x = np.zeros((4, ), dtype=np.float32)
corners_y = np.zeros((4, ), dtype=np.float32)
corners_x[0] = -x_d / 2
corners_x[1] = -x_d / 2
corners_x[2] = x_d / 2
corners_x[3] = x_d / 2
corners_y[0] = -y_d / 2
corners_y[1] = y_d / 2
corners_y[2] = y_d / 2
corners_y[3] = -y_d / 2
for i in range(4):
corners[2 * i] = a_cos * corners_x[i] + a_sin * corners_y[i] + center_x
corners[2 * i +
1] = -a_sin * corners_x[i] + a_cos * corners_y[i] + center_y


def inter(rbbox1, rbbox2):
corners1 = np.zeros((8, ), dtype=np.float32)
corners2 = np.zeros((8, ), dtype=np.float32)
intersection_corners = np.zeros((16, ), dtype=np.float32)

rbbox_to_corners(corners1, rbbox1)
rbbox_to_corners(corners2, rbbox2)

num_intersection = quadrilateral_intersection(corners1, corners2,
intersection_corners)
sort_vertex_in_convex_polygon(intersection_corners, num_intersection)
# print(intersection_corners.reshape([-1, 2])[:num_intersection])

return area(intersection_corners, num_intersection)


def devRotateIoUEval(rbox1, rbox2, criterion=-1):
area1 = rbox1[2] * rbox1[3]
area2 = rbox2[2] * rbox2[3]
area_inter = inter(rbox1, rbox2)
if criterion == -1:
return area_inter / (area1 + area2 - area_inter)
elif criterion == 0:
return area_inter / area1
elif criterion == 1:
return area_inter / area2
else:
return area_inter


def rotate_iou_cpu_eval(dev_boxes, dev_query_boxes, criterion=-1):
num_boxes = dev_boxes.shape[0]
num_qboxes = dev_query_boxes.shape[0]
dev_iou = np.zeros((num_boxes, num_qboxes))
for box_i in range(num_boxes):
for qbox_i in range(num_qboxes):
dev_iou[box_i,
qbox_i] = devRotateIoUEval(dev_query_boxes[qbox_i],
dev_boxes[box_i], criterion)
return dev_iou
6 changes: 0 additions & 6 deletions tests/test_evaluation/test_functional/test_kitti_eval.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
# Copyright (c) OpenMMLab. All rights reserved.
import numpy as np
import pytest
import torch

from mmdet3d.evaluation import do_eval, eval_class, kitti_eval


def test_do_eval():
if not torch.cuda.is_available():
pytest.skip('test requires GPU and CUDA')
gt_name = np.array(
['Pedestrian', 'Cyclist', 'Car', 'Car', 'Car', 'DontCare', 'DontCare'])
gt_truncated = np.array([0., 0., 0., -1., -1., -1., -1.])
Expand Down Expand Up @@ -128,8 +124,6 @@ def test_do_eval():


def test_kitti_eval():
if not torch.cuda.is_available():
pytest.skip('test requires GPU and CUDA')
gt_name = np.array(
['Pedestrian', 'Cyclist', 'Car', 'Car', 'Car', 'DontCare', 'DontCare'])
gt_truncated = np.array([0., 0., 0., -1., -1., -1., -1.])
Expand Down