Skip to content

Commit

Permalink
[Add] Add rotate iou cpu evaluation on kitti dateset
Browse files Browse the repository at this point in the history
  • Loading branch information
shun001 committed Jan 17, 2024
1 parent fe25f7a commit ee58a4a
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 11 deletions.
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

0 comments on commit ee58a4a

Please sign in to comment.