Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
mmdetection3d
Commits
2eebdc2d
"models/vscode:/vscode.git/clone" did not exist on "80b865878c5a8478f2594758183cf5eceb82ec3b"
Commit
2eebdc2d
authored
Jul 22, 2021
by
Yezhen Cong
Committed by
Tai-Wang
Sep 24, 2021
Browse files
[Refactor] Main code modification for coordinate system refactor (#677)
parent
26ab7ff2
Changes
97
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
403 additions
and
449 deletions
+403
-449
configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_waymo-3d-car.py
...illars/hv_pointpillars_secfpn_sbn_2x16_2x_waymo-3d-car.py
+1
-1
configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_waymoD5-3d-car.py
...lars/hv_pointpillars_secfpn_sbn_2x16_2x_waymoD5-3d-car.py
+1
-1
configs/regnet/hv_pointpillars_regnet-400mf_secfpn_sbn-all_2x8_2x_lyft-3d.py
...ointpillars_regnet-400mf_secfpn_sbn-all_2x8_2x_lyft-3d.py
+9
-9
configs/regnet/hv_pointpillars_regnet-400mf_secfpn_sbn-all_4x8_2x_nus-3d.py
...pointpillars_regnet-400mf_secfpn_sbn-all_4x8_2x_nus-3d.py
+7
-7
configs/regnet/hv_pointpillars_regnet-400mf_secfpn_sbn-all_range100_2x8_2x_lyft-3d.py
...rs_regnet-400mf_secfpn_sbn-all_range100_2x8_2x_lyft-3d.py
+9
-9
configs/second/hv_second_secfpn_6x8_80e_kitti-3d-car.py
configs/second/hv_second_secfpn_6x8_80e_kitti-3d-car.py
+1
-1
configs/second/hv_second_secfpn_sbn_2x16_2x_waymoD5-3d-3class.py
.../second/hv_second_secfpn_sbn_2x16_2x_waymoD5-3d-3class.py
+4
-1
configs/ssn/hv_ssn_secfpn_sbn-all_2x16_2x_lyft-3d.py
configs/ssn/hv_ssn_secfpn_sbn-all_2x16_2x_lyft-3d.py
+10
-10
configs/ssn/hv_ssn_secfpn_sbn-all_2x16_2x_nus-3d.py
configs/ssn/hv_ssn_secfpn_sbn-all_2x16_2x_nus-3d.py
+11
-11
mmdet3d/apis/inference.py
mmdet3d/apis/inference.py
+4
-6
mmdet3d/core/anchor/anchor_3d_generator.py
mmdet3d/core/anchor/anchor_3d_generator.py
+3
-3
mmdet3d/core/bbox/box_np_ops.py
mmdet3d/core/bbox/box_np_ops.py
+44
-130
mmdet3d/core/bbox/iou_calculators/iou3d_calculator.py
mmdet3d/core/bbox/iou_calculators/iou3d_calculator.py
+10
-8
mmdet3d/core/bbox/samplers/iou_neg_piecewise_sampler.py
mmdet3d/core/bbox/samplers/iou_neg_piecewise_sampler.py
+4
-4
mmdet3d/core/bbox/structures/base_box3d.py
mmdet3d/core/bbox/structures/base_box3d.py
+58
-12
mmdet3d/core/bbox/structures/box_3d_mode.py
mmdet3d/core/bbox/structures/box_3d_mode.py
+42
-13
mmdet3d/core/bbox/structures/cam_box3d.py
mmdet3d/core/bbox/structures/cam_box3d.py
+79
-25
mmdet3d/core/bbox/structures/coord_3d_mode.py
mmdet3d/core/bbox/structures/coord_3d_mode.py
+54
-105
mmdet3d/core/bbox/structures/depth_box3d.py
mmdet3d/core/bbox/structures/depth_box3d.py
+24
-52
mmdet3d/core/bbox/structures/lidar_box3d.py
mmdet3d/core/bbox/structures/lidar_box3d.py
+28
-41
No files found.
configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_waymo-3d-car.py
View file @
2eebdc2d
...
...
@@ -17,7 +17,7 @@ model = dict(
anchor_generator
=
dict
(
type
=
'AlignedAnchor3DRangeGenerator'
,
ranges
=
[[
-
74.88
,
-
74.88
,
-
0.0345
,
74.88
,
74.88
,
-
0.0345
]],
sizes
=
[[
2.08
,
4.73
,
1.77
]],
sizes
=
[[
4.73
,
2.08
,
1.77
]],
rotations
=
[
0
,
1.57
],
reshape_out
=
True
)),
# model training and testing settings
...
...
configs/pointpillars/hv_pointpillars_secfpn_sbn_2x16_2x_waymoD5-3d-car.py
View file @
2eebdc2d
...
...
@@ -14,7 +14,7 @@ model = dict(
anchor_generator
=
dict
(
type
=
'AlignedAnchor3DRangeGenerator'
,
ranges
=
[[
-
74.88
,
-
74.88
,
-
0.0345
,
74.88
,
74.88
,
-
0.0345
]],
sizes
=
[[
2.08
,
4.73
,
1.77
]],
sizes
=
[[
4.73
,
2.08
,
1.77
]],
rotations
=
[
0
,
1.57
],
reshape_out
=
True
)),
# model training and testing settings
...
...
configs/regnet/hv_pointpillars_regnet-400mf_secfpn_sbn-all_2x8_2x_lyft-3d.py
View file @
2eebdc2d
...
...
@@ -25,15 +25,15 @@ model = dict(
[
-
80
,
-
80
,
-
0.9122268
,
80
,
80
,
-
0.9122268
],
[
-
80
,
-
80
,
-
1.8012227
,
80
,
80
,
-
1.8012227
]],
sizes
=
[
[
1.92
,
4.75
,
1.71
],
# car
[
2.84
,
10.2
4
,
3.44
],
# truck
[
2.
92
,
1
2.
70
,
3.42
],
# bus
[
2.42
,
6.5
2
,
2.34
],
# emergency vehicle
[
2.75
,
8.17
,
3.20
],
# other vehicle
[
0.96
,
2.35
,
1.59
],
# motorcycle
[
0.63
,
1.76
,
1.44
],
# bicycle
[
0.
76
,
0.
80
,
1.76
],
# pedestrian
[
0.3
5
,
0.
7
3
,
0.50
]
# animal
[
4.75
,
1.92
,
1.71
],
# car
[
10.24
,
2.8
4
,
3.44
],
# truck
[
1
2.
70
,
2.
92
,
3.42
],
# bus
[
6.52
,
2.4
2
,
2.34
],
# emergency vehicle
[
8.17
,
2.75
,
3.20
],
# other vehicle
[
2.35
,
0.96
,
1.59
],
# motorcycle
[
1.76
,
0.63
,
1.44
],
# bicycle
[
0.
80
,
0.
76
,
1.76
],
# pedestrian
[
0.
7
3
,
0.3
5
,
0.50
]
# animal
],
rotations
=
[
0
,
1.57
],
reshape_out
=
True
)))
configs/regnet/hv_pointpillars_regnet-400mf_secfpn_sbn-all_4x8_2x_nus-3d.py
View file @
2eebdc2d
...
...
@@ -25,13 +25,13 @@ model = dict(
[
-
49.6
,
-
49.6
,
-
1.763965
,
49.6
,
49.6
,
-
1.763965
],
],
sizes
=
[
[
1.95017717
,
4.60718145
,
1.72270761
],
# car
[
2.4560939
,
6.73778078
,
2.73004906
],
# truck
[
2.87427237
,
12.01320693
,
3.81509561
],
# trailer
[
0
.6
005891
1
,
1
.6
845216
1
,
1.27192197
],
# bicycle
[
0.
66344886
,
0.7256437
,
1.75748069
],
# pedestrian
[
0.
39694519
,
0.40359262
,
1.06232151
],
# traffic_cone
[
2.49008838
,
0.48578221
,
0.98297065
],
# barrier
[
4.60718145
,
1.95017717
,
1.72270761
],
# car
[
6.73778078
,
2.4560939
,
2.73004906
],
# truck
[
12.01320693
,
2.87427237
,
3.81509561
],
# trailer
[
1
.6
845216
1
,
0
.6
005891
1
,
1.27192197
],
# bicycle
[
0.
7256437
,
0.66344886
,
1.75748069
],
# pedestrian
[
0.
40359262
,
0.39694519
,
1.06232151
],
# traffic_cone
[
0.48578221
,
2.49008838
,
0.98297065
],
# barrier
],
custom_values
=
[
0
,
0
],
rotations
=
[
0
,
1.57
],
...
...
configs/regnet/hv_pointpillars_regnet-400mf_secfpn_sbn-all_range100_2x8_2x_lyft-3d.py
View file @
2eebdc2d
...
...
@@ -26,15 +26,15 @@ model = dict(
[
-
100
,
-
100
,
-
0.9122268
,
100
,
100
,
-
0.9122268
],
[
-
100
,
-
100
,
-
1.8012227
,
100
,
100
,
-
1.8012227
]],
sizes
=
[
[
1.92
,
4.75
,
1.71
],
# car
[
2.84
,
10.2
4
,
3.44
],
# truck
[
2.
92
,
1
2.
70
,
3.42
],
# bus
[
2.42
,
6.5
2
,
2.34
],
# emergency vehicle
[
2.75
,
8.17
,
3.20
],
# other vehicle
[
0.96
,
2.35
,
1.59
],
# motorcycle
[
0.63
,
1.76
,
1.44
],
# bicycle
[
0.
76
,
0.
80
,
1.76
],
# pedestrian
[
0.3
5
,
0.
7
3
,
0.50
]
# animal
[
4.75
,
1.92
,
1.71
],
# car
[
10.24
,
2.8
4
,
3.44
],
# truck
[
1
2.
70
,
2.
92
,
3.42
],
# bus
[
6.52
,
2.4
2
,
2.34
],
# emergency vehicle
[
8.17
,
2.75
,
3.20
],
# other vehicle
[
2.35
,
0.96
,
1.59
],
# motorcycle
[
1.76
,
0.63
,
1.44
],
# bicycle
[
0.
80
,
0.
76
,
1.76
],
# pedestrian
[
0.
7
3
,
0.3
5
,
0.50
]
# animal
],
rotations
=
[
0
,
1.57
],
reshape_out
=
True
)))
configs/second/hv_second_secfpn_6x8_80e_kitti-3d-car.py
View file @
2eebdc2d
...
...
@@ -12,7 +12,7 @@ model = dict(
_delete_
=
True
,
type
=
'Anchor3DRangeGenerator'
,
ranges
=
[[
0
,
-
40.0
,
-
1.78
,
70.4
,
40.0
,
-
1.78
]],
sizes
=
[[
1.6
,
3.9
,
1.56
]],
sizes
=
[[
3.9
,
1.6
,
1.56
]],
rotations
=
[
0
,
1.57
],
reshape_out
=
True
)),
# model training and testing settings
...
...
configs/second/hv_second_secfpn_sbn_2x16_2x_waymoD5-3d-3class.py
View file @
2eebdc2d
...
...
@@ -21,7 +21,10 @@ db_sampler = dict(
classes
=
class_names
,
sample_groups
=
dict
(
Car
=
15
,
Pedestrian
=
10
,
Cyclist
=
10
),
points_loader
=
dict
(
type
=
'LoadPointsFromFile'
,
load_dim
=
5
,
use_dim
=
[
0
,
1
,
2
,
3
,
4
]))
type
=
'LoadPointsFromFile'
,
coord_type
=
'LIDAR'
,
load_dim
=
5
,
use_dim
=
[
0
,
1
,
2
,
3
,
4
]))
train_pipeline
=
[
dict
(
type
=
'LoadPointsFromFile'
,
coord_type
=
'LIDAR'
,
load_dim
=
6
,
use_dim
=
5
),
...
...
configs/ssn/hv_ssn_secfpn_sbn-all_2x16_2x_lyft-3d.py
View file @
2eebdc2d
...
...
@@ -96,15 +96,15 @@ model = dict(
[
-
100
,
-
100
,
-
0.6276341
,
100
,
100
,
-
0.6276341
],
[
-
100
,
-
100
,
-
0.3033737
,
100
,
100
,
-
0.3033737
]],
sizes
=
[
[
0.63
,
1.76
,
1.44
],
# bicycle
[
0.96
,
2.35
,
1.59
],
# motorcycle
[
0.
76
,
0.
80
,
1.76
],
# pedestrian
[
0.3
5
,
0.
7
3
,
0.50
],
# animal
[
1.92
,
4.75
,
1.71
],
# car
[
2.42
,
6.5
2
,
2.34
],
# emergency vehicle
[
2.
92
,
1
2.
70
,
3.42
],
# bus
[
2.75
,
8.17
,
3.20
],
# other vehicle
[
2.84
,
10.2
4
,
3.44
]
# truck
[
1.76
,
0.63
,
1.44
],
# bicycle
[
2.35
,
0.96
,
1.59
],
# motorcycle
[
0.
80
,
0.
76
,
1.76
],
# pedestrian
[
0.
7
3
,
0.3
5
,
0.50
],
# animal
[
4.75
,
1.92
,
1.71
],
# car
[
6.52
,
2.4
2
,
2.34
],
# emergency vehicle
[
1
2.
70
,
2.
92
,
3.42
],
# bus
[
8.17
,
2.75
,
3.20
],
# other vehicle
[
10.24
,
2.8
4
,
3.44
]
# truck
],
custom_values
=
[],
rotations
=
[
0
,
1.57
],
...
...
@@ -137,7 +137,7 @@ model = dict(
],
assign_per_class
=
True
,
diff_rad_by_sin
=
True
,
dir_offset
=
0.7854
,
# pi/4
dir_offset
=
-
0.7854
,
#
-
pi/4
dir_limit_offset
=
0
,
bbox_coder
=
dict
(
type
=
'DeltaXYZWLHRBBoxCoder'
,
code_size
=
7
),
loss_cls
=
dict
(
...
...
configs/ssn/hv_ssn_secfpn_sbn-all_2x16_2x_nus-3d.py
View file @
2eebdc2d
...
...
@@ -94,16 +94,16 @@ model = dict(
[
-
50
,
-
50
,
-
1.80673031
,
50
,
50
,
-
1.80673031
],
[
-
50
,
-
50
,
-
1.64824291
,
50
,
50
,
-
1.64824291
]],
sizes
=
[
[
0
.6
005891
1
,
1
.6
845216
1
,
1.27192197
],
# bicycle
[
0.76279481
,
2.09973778
,
1.44403034
],
# motorcycle
[
0.
66344886
,
0.72564370
,
1.75748069
],
# pedestrian
[
0.
39694519
,
0.40359262
,
1.06232151
],
# traffic cone
[
2.49008838
,
0.48578221
,
0.98297065
],
# barrier
[
1.95017717
,
4.60718145
,
1.72270761
],
# car
[
2.45609390
,
6.73778078
,
2.73004906
],
# truck
[
2.87427237
,
12.01320693
,
3.81509561
],
# trailer
[
2.94046906
,
11.1885991
,
3.47030982
],
# bus
[
2.73050468
,
6.38352896
,
3.13312415
]
# construction vehicle
[
1
.6
845216
1
,
0
.6
005891
1
,
1.27192197
],
# bicycle
[
2.09973778
,
0.76279481
,
1.44403034
],
# motorcycle
[
0.
72564370
,
0.66344886
,
1.75748069
],
# pedestrian
[
0.
40359262
,
0.39694519
,
1.06232151
],
# traffic cone
[
0.48578221
,
2.49008838
,
0.98297065
],
# barrier
[
4.60718145
,
1.95017717
,
1.72270761
],
# car
[
6.73778078
,
2.45609390
,
2.73004906
],
# truck
[
12.01320693
,
2.87427237
,
3.81509561
],
# trailer
[
11.1885991
,
2.94046906
,
3.47030982
],
# bus
[
6.38352896
,
2.73050468
,
3.13312415
]
# construction vehicle
],
custom_values
=
[
0
,
0
],
rotations
=
[
0
,
1.57
],
...
...
@@ -144,7 +144,7 @@ model = dict(
],
assign_per_class
=
True
,
diff_rad_by_sin
=
True
,
dir_offset
=
0.7854
,
# pi/4
dir_offset
=
-
0.7854
,
#
-
pi/4
dir_limit_offset
=
0
,
bbox_coder
=
dict
(
type
=
'DeltaXYZWLHRBBoxCoder'
,
code_size
=
9
),
loss_cls
=
dict
(
...
...
mmdet3d/apis/inference.py
View file @
2eebdc2d
...
...
@@ -8,10 +8,9 @@ from mmcv.parallel import collate, scatter
from
mmcv.runner
import
load_checkpoint
from
os
import
path
as
osp
from
mmdet3d.core
import
(
Box3DMode
,
CameraInstance3DBoxes
,
DepthInstance3DBoxes
,
LiDARInstance3DBoxes
,
show_multi_modality_result
,
show_result
,
show_seg_result
)
from
mmdet3d.core
import
(
Box3DMode
,
Coord3DMode
,
DepthInstance3DBoxes
,
LiDARInstance3DBoxes
,
show_multi_modality_result
,
show_result
,
show_seg_result
)
from
mmdet3d.core.bbox
import
get_box_type
from
mmdet3d.datasets.pipelines
import
Compose
from
mmdet3d.models
import
build_model
...
...
@@ -316,8 +315,7 @@ def show_det_result_meshlab(data,
# for now we convert points into depth mode
box_mode
=
data
[
'img_metas'
][
0
][
0
][
'box_mode_3d'
]
if
box_mode
!=
Box3DMode
.
DEPTH
:
points
=
points
[...,
[
1
,
0
,
2
]]
points
[...,
0
]
*=
-
1
points
=
Coord3DMode
.
convert
(
points
,
box_mode
,
Coord3DMode
.
DEPTH
)
show_bboxes
=
Box3DMode
.
convert
(
pred_bboxes
,
box_mode
,
Box3DMode
.
DEPTH
)
else
:
show_bboxes
=
deepcopy
(
pred_bboxes
)
...
...
mmdet3d/core/anchor/anchor_3d_generator.py
View file @
2eebdc2d
...
...
@@ -32,7 +32,7 @@ class Anchor3DRangeGenerator(object):
def
__init__
(
self
,
ranges
,
sizes
=
[[
1.6
,
3.9
,
1.56
]],
sizes
=
[[
3.9
,
1.6
,
1.56
]],
scales
=
[
1
],
rotations
=
[
0
,
1.5707963
],
custom_values
=
(),
...
...
@@ -149,7 +149,7 @@ class Anchor3DRangeGenerator(object):
feature_size
,
anchor_range
,
scale
=
1
,
sizes
=
[[
1.6
,
3.9
,
1.56
]],
sizes
=
[[
3.9
,
1.6
,
1.56
]],
rotations
=
[
0
,
1.5707963
],
device
=
'cuda'
):
"""Generate anchors in a single range.
...
...
@@ -245,7 +245,7 @@ class AlignedAnchor3DRangeGenerator(Anchor3DRangeGenerator):
feature_size
,
anchor_range
,
scale
,
sizes
=
[[
1.6
,
3.9
,
1.56
]],
sizes
=
[[
3.9
,
1.6
,
1.56
]],
rotations
=
[
0
,
1.5707963
],
device
=
'cuda'
):
"""Generate anchors in a single range.
...
...
mmdet3d/core/bbox/box_np_ops.py
View file @
2eebdc2d
# Copyright (c) OpenMMLab. All rights reserved.
# TODO: clean the functions in this file and move the APIs into box structures
# in the future
import
numba
import
numpy
as
np
from
.structures.utils
import
limit_period
,
points_cam2img
,
rotation_3d_in_axis
def
camera_to_lidar
(
points
,
r_rect
,
velo2cam
):
"""Convert points in camera coordinate to lidar coordinate.
Note:
This function is for KITTI only.
Args:
points (np.ndarray, shape=[N, 3]): Points in camera coordinate.
r_rect (np.ndarray, shape=[4, 4]): Matrix to project points in
...
...
@@ -27,7 +31,10 @@ def camera_to_lidar(points, r_rect, velo2cam):
def
box_camera_to_lidar
(
data
,
r_rect
,
velo2cam
):
"""Covert boxes in camera coordinate to lidar coordinate.
"""Convert boxes in camera coordinate to lidar coordinate.
Note:
This function is for KITTI only.
Args:
data (np.ndarray, shape=[N, 7]): Boxes in camera coordinate.
...
...
@@ -40,10 +47,13 @@ def box_camera_to_lidar(data, r_rect, velo2cam):
np.ndarray, shape=[N, 3]: Boxes in lidar coordinate.
"""
xyz
=
data
[:,
0
:
3
]
l
,
h
,
w
=
data
[:,
3
:
4
],
data
[:,
4
:
5
],
data
[:,
5
:
6
]
dx
,
dy
,
dz
=
data
[:,
3
:
4
],
data
[:,
4
:
5
],
data
[:,
5
:
6
]
r
=
data
[:,
6
:
7
]
xyz_lidar
=
camera_to_lidar
(
xyz
,
r_rect
,
velo2cam
)
return
np
.
concatenate
([
xyz_lidar
,
w
,
l
,
h
,
r
],
axis
=
1
)
# yaw and dims also needs to be converted
r_new
=
-
r
-
np
.
pi
/
2
r_new
=
limit_period
(
r_new
,
period
=
np
.
pi
*
2
)
return
np
.
concatenate
([
xyz_lidar
,
dx
,
dz
,
dy
,
r_new
],
axis
=
1
)
def
corners_nd
(
dims
,
origin
=
0.5
):
...
...
@@ -80,23 +90,6 @@ def corners_nd(dims, origin=0.5):
return
corners
def
rotation_2d
(
points
,
angles
):
"""Rotation 2d points based on origin point clockwise when angle positive.
Args:
points (np.ndarray): Points to be rotated with shape
\
(N, point_size, 2).
angles (np.ndarray): Rotation angle with shape (N).
Returns:
np.ndarray: Same shape as points.
"""
rot_sin
=
np
.
sin
(
angles
)
rot_cos
=
np
.
cos
(
angles
)
rot_mat_T
=
np
.
stack
([[
rot_cos
,
-
rot_sin
],
[
rot_sin
,
rot_cos
]])
return
np
.
einsum
(
'aij,jka->aik'
,
points
,
rot_mat_T
)
def
center_to_corner_box2d
(
centers
,
dims
,
angles
=
None
,
origin
=
0.5
):
"""Convert kitti locations, dimensions and angles to corners.
format: center(xy), dims(xy), angles(clockwise when positive)
...
...
@@ -118,7 +111,7 @@ def center_to_corner_box2d(centers, dims, angles=None, origin=0.5):
corners
=
corners_nd
(
dims
,
origin
=
origin
)
# corners: [N, 4, 2]
if
angles
is
not
None
:
corners
=
rotation_
2d
(
corners
,
angles
)
corners
=
rotation_
3d_in_axis
(
corners
,
angles
)
corners
+=
centers
.
reshape
([
-
1
,
1
,
2
])
return
corners
...
...
@@ -172,37 +165,6 @@ def depth_to_lidar_points(depth, trunc_pixel, P2, r_rect, velo2cam):
return
lidar_points
def
rotation_3d_in_axis
(
points
,
angles
,
axis
=
0
):
"""Rotate points in specific axis.
Args:
points (np.ndarray, shape=[N, point_size, 3]]):
angles (np.ndarray, shape=[N]]):
axis (int, optional): Axis to rotate at. Defaults to 0.
Returns:
np.ndarray: Rotated points.
"""
# points: [N, point_size, 3]
rot_sin
=
np
.
sin
(
angles
)
rot_cos
=
np
.
cos
(
angles
)
ones
=
np
.
ones_like
(
rot_cos
)
zeros
=
np
.
zeros_like
(
rot_cos
)
if
axis
==
1
:
rot_mat_T
=
np
.
stack
([[
rot_cos
,
zeros
,
-
rot_sin
],
[
zeros
,
ones
,
zeros
],
[
rot_sin
,
zeros
,
rot_cos
]])
elif
axis
==
2
or
axis
==
-
1
:
rot_mat_T
=
np
.
stack
([[
rot_cos
,
-
rot_sin
,
zeros
],
[
rot_sin
,
rot_cos
,
zeros
],
[
zeros
,
zeros
,
ones
]])
elif
axis
==
0
:
rot_mat_T
=
np
.
stack
([[
zeros
,
rot_cos
,
-
rot_sin
],
[
zeros
,
rot_sin
,
rot_cos
],
[
ones
,
zeros
,
zeros
]])
else
:
raise
ValueError
(
'axis should in range'
)
return
np
.
einsum
(
'aij,jka->aik'
,
points
,
rot_mat_T
)
def
center_to_corner_box3d
(
centers
,
dims
,
angles
=
None
,
...
...
@@ -259,8 +221,8 @@ def box2d_to_corner_jit(boxes):
rot_sin
=
np
.
sin
(
boxes
[
i
,
-
1
])
rot_cos
=
np
.
cos
(
boxes
[
i
,
-
1
])
rot_mat_T
[
0
,
0
]
=
rot_cos
rot_mat_T
[
0
,
1
]
=
-
rot_sin
rot_mat_T
[
1
,
0
]
=
rot_sin
rot_mat_T
[
0
,
1
]
=
rot_sin
rot_mat_T
[
1
,
0
]
=
-
rot_sin
rot_mat_T
[
1
,
1
]
=
rot_cos
box_corners
[
i
]
=
corners
[
i
]
@
rot_mat_T
+
boxes
[
i
,
:
2
]
return
box_corners
...
...
@@ -327,15 +289,15 @@ def rotation_points_single_angle(points, angle, axis=0):
rot_cos
=
np
.
cos
(
angle
)
if
axis
==
1
:
rot_mat_T
=
np
.
array
(
[[
rot_cos
,
0
,
-
rot_sin
],
[
0
,
1
,
0
],
[
rot_sin
,
0
,
rot_cos
]],
[[
rot_cos
,
0
,
rot_sin
],
[
0
,
1
,
0
],
[
-
rot_sin
,
0
,
rot_cos
]],
dtype
=
points
.
dtype
)
elif
axis
==
2
or
axis
==
-
1
:
rot_mat_T
=
np
.
array
(
[[
rot_cos
,
-
rot_sin
,
0
],
[
rot_sin
,
rot_cos
,
0
],
[
0
,
0
,
1
]],
[[
rot_cos
,
rot_sin
,
0
],
[
-
rot_sin
,
rot_cos
,
0
],
[
0
,
0
,
1
]],
dtype
=
points
.
dtype
)
elif
axis
==
0
:
rot_mat_T
=
np
.
array
(
[[
1
,
0
,
0
],
[
0
,
rot_cos
,
-
rot_sin
],
[
0
,
rot_sin
,
rot_cos
]],
[[
1
,
0
,
0
],
[
0
,
rot_cos
,
rot_sin
],
[
0
,
-
rot_sin
,
rot_cos
]],
dtype
=
points
.
dtype
)
else
:
raise
ValueError
(
'axis should in range'
)
...
...
@@ -343,44 +305,6 @@ def rotation_points_single_angle(points, angle, axis=0):
return
points
@
rot_mat_T
,
rot_mat_T
def
points_cam2img
(
points_3d
,
proj_mat
,
with_depth
=
False
):
"""Project points in camera coordinates to image coordinates.
Args:
points_3d (np.ndarray): Points in shape (N, 3)
proj_mat (np.ndarray): Transformation matrix between coordinates.
with_depth (bool, optional): Whether to keep depth in the output.
Defaults to False.
Returns:
np.ndarray: Points in image coordinates with shape [N, 2].
"""
points_shape
=
list
(
points_3d
.
shape
)
points_shape
[
-
1
]
=
1
assert
len
(
proj_mat
.
shape
)
==
2
,
'The dimension of the projection'
\
f
' matrix should be 2 instead of
{
len
(
proj_mat
.
shape
)
}
.'
d1
,
d2
=
proj_mat
.
shape
[:
2
]
assert
(
d1
==
3
and
d2
==
3
)
or
(
d1
==
3
and
d2
==
4
)
or
(
d1
==
4
and
d2
==
4
),
'The shape of the projection matrix'
\
f
' (
{
d1
}
*
{
d2
}
) is not supported.'
if
d1
==
3
:
proj_mat_expanded
=
np
.
eye
(
4
,
dtype
=
proj_mat
.
dtype
)
proj_mat_expanded
[:
d1
,
:
d2
]
=
proj_mat
proj_mat
=
proj_mat_expanded
points_4
=
np
.
concatenate
([
points_3d
,
np
.
ones
(
points_shape
)],
axis
=-
1
)
point_2d
=
points_4
@
proj_mat
.
T
point_2d_res
=
point_2d
[...,
:
2
]
/
point_2d
[...,
2
:
3
]
if
with_depth
:
points_2d_depth
=
np
.
concatenate
([
point_2d_res
,
point_2d
[...,
2
:
3
]],
axis
=-
1
)
return
points_2d_depth
return
point_2d_res
def
box3d_to_bbox
(
box3d
,
P2
):
"""Convert box3d in camera coordinates to bbox in image coordinates.
...
...
@@ -461,25 +385,9 @@ def minmax_to_corner_2d(minmax_box):
return
center_to_corner_box2d
(
center
,
dims
,
origin
=
0.0
)
def
limit_period
(
val
,
offset
=
0.5
,
period
=
np
.
pi
):
"""Limit the value into a period for periodic function.
Args:
val (np.ndarray): The value to be converted.
offset (float, optional): Offset to set the value range.
\
Defaults to 0.5.
period (float, optional): Period of the value. Defaults to np.pi.
Returns:
torch.Tensor: Value in the range of
\
[-offset * period, (1-offset) * period]
"""
return
val
-
np
.
floor
(
val
/
period
+
offset
)
*
period
def
create_anchors_3d_range
(
feature_size
,
anchor_range
,
sizes
=
((
1.6
,
3.9
,
1.56
),
),
sizes
=
((
3.9
,
1.6
,
1.56
),
),
rotations
=
(
0
,
np
.
pi
/
2
),
dtype
=
np
.
float32
):
"""Create anchors 3d by range.
...
...
@@ -492,14 +400,14 @@ def create_anchors_3d_range(feature_size,
(x_min, y_min, z_min, x_max, y_max, z_max).
sizes (list[list] | np.ndarray | torch.Tensor, optional):
Anchor size with shape [N, 3], in order of x, y, z.
Defaults to ((
1.6, 3.9
, 1.56), ).
Defaults to ((
3.9, 1.6
, 1.56), ).
rotations (list[float] | np.ndarray | torch.Tensor, optional):
Rotations of anchors in a single feature grid.
Defaults to (0, np.pi / 2).
dtype (type, optional): Data type. Default to np.float32.
Returns:
np.ndarray: Range based anchors with shape of
\
np.ndarray: Range based anchors with shape of
(*feature_size, num_sizes, num_rots, 7).
"""
anchor_range
=
np
.
array
(
anchor_range
,
dtype
)
...
...
@@ -550,7 +458,7 @@ def rbbox2d_to_near_bbox(rbboxes):
"""convert rotated bbox to nearest 'standing' or 'lying' bbox.
Args:
rbboxes (np.ndarray): Rotated bboxes with shape of
\
rbboxes (np.ndarray): Rotated bboxes with shape of
(N, 5(x, y, xdim, ydim, rad)).
Returns:
...
...
@@ -841,8 +749,8 @@ def boxes3d_to_corners3d_lidar(boxes3d, bottom_center=True):
Args:
boxes3d (np.ndarray): Boxes with shape of (N, 7)
[x, y, z,
w, l, h
, ry] in LiDAR coords, see the definition of
ry
in KITTI dataset.
[x, y, z,
dx, dy, dz
, ry] in LiDAR coords, see the definition of
ry
in KITTI dataset.
bottom_center (bool, optional): Whether z is on the bottom center
of object. Defaults to True.
...
...
@@ -850,19 +758,25 @@ def boxes3d_to_corners3d_lidar(boxes3d, bottom_center=True):
np.ndarray: Box corners with the shape of [N, 8, 3].
"""
boxes_num
=
boxes3d
.
shape
[
0
]
w
,
l
,
h
=
boxes3d
[:,
3
],
boxes3d
[:,
4
],
boxes3d
[:,
5
]
x_corners
=
np
.
array
(
[
w
/
2.
,
-
w
/
2.
,
-
w
/
2.
,
w
/
2.
,
w
/
2.
,
-
w
/
2.
,
-
w
/
2.
,
w
/
2.
],
dtype
=
np
.
float32
).
T
y_corners
=
np
.
array
(
[
-
l
/
2.
,
-
l
/
2.
,
l
/
2.
,
l
/
2.
,
-
l
/
2.
,
-
l
/
2.
,
l
/
2.
,
l
/
2.
],
dtype
=
np
.
float32
).
T
dx
,
dy
,
dz
=
boxes3d
[:,
3
],
boxes3d
[:,
4
],
boxes3d
[:,
5
]
x_corners
=
np
.
array
([
dx
/
2.
,
-
dx
/
2.
,
-
dx
/
2.
,
dx
/
2.
,
dx
/
2.
,
-
dx
/
2.
,
-
dx
/
2.
,
dx
/
2.
],
dtype
=
np
.
float32
).
T
y_corners
=
np
.
array
([
-
dy
/
2.
,
-
dy
/
2.
,
dy
/
2.
,
dy
/
2.
,
-
dy
/
2.
,
-
dy
/
2.
,
dy
/
2.
,
dy
/
2.
],
dtype
=
np
.
float32
).
T
if
bottom_center
:
z_corners
=
np
.
zeros
((
boxes_num
,
8
),
dtype
=
np
.
float32
)
z_corners
[:,
4
:
8
]
=
h
.
reshape
(
boxes_num
,
1
).
repeat
(
4
,
axis
=
1
)
# (N, 8)
z_corners
[:,
4
:
8
]
=
dz
.
reshape
(
boxes_num
,
1
).
repeat
(
4
,
axis
=
1
)
# (N, 8)
else
:
z_corners
=
np
.
array
([
-
h
/
2.
,
-
h
/
2.
,
-
h
/
2.
,
-
h
/
2.
,
h
/
2.
,
h
/
2.
,
h
/
2.
,
h
/
2.
-
dz
/
2.
,
-
dz
/
2.
,
-
dz
/
2.
,
-
dz
/
2.
,
dz
/
2.
,
dz
/
2.
,
dz
/
2.
,
dz
/
2.
],
dtype
=
np
.
float32
).
T
...
...
@@ -870,9 +784,9 @@ def boxes3d_to_corners3d_lidar(boxes3d, bottom_center=True):
zeros
,
ones
=
np
.
zeros
(
ry
.
size
,
dtype
=
np
.
float32
),
np
.
ones
(
ry
.
size
,
dtype
=
np
.
float32
)
rot_list
=
np
.
array
([[
np
.
cos
(
ry
),
-
np
.
sin
(
ry
),
zeros
],
[
np
.
sin
(
ry
),
np
.
cos
(
ry
),
zeros
],
[
zeros
,
zeros
,
ones
]])
# (3, 3, N)
rot_list
=
np
.
array
([[
np
.
cos
(
ry
),
np
.
sin
(
ry
),
zeros
],
[
-
np
.
sin
(
ry
),
np
.
cos
(
ry
),
zeros
],
[
zeros
,
zeros
,
ones
]])
# (3, 3, N)
R_list
=
np
.
transpose
(
rot_list
,
(
2
,
0
,
1
))
# (N, 3, 3)
temp_corners
=
np
.
concatenate
((
x_corners
.
reshape
(
...
...
mmdet3d/core/bbox/iou_calculators/iou3d_calculator.py
View file @
2eebdc2d
...
...
@@ -31,8 +31,10 @@ class BboxOverlapsNearest3D(object):
between each aligned pair of bboxes1 and bboxes2.
Args:
bboxes1 (torch.Tensor): shape (N, 7+N) [x, y, z, h, w, l, ry, v].
bboxes2 (torch.Tensor): shape (M, 7+N) [x, y, z, h, w, l, ry, v].
bboxes1 (torch.Tensor): shape (N, 7+N)
[x, y, z, dx, dy, dz, ry, v].
bboxes2 (torch.Tensor): shape (M, 7+N)
[x, y, z, dx, dy, dz, ry, v].
mode (str): "iou" (intersection over union) or iof
(intersection over foreground).
is_aligned (bool): Whether the calculation is aligned.
...
...
@@ -74,8 +76,8 @@ class BboxOverlaps3D(object):
calculate the actual 3D IoUs of boxes.
Args:
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z,
h, w, l
, ry].
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z,
h, w, l
, ry].
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z,
dx, dy, dz
, ry].
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z,
dx, dy, dz
, ry].
mode (str): "iou" (intersection over union) or
iof (intersection over foreground).
...
...
@@ -110,8 +112,8 @@ def bbox_overlaps_nearest_3d(bboxes1,
aligned pair of bboxes1 and bboxes2.
Args:
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z,
h, w, l
, ry, v].
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z,
h, w, l
, ry, v].
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z,
dx, dy, dz
, ry, v].
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z,
dx, dy, dz
, ry, v].
mode (str): "iou" (intersection over union) or iof
(intersection over foreground).
is_aligned (bool): Whether the calculation is aligned
...
...
@@ -148,8 +150,8 @@ def bbox_overlaps_3d(bboxes1, bboxes2, mode='iou', coordinate='camera'):
calculate the actual IoUs of boxes.
Args:
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z,
h, w, l
, ry].
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z,
h, w, l
, ry].
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z,
dx, dy, dz
, ry].
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z,
dx, dy, dz
, ry].
mode (str): "iou" (intersection over union) or
iof (intersection over foreground).
coordinate (str): 'camera' or 'lidar' coordinate system.
...
...
mmdet3d/core/bbox/samplers/iou_neg_piecewise_sampler.py
View file @
2eebdc2d
...
...
@@ -9,8 +9,8 @@ from . import RandomSampler, SamplingResult
class
IoUNegPiecewiseSampler
(
RandomSampler
):
"""IoU Piece-wise Sampling.
Sampling negtive proposals according to a list of IoU thresholds.
The negtive proposals are divided into several pieces according
Sampling neg
a
tive proposals according to a list of IoU thresholds.
The neg
a
tive proposals are divided into several pieces according
to `neg_iou_piece_thrs`. And the ratio of each piece is indicated
by `neg_piece_fractions`.
...
...
@@ -18,11 +18,11 @@ class IoUNegPiecewiseSampler(RandomSampler):
num (int): Number of proposals.
pos_fraction (float): The fraction of positive proposals.
neg_piece_fractions (list): A list contains fractions that indicates
the ratio of each piece of total negtive samplers.
the ratio of each piece of total neg
a
tive samplers.
neg_iou_piece_thrs (list): A list contains IoU thresholds that
indicate the upper bound of this piece.
neg_pos_ub (float): The total ratio to limit the upper bound
number of negtive samples.
number of neg
a
tive samples.
add_gt_as_proposals (bool): Whether to add gt as proposals.
"""
...
...
mmdet3d/core/bbox/structures/base_box3d.py
View file @
2eebdc2d
...
...
@@ -3,6 +3,7 @@ import numpy as np
import
torch
from
abc
import
abstractmethod
from
mmdet3d.ops
import
points_in_boxes_batch
,
points_in_boxes_gpu
from
mmdet3d.ops.iou3d
import
iou3d_cuda
from
.utils
import
limit_period
,
xywhr2xyxyr
...
...
@@ -131,8 +132,8 @@ class BaseInstance3DBoxes(object):
@
abstractmethod
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
rotation
matrix.
"""Rotate boxes with points (optional) with the given angle or
rotation
matrix.
Args:
angle (float | torch.Tensor | np.ndarray):
...
...
@@ -170,7 +171,7 @@ class BaseInstance3DBoxes(object):
polygon, we try to reduce the burden for simpler cases.
Returns:
torch.Tensor: A binary vector indicating whether each box is
\
torch.Tensor: A binary vector indicating whether each box is
inside the reference range.
"""
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
...
...
@@ -190,7 +191,7 @@ class BaseInstance3DBoxes(object):
in order of (x_min, y_min, x_max, y_max).
Returns:
torch.Tensor: Indicating whether each box is inside
\
torch.Tensor: Indicating whether each box is inside
the reference range.
"""
pass
...
...
@@ -208,7 +209,7 @@ class BaseInstance3DBoxes(object):
to LiDAR. This requires a transformation matrix.
Returns:
:obj:`BaseInstance3DBoxes`: The converted box of the same type
\
:obj:`BaseInstance3DBoxes`: The converted box of the same type
in the `dst` mode.
"""
pass
...
...
@@ -241,7 +242,7 @@ class BaseInstance3DBoxes(object):
threshold (float): The threshold of minimal sizes.
Returns:
torch.Tensor: A binary vector which represents whether each
\
torch.Tensor: A binary vector which represents whether each
box is empty (False) or non-empty (True).
"""
box
=
self
.
tensor
...
...
@@ -267,8 +268,8 @@ class BaseInstance3DBoxes(object):
subject to Pytorch's indexing semantics.
Returns:
:obj:`BaseInstance3DBoxes`: A new object of
\
:class:`BaseInstance
s
3DBoxes` after indexing.
:obj:`BaseInstance3DBoxes`: A new object of
:class:`BaseInstance3DBoxes` after indexing.
"""
original_type
=
type
(
self
)
if
isinstance
(
item
,
int
):
...
...
@@ -319,7 +320,7 @@ class BaseInstance3DBoxes(object):
device (str | :obj:`torch.device`): The name of the device.
Returns:
:obj:`BaseInstance3DBoxes`: A new boxes object on the
\
:obj:`BaseInstance3DBoxes`: A new boxes object on the
specific device.
"""
original_type
=
type
(
self
)
...
...
@@ -332,7 +333,7 @@ class BaseInstance3DBoxes(object):
"""Clone the Boxes.
Returns:
:obj:`BaseInstance3DBoxes`: Box object with the same properties
\
:obj:`BaseInstance3DBoxes`: Box object with the same properties
as self.
"""
original_type
=
type
(
self
)
...
...
@@ -444,14 +445,14 @@ class BaseInstance3DBoxes(object):
def
new_box
(
self
,
data
):
"""Create a new box object with data.
The new box and its tensor has the similar properties
\
The new box and its tensor has the similar properties
as self and self.tensor, respectively.
Args:
data (torch.Tensor | numpy.array | list): Data to be copied.
Returns:
:obj:`BaseInstance3DBoxes`: A new bbox object with ``data``,
\
:obj:`BaseInstance3DBoxes`: A new bbox object with ``data``,
the object's other properties are similar to ``self``.
"""
new_tensor
=
self
.
tensor
.
new_tensor
(
data
)
\
...
...
@@ -459,3 +460,48 @@ class BaseInstance3DBoxes(object):
original_type
=
type
(
self
)
return
original_type
(
new_tensor
,
box_dim
=
self
.
box_dim
,
with_yaw
=
self
.
with_yaw
)
def
points_in_boxes
(
self
,
points
,
boxes_override
=
None
):
"""Find the box which the points are in.
Args:
points (torch.Tensor): Points in shape (N, 3).
Returns:
torch.Tensor: The index of box where each point are in.
"""
if
boxes_override
is
not
None
:
boxes
=
boxes_override
else
:
boxes
=
self
.
tensor
box_idx
=
points_in_boxes_gpu
(
points
.
unsqueeze
(
0
),
boxes
.
unsqueeze
(
0
).
to
(
points
.
device
)).
squeeze
(
0
)
return
box_idx
def
points_in_boxes_batch
(
self
,
points
,
boxes_override
=
None
):
"""Find points that are in boxes (CUDA).
Args:
points (torch.Tensor): Points in shape [1, M, 3] or [M, 3],
3 dimensions are [x, y, z] in LiDAR coordinate.
Returns:
torch.Tensor: The index of boxes each point lies in with shape
of (B, M, T).
"""
if
boxes_override
is
not
None
:
boxes
=
boxes_override
else
:
boxes
=
self
.
tensor
points_clone
=
points
.
clone
()[...,
:
3
]
if
points_clone
.
dim
()
==
2
:
points_clone
=
points_clone
.
unsqueeze
(
0
)
else
:
assert
points_clone
.
dim
()
==
3
and
points_clone
.
shape
[
0
]
==
1
boxes
=
boxes
.
to
(
points_clone
.
device
).
unsqueeze
(
0
)
box_idxs_of_pts
=
points_in_boxes_batch
(
points_clone
,
boxes
)
return
box_idxs_of_pts
.
squeeze
(
0
)
mmdet3d/core/bbox/structures/box_3d_mode.py
View file @
2eebdc2d
...
...
@@ -7,6 +7,7 @@ from .base_box3d import BaseInstance3DBoxes
from
.cam_box3d
import
CameraInstance3DBoxes
from
.depth_box3d
import
DepthInstance3DBoxes
from
.lidar_box3d
import
LiDARInstance3DBoxes
from
.utils
import
limit_period
@
unique
...
...
@@ -61,12 +62,12 @@ class Box3DMode(IntEnum):
DEPTH
=
2
@
staticmethod
def
convert
(
box
,
src
,
dst
,
rt_mat
=
None
):
def
convert
(
box
,
src
,
dst
,
rt_mat
=
None
,
with_yaw
=
True
):
"""Convert boxes from `src` mode to `dst` mode.
Args:
box (tuple | list | np.ndarray |
torch.Tensor | BaseInstance3DBoxes):
torch.Tensor |
:obj:`
BaseInstance3DBoxes
`
):
Can be a k-tuple, k-list or an Nxk array/tensor, where k = 7.
src (:obj:`Box3DMode`): The src Box mode.
dst (:obj:`Box3DMode`): The target Box mode.
...
...
@@ -75,9 +76,13 @@ class Box3DMode(IntEnum):
The conversion from `src` coordinates to `dst` coordinates
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
with_yaw (bool): If `box` is an instance of
:obj:`BaseInstance3DBoxes`, whether or not it has a yaw angle.
Defaults to True.
Returns:
(tuple | list | np.ndarray | torch.Tensor | BaseInstance3DBoxes):
\
(tuple | list | np.ndarray | torch.Tensor |
:obj:`BaseInstance3DBoxes`):
The converted box of the same type.
"""
if
src
==
dst
:
...
...
@@ -100,32 +105,53 @@ class Box3DMode(IntEnum):
else
:
arr
=
box
.
clone
()
if
is_Instance3DBoxes
:
with_yaw
=
box
.
with_yaw
# convert box from `src` mode to `dst` mode.
x_size
,
y_size
,
z_size
=
arr
[...,
3
:
4
],
arr
[...,
4
:
5
],
arr
[...,
5
:
6
]
if
with_yaw
:
yaw
=
arr
[...,
6
:
7
]
if
src
==
Box3DMode
.
LIDAR
and
dst
==
Box3DMode
.
CAM
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
0
,
0
,
-
1
],
[
1
,
0
,
0
]])
xyz_size
=
torch
.
cat
([
y_size
,
z_size
,
x_size
],
dim
=-
1
)
xyz_size
=
torch
.
cat
([
x_size
,
z_size
,
y_size
],
dim
=-
1
)
if
with_yaw
:
yaw
=
-
yaw
-
np
.
pi
/
2
yaw
=
limit_period
(
yaw
,
period
=
np
.
pi
*
2
)
elif
src
==
Box3DMode
.
CAM
and
dst
==
Box3DMode
.
LIDAR
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
0
,
1
],
[
-
1
,
0
,
0
],
[
0
,
-
1
,
0
]])
xyz_size
=
torch
.
cat
([
z_size
,
x_size
,
y_size
],
dim
=-
1
)
xyz_size
=
torch
.
cat
([
x_size
,
z_size
,
y_size
],
dim
=-
1
)
if
with_yaw
:
yaw
=
-
yaw
-
np
.
pi
/
2
yaw
=
limit_period
(
yaw
,
period
=
np
.
pi
*
2
)
elif
src
==
Box3DMode
.
DEPTH
and
dst
==
Box3DMode
.
CAM
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
1
,
0
,
0
],
[
0
,
0
,
1
],
[
0
,
-
1
,
0
]])
xyz_size
=
torch
.
cat
([
x_size
,
z_size
,
y_size
],
dim
=-
1
)
if
with_yaw
:
yaw
=
-
yaw
elif
src
==
Box3DMode
.
CAM
and
dst
==
Box3DMode
.
DEPTH
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
1
,
0
,
0
],
[
0
,
0
,
-
1
],
[
0
,
1
,
0
]])
xyz_size
=
torch
.
cat
([
x_size
,
z_size
,
y_size
],
dim
=-
1
)
if
with_yaw
:
yaw
=
-
yaw
elif
src
==
Box3DMode
.
LIDAR
and
dst
==
Box3DMode
.
DEPTH
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
1
,
0
,
0
],
[
0
,
0
,
1
]])
xyz_size
=
torch
.
cat
([
y_size
,
x_size
,
z_size
],
dim
=-
1
)
xyz_size
=
torch
.
cat
([
x_size
,
y_size
,
z_size
],
dim
=-
1
)
if
with_yaw
:
yaw
=
yaw
+
np
.
pi
/
2
yaw
=
limit_period
(
yaw
,
period
=
np
.
pi
*
2
)
elif
src
==
Box3DMode
.
DEPTH
and
dst
==
Box3DMode
.
LIDAR
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
1
,
0
],
[
-
1
,
0
,
0
],
[
0
,
0
,
1
]])
xyz_size
=
torch
.
cat
([
y_size
,
x_size
,
z_size
],
dim
=-
1
)
xyz_size
=
torch
.
cat
([
x_size
,
y_size
,
z_size
],
dim
=-
1
)
if
with_yaw
:
yaw
=
yaw
-
np
.
pi
/
2
yaw
=
limit_period
(
yaw
,
period
=
np
.
pi
*
2
)
else
:
raise
NotImplementedError
(
f
'Conversion from Box3DMode
{
src
}
to
{
dst
}
'
...
...
@@ -135,13 +161,17 @@ class Box3DMode(IntEnum):
rt_mat
=
arr
.
new_tensor
(
rt_mat
)
if
rt_mat
.
size
(
1
)
==
4
:
extended_xyz
=
torch
.
cat
(
[
arr
[
:
,
:
3
],
arr
.
new_ones
(
arr
.
size
(
0
),
1
)],
dim
=-
1
)
[
arr
[
...
,
:
3
],
arr
.
new_ones
(
arr
.
size
(
0
),
1
)],
dim
=-
1
)
xyz
=
extended_xyz
@
rt_mat
.
t
()
else
:
xyz
=
arr
[
:
,
:
3
]
@
rt_mat
.
t
()
xyz
=
arr
[
...
,
:
3
]
@
rt_mat
.
t
()
remains
=
arr
[...,
6
:]
arr
=
torch
.
cat
([
xyz
[:,
:
3
],
xyz_size
,
remains
],
dim
=-
1
)
if
with_yaw
:
remains
=
arr
[...,
7
:]
arr
=
torch
.
cat
([
xyz
[...,
:
3
],
xyz_size
,
yaw
,
remains
],
dim
=-
1
)
else
:
remains
=
arr
[...,
6
:]
arr
=
torch
.
cat
([
xyz
[...,
:
3
],
xyz_size
,
remains
],
dim
=-
1
)
# convert arr to the original type
original_type
=
type
(
box
)
...
...
@@ -160,7 +190,6 @@ class Box3DMode(IntEnum):
raise
NotImplementedError
(
f
'Conversion to
{
dst
}
through
{
original_type
}
'
' is not supported yet'
)
return
target_type
(
arr
,
box_dim
=
arr
.
size
(
-
1
),
with_yaw
=
box
.
with_yaw
)
return
target_type
(
arr
,
box_dim
=
arr
.
size
(
-
1
),
with_yaw
=
with_yaw
)
else
:
return
arr
mmdet3d/core/bbox/structures/cam_box3d.py
View file @
2eebdc2d
...
...
@@ -2,7 +2,7 @@
import
numpy
as
np
import
torch
from
mmdet3d.core
.points
import
BasePoints
from
..
.points
import
BasePoints
from
.base_box3d
import
BaseInstance3DBoxes
from
.utils
import
limit_period
,
rotation_3d_in_axis
...
...
@@ -38,6 +38,7 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
boxes.
"""
YAW_AXIS
=
1
def
__init__
(
self
,
tensor
,
...
...
@@ -117,16 +118,16 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
/ | / |
(x0, y0, z0) + ----------- + + (x1, y1, z1)
| / . | /
| / ori
i
gn | /
| / orig
i
n | /
(x0, y1, z0) + ----------- + -------> x right
| (x1, y1, z0)
|
v
down y
"""
# TODO: rotation_3d_in_axis function do not support
#
empty tensor currently.
assert
len
(
self
.
tensor
)
!=
0
if
self
.
tensor
.
numel
()
==
0
:
return
torch
.
empty
([
0
,
8
,
3
],
device
=
self
.
tensor
.
device
)
dims
=
self
.
dims
corners_norm
=
torch
.
from_numpy
(
np
.
stack
(
np
.
unravel_index
(
np
.
arange
(
8
),
[
2
]
*
3
),
axis
=
1
)).
to
(
...
...
@@ -137,8 +138,11 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
corners_norm
=
corners_norm
-
dims
.
new_tensor
([
0.5
,
1
,
0.5
])
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
# rotate around y axis
corners
=
rotation_3d_in_axis
(
corners
,
self
.
tensor
[:,
6
],
axis
=
1
)
# positive direction of the gravity axis
# in cam coord system points to the earth
# so the rotation is clockwise if viewed from above
corners
=
rotation_3d_in_axis
(
corners
,
self
.
tensor
[:,
6
],
axis
=
self
.
YAW_AXIS
,
clockwise
=
True
)
corners
+=
self
.
tensor
[:,
:
3
].
view
(
-
1
,
1
,
3
)
return
corners
...
...
@@ -146,7 +150,12 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
def
bev
(
self
):
"""torch.Tensor: A n x 5 tensor of 2D BEV box of each box
with rotation in XYWHR format."""
return
self
.
tensor
[:,
[
0
,
2
,
3
,
5
,
6
]]
bev
=
self
.
tensor
[:,
[
0
,
2
,
3
,
5
,
6
]].
clone
()
# positive direction of the gravity axis
# in cam coord system points to the earth
# so the bev yaw angle needs to be reversed
bev
[:,
-
1
]
=
-
bev
[:,
-
1
]
return
bev
@
property
def
nearest_bev
(
self
):
...
...
@@ -170,8 +179,8 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
return
bev_boxes
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
rotation
matrix.
"""Rotate boxes with points (optional) with the given angle or
rotation
matrix.
Args:
angle (float | torch.Tensor | np.ndarray):
...
...
@@ -180,39 +189,43 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
Points to rotate. Defaults to None.
Returns:
tuple or None: When ``points`` is None, the function returns
\
None, otherwise it returns the rotated points and the
\
tuple or None: When ``points`` is None, the function returns
None, otherwise it returns the rotated points and the
rotation matrix ``rot_mat_T``.
"""
if
not
isinstance
(
angle
,
torch
.
Tensor
):
angle
=
self
.
tensor
.
new_tensor
(
angle
)
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
f
'invalid rotation angle shape
{
angle
.
shape
}
'
if
angle
.
numel
()
==
1
:
rot_sin
=
torch
.
sin
(
angle
)
rot_cos
=
torch
.
cos
(
angle
)
rot_mat_T
=
self
.
tensor
.
new_tensor
([[
rot_cos
,
0
,
-
rot_sin
],
[
0
,
1
,
0
],
[
rot_sin
,
0
,
rot_cos
]])
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
rotation_3d_in_axis
(
self
.
tensor
[:,
0
:
3
],
angle
,
axis
=
self
.
YAW_AXIS
,
return_mat
=
True
,
# positive direction of the gravity axis
# in cam coord system points to the earth
# so the rotation is clockwise if viewed from above
clockwise
=
True
)
else
:
rot_mat_T
=
angle
rot_sin
=
rot_mat_T
[
2
,
0
]
rot_cos
=
rot_mat_T
[
0
,
0
]
angle
=
np
.
arctan2
(
rot_sin
,
rot_cos
)
self
.
tensor
[:,
0
:
3
]
=
self
.
tensor
[:,
0
:
3
]
@
rot_mat_T
self
.
tensor
[:,
:
3
]
=
self
.
tensor
[:,
:
3
]
@
rot_mat_T
self
.
tensor
[:,
6
]
+=
angle
if
points
is
not
None
:
if
isinstance
(
points
,
torch
.
Tensor
):
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
elif
isinstance
(
points
,
np
.
ndarray
):
rot_mat_T
=
rot_mat_T
.
numpy
()
rot_mat_T
=
rot_mat_T
.
cpu
().
numpy
()
points
[:,
:
3
]
=
np
.
dot
(
points
[:,
:
3
],
rot_mat_T
)
elif
isinstance
(
points
,
BasePoints
):
# clockwise
points
.
rotate
(
-
angle
)
points
.
rotate
(
rot_mat_T
)
else
:
raise
ValueError
return
points
,
rot_mat_T
...
...
@@ -264,7 +277,7 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
polygon, we reduce the burden for simpler cases.
Returns:
torch.Tensor: Indicating whether each box is inside
\
torch.Tensor: Indicating whether each box is inside
the reference range.
"""
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
...
...
@@ -296,8 +309,8 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
boxes2_top_height
=
boxes2
.
top_height
.
view
(
1
,
-
1
)
boxes2_bottom_height
=
boxes2
.
bottom_height
.
view
(
1
,
-
1
)
#
In camera coordinate system
#
from up to down is the positive direction
#
positive direction of the gravity axis
#
in cam coord system points to the earth
heighest_of_bottom
=
torch
.
min
(
boxes1_bottom_height
,
boxes2_bottom_height
)
lowest_of_top
=
torch
.
max
(
boxes1_top_height
,
boxes2_top_height
)
...
...
@@ -316,9 +329,50 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
to LiDAR. This requires a transformation matrix.
Returns:
:obj:`BaseInstance3DBoxes`:
\
:obj:`BaseInstance3DBoxes`:
The converted box of the same type in the ``dst`` mode.
"""
from
.box_3d_mode
import
Box3DMode
return
Box3DMode
.
convert
(
box
=
self
,
src
=
Box3DMode
.
CAM
,
dst
=
dst
,
rt_mat
=
rt_mat
)
def
points_in_boxes
(
self
,
points
):
"""Find the box which the points are in.
Args:
points (torch.Tensor): Points in shape (N, 3).
Returns:
torch.Tensor: The index of box where each point are in.
"""
from
.coord_3d_mode
import
Coord3DMode
points_lidar
=
Coord3DMode
.
convert
(
points
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
boxes_lidar
=
Coord3DMode
.
convert
(
self
.
tensor
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
box_idx
=
super
().
points_in_boxes
(
self
,
points_lidar
,
boxes_lidar
)
return
box_idx
def
points_in_boxes_batch
(
self
,
points
):
"""Find points that are in boxes (CUDA).
Args:
points (torch.Tensor): Points in shape [1, M, 3] or [M, 3],
3 dimensions are [x, y, z] in LiDAR coordinate.
Returns:
torch.Tensor: The index of boxes each point lies in with shape
of (B, M, T).
"""
from
.coord_3d_mode
import
Coord3DMode
points_lidar
=
Coord3DMode
.
convert
(
points
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
boxes_lidar
=
Coord3DMode
.
convert
(
self
.
tensor
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
box_idx
=
super
().
points_in_boxes_batch
(
self
,
points_lidar
,
boxes_lidar
)
return
box_idx
mmdet3d/core/bbox/structures/coord_3d_mode.py
View file @
2eebdc2d
...
...
@@ -3,12 +3,9 @@ import numpy as np
import
torch
from
enum
import
IntEnum
,
unique
from
mmdet3d.core.points
import
(
BasePoints
,
CameraPoints
,
DepthPoints
,
LiDARPoints
)
from
...points
import
BasePoints
,
CameraPoints
,
DepthPoints
,
LiDARPoints
from
.base_box3d
import
BaseInstance3DBoxes
from
.cam_box3d
import
CameraInstance3DBoxes
from
.depth_box3d
import
DepthInstance3DBoxes
from
.lidar_box3d
import
LiDARInstance3DBoxes
from
.box_3d_mode
import
Box3DMode
@
unique
...
...
@@ -64,119 +61,73 @@ class Coord3DMode(IntEnum):
DEPTH
=
2
@
staticmethod
def
convert
(
input
,
src
,
dst
,
rt_mat
=
None
):
"""Convert boxes or points from `src` mode to `dst` mode."""
def
convert
(
input
,
src
,
dst
,
rt_mat
=
None
,
with_yaw
=
True
,
is_point
=
True
):
"""Convert boxes or points from `src` mode to `dst` mode.
Args:
input (tuple | list | np.ndarray | torch.Tensor |
:obj:`BaseInstance3DBoxes` | :obj:`BasePoints`):
Can be a k-tuple, k-list or an Nxk array/tensor, where k = 7.
src (:obj:`Box3DMode` | :obj:`Coord3DMode`): The source mode.
dst (:obj:`Box3DMode` | :obj:`Coord3DMode`): The target mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
matrix between different coordinates. Defaults to None.
The conversion from `src` coordinates to `dst` coordinates
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
with_yaw (bool): If `box` is an instance of
:obj:`BaseInstance3DBoxes`, whether or not it has a yaw angle.
Defaults to True.
is_point (bool): If `input` is neither an instance of
:obj:`BaseInstance3DBoxes` nor an instance of
:obj:`BasePoints`, whether or not it is point data.
Defaults to True.
Returns:
(tuple | list | np.ndarray | torch.Tensor |
:obj:`BaseInstance3DBoxes` | :obj:`BasePoints`):
The converted box of the same type.
"""
if
isinstance
(
input
,
BaseInstance3DBoxes
):
return
Coord3DMode
.
convert_box
(
input
,
src
,
dst
,
rt_mat
=
rt_mat
)
return
Coord3DMode
.
convert_box
(
input
,
src
,
dst
,
rt_mat
=
rt_mat
,
with_yaw
=
with_yaw
)
elif
isinstance
(
input
,
BasePoints
):
return
Coord3DMode
.
convert_point
(
input
,
src
,
dst
,
rt_mat
=
rt_mat
)
elif
isinstance
(
input
,
(
tuple
,
list
,
np
.
ndarray
,
torch
.
Tensor
)):
if
is_point
:
return
Coord3DMode
.
convert_point
(
input
,
src
,
dst
,
rt_mat
=
rt_mat
)
else
:
return
Coord3DMode
.
convert_box
(
input
,
src
,
dst
,
rt_mat
=
rt_mat
,
with_yaw
=
with_yaw
)
else
:
raise
NotImplementedError
@
staticmethod
def
convert_box
(
box
,
src
,
dst
,
rt_mat
=
None
):
def
convert_box
(
box
,
src
,
dst
,
rt_mat
=
None
,
with_yaw
=
True
):
"""Convert boxes from `src` mode to `dst` mode.
Args:
box (tuple | list | np.ndarray |
torch.Tensor | BaseInstance3DBoxes):
torch.Tensor |
:obj:`
BaseInstance3DBoxes
`
):
Can be a k-tuple, k-list or an Nxk array/tensor, where k = 7.
src (:obj:`
Coord
Mode`): The src Box mode.
dst (:obj:`
Coord
Mode`): The target Box mode.
src (:obj:`
Box3D
Mode`): The src Box mode.
dst (:obj:`
Box3D
Mode`): The target Box mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
matrix between different coordinates. Defaults to None.
The conversion from `src` coordinates to `dst` coordinates
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
with_yaw (bool): If `box` is an instance of
:obj:`BaseInstance3DBoxes`, whether or not it has a yaw angle.
Defaults to True.
Returns:
(tuple | list | np.ndarray | torch.Tensor | BaseInstance3DBoxes):
\
(tuple | list | np.ndarray | torch.Tensor |
:obj:`BaseInstance3DBoxes`):
The converted box of the same type.
"""
if
src
==
dst
:
return
box
is_numpy
=
isinstance
(
box
,
np
.
ndarray
)
is_Instance3DBoxes
=
isinstance
(
box
,
BaseInstance3DBoxes
)
single_box
=
isinstance
(
box
,
(
list
,
tuple
))
if
single_box
:
assert
len
(
box
)
>=
7
,
(
'CoordMode.convert takes either a k-tuple/list or '
'an Nxk array/tensor, where k >= 7'
)
arr
=
torch
.
tensor
(
box
)[
None
,
:]
else
:
# avoid modifying the input box
if
is_numpy
:
arr
=
torch
.
from_numpy
(
np
.
asarray
(
box
)).
clone
()
elif
is_Instance3DBoxes
:
arr
=
box
.
tensor
.
clone
()
else
:
arr
=
box
.
clone
()
# convert box from `src` mode to `dst` mode.
x_size
,
y_size
,
z_size
=
arr
[...,
3
:
4
],
arr
[...,
4
:
5
],
arr
[...,
5
:
6
]
if
src
==
Coord3DMode
.
LIDAR
and
dst
==
Coord3DMode
.
CAM
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
0
,
0
,
-
1
],
[
1
,
0
,
0
]])
xyz_size
=
torch
.
cat
([
y_size
,
z_size
,
x_size
],
dim
=-
1
)
elif
src
==
Coord3DMode
.
CAM
and
dst
==
Coord3DMode
.
LIDAR
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
0
,
1
],
[
-
1
,
0
,
0
],
[
0
,
-
1
,
0
]])
xyz_size
=
torch
.
cat
([
z_size
,
x_size
,
y_size
],
dim
=-
1
)
elif
src
==
Coord3DMode
.
DEPTH
and
dst
==
Coord3DMode
.
CAM
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
1
,
0
,
0
],
[
0
,
0
,
1
],
[
0
,
-
1
,
0
]])
xyz_size
=
torch
.
cat
([
x_size
,
z_size
,
y_size
],
dim
=-
1
)
elif
src
==
Coord3DMode
.
CAM
and
dst
==
Coord3DMode
.
DEPTH
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
1
,
0
,
0
],
[
0
,
0
,
-
1
],
[
0
,
1
,
0
]])
xyz_size
=
torch
.
cat
([
x_size
,
z_size
,
y_size
],
dim
=-
1
)
elif
src
==
Coord3DMode
.
LIDAR
and
dst
==
Coord3DMode
.
DEPTH
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
1
,
0
,
0
],
[
0
,
0
,
1
]])
xyz_size
=
torch
.
cat
([
y_size
,
x_size
,
z_size
],
dim
=-
1
)
elif
src
==
Coord3DMode
.
DEPTH
and
dst
==
Coord3DMode
.
LIDAR
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
1
,
0
],
[
-
1
,
0
,
0
],
[
0
,
0
,
1
]])
xyz_size
=
torch
.
cat
([
y_size
,
x_size
,
z_size
],
dim
=-
1
)
else
:
raise
NotImplementedError
(
f
'Conversion from Coord3DMode
{
src
}
to
{
dst
}
'
'is not supported yet'
)
if
not
isinstance
(
rt_mat
,
torch
.
Tensor
):
rt_mat
=
arr
.
new_tensor
(
rt_mat
)
if
rt_mat
.
size
(
1
)
==
4
:
extended_xyz
=
torch
.
cat
(
[
arr
[:,
:
3
],
arr
.
new_ones
(
arr
.
size
(
0
),
1
)],
dim
=-
1
)
xyz
=
extended_xyz
@
rt_mat
.
t
()
else
:
xyz
=
arr
[:,
:
3
]
@
rt_mat
.
t
()
remains
=
arr
[...,
6
:]
arr
=
torch
.
cat
([
xyz
[:,
:
3
],
xyz_size
,
remains
],
dim
=-
1
)
# convert arr to the original type
original_type
=
type
(
box
)
if
single_box
:
return
original_type
(
arr
.
flatten
().
tolist
())
if
is_numpy
:
return
arr
.
numpy
()
elif
is_Instance3DBoxes
:
if
dst
==
Coord3DMode
.
CAM
:
target_type
=
CameraInstance3DBoxes
elif
dst
==
Coord3DMode
.
LIDAR
:
target_type
=
LiDARInstance3DBoxes
elif
dst
==
Coord3DMode
.
DEPTH
:
target_type
=
DepthInstance3DBoxes
else
:
raise
NotImplementedError
(
f
'Conversion to
{
dst
}
through
{
original_type
}
'
' is not supported yet'
)
return
target_type
(
arr
,
box_dim
=
arr
.
size
(
-
1
),
with_yaw
=
box
.
with_yaw
)
else
:
return
arr
return
Box3DMode
.
convert
(
box
,
src
,
dst
,
rt_mat
=
rt_mat
)
@
staticmethod
def
convert_point
(
point
,
src
,
dst
,
rt_mat
=
None
):
...
...
@@ -184,7 +135,7 @@ class Coord3DMode(IntEnum):
Args:
point (tuple | list | np.ndarray |
torch.Tensor | BasePoints):
torch.Tensor |
:obj:`
BasePoints
`
):
Can be a k-tuple, k-list or an Nxk array/tensor.
src (:obj:`CoordMode`): The src Point mode.
dst (:obj:`CoordMode`): The target Point mode.
...
...
@@ -195,7 +146,7 @@ class Coord3DMode(IntEnum):
to LiDAR. This requires a transformation matrix.
Returns:
(tuple | list | np.ndarray | torch.Tensor | BasePoints):
\
(tuple | list | np.ndarray | torch.Tensor |
:obj:`
BasePoints
`
):
The converted point of the same type.
"""
if
src
==
dst
:
...
...
@@ -219,8 +170,6 @@ class Coord3DMode(IntEnum):
arr
=
point
.
clone
()
# convert point from `src` mode to `dst` mode.
# TODO: LIDAR
# only implemented provided Rt matrix in cam-depth conversion
if
src
==
Coord3DMode
.
LIDAR
and
dst
==
Coord3DMode
.
CAM
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
0
,
0
,
-
1
],
[
1
,
0
,
0
]])
...
...
@@ -248,13 +197,13 @@ class Coord3DMode(IntEnum):
rt_mat
=
arr
.
new_tensor
(
rt_mat
)
if
rt_mat
.
size
(
1
)
==
4
:
extended_xyz
=
torch
.
cat
(
[
arr
[
:
,
:
3
],
arr
.
new_ones
(
arr
.
size
(
0
),
1
)],
dim
=-
1
)
[
arr
[
...
,
:
3
],
arr
.
new_ones
(
arr
.
size
(
0
),
1
)],
dim
=-
1
)
xyz
=
extended_xyz
@
rt_mat
.
t
()
else
:
xyz
=
arr
[
:
,
:
3
]
@
rt_mat
.
t
()
xyz
=
arr
[
...
,
:
3
]
@
rt_mat
.
t
()
remains
=
arr
[
:
,
3
:]
arr
=
torch
.
cat
([
xyz
[
:
,
:
3
],
remains
],
dim
=-
1
)
remains
=
arr
[
...
,
3
:]
arr
=
torch
.
cat
([
xyz
[
...
,
:
3
],
remains
],
dim
=-
1
)
# convert arr to the original type
original_type
=
type
(
point
)
...
...
mmdet3d/core/bbox/structures/depth_box3d.py
View file @
2eebdc2d
...
...
@@ -3,7 +3,6 @@ import numpy as np
import
torch
from
mmdet3d.core.points
import
BasePoints
from
mmdet3d.ops
import
points_in_boxes_batch
from
.base_box3d
import
BaseInstance3DBoxes
from
.utils
import
limit_period
,
rotation_3d_in_axis
...
...
@@ -38,6 +37,7 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
boxes.
"""
YAW_AXIS
=
2
@
property
def
gravity_center
(
self
):
...
...
@@ -67,7 +67,7 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
/ | / |
(x0, y0, z1) + ----------- + + (x1, y1, z0)
| / . | /
| / ori
i
gn | /
| / orig
i
n | /
(x0, y0, z0) + ----------- + --------> right x
(x1, y0, z0)
"""
...
...
@@ -85,7 +85,8 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
# rotate around z axis
corners
=
rotation_3d_in_axis
(
corners
,
self
.
tensor
[:,
6
],
axis
=
2
)
corners
=
rotation_3d_in_axis
(
corners
,
self
.
tensor
[:,
6
],
axis
=
self
.
YAW_AXIS
)
corners
+=
self
.
tensor
[:,
:
3
].
view
(
-
1
,
1
,
3
)
return
corners
...
...
@@ -117,8 +118,8 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
return
bev_boxes
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
rotation
matrix.
"""Rotate boxes with points (optional) with the given angle or
rotation
matrix.
Args:
angle (float | torch.Tensor | np.ndarray):
...
...
@@ -127,30 +128,31 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
Points to rotate. Defaults to None.
Returns:
tuple or None: When ``points`` is None, the function returns
\
None, otherwise it returns the rotated points and the
\
tuple or None: When ``points`` is None, the function returns
None, otherwise it returns the rotated points and the
rotation matrix ``rot_mat_T``.
"""
if
not
isinstance
(
angle
,
torch
.
Tensor
):
angle
=
self
.
tensor
.
new_tensor
(
angle
)
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
f
'invalid rotation angle shape
{
angle
.
shape
}
'
if
angle
.
numel
()
==
1
:
rot_sin
=
torch
.
sin
(
angle
)
rot_cos
=
torch
.
cos
(
angle
)
rot_mat_T
=
self
.
tensor
.
new_tensor
([[
rot_cos
,
-
rot_sin
,
0
]
,
[
rot_sin
,
rot_cos
,
0
]
,
[
0
,
0
,
1
]]).
T
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
rotation_3d_in_axis
(
self
.
tensor
[:,
0
:
3
],
angle
,
axis
=
self
.
YAW_AXIS
,
return_mat
=
True
)
else
:
rot_mat_T
=
angle
.
T
rot_mat_T
=
angle
rot_sin
=
rot_mat_T
[
0
,
1
]
rot_cos
=
rot_mat_T
[
0
,
0
]
angle
=
np
.
arctan2
(
rot_sin
,
rot_cos
)
self
.
tensor
[:,
0
:
3
]
=
self
.
tensor
[:,
0
:
3
]
@
rot_mat_T
self
.
tensor
[:,
0
:
3
]
=
self
.
tensor
[:,
0
:
3
]
@
rot_mat_T
if
self
.
with_yaw
:
self
.
tensor
[:,
6
]
-
=
angle
self
.
tensor
[:,
6
]
+
=
angle
else
:
corners_rot
=
self
.
corners
@
rot_mat_T
new_x_size
=
corners_rot
[...,
0
].
max
(
...
...
@@ -165,11 +167,10 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
if
isinstance
(
points
,
torch
.
Tensor
):
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
elif
isinstance
(
points
,
np
.
ndarray
):
rot_mat_T
=
rot_mat_T
.
numpy
()
rot_mat_T
=
rot_mat_T
.
cpu
().
numpy
()
points
[:,
:
3
]
=
np
.
dot
(
points
[:,
:
3
],
rot_mat_T
)
elif
isinstance
(
points
,
BasePoints
):
# anti-clockwise
points
.
rotate
(
angle
)
points
.
rotate
(
rot_mat_T
)
else
:
raise
ValueError
return
points
,
rot_mat_T
...
...
@@ -221,7 +222,7 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
polygon, we try to reduce the burdun for simpler cases.
Returns:
torch.Tensor: Indicating whether each box is inside
\
torch.Tensor: Indicating whether each box is inside
the reference range.
"""
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
...
...
@@ -242,41 +243,13 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
to LiDAR. This requires a transformation matrix.
Returns:
:obj:`DepthInstance3DBoxes`:
\
:obj:`DepthInstance3DBoxes`:
The converted box of the same type in the ``dst`` mode.
"""
from
.box_3d_mode
import
Box3DMode
return
Box3DMode
.
convert
(
box
=
self
,
src
=
Box3DMode
.
DEPTH
,
dst
=
dst
,
rt_mat
=
rt_mat
)
def
points_in_boxes
(
self
,
points
):
"""Find points that are in boxes (CUDA).
Args:
points (torch.Tensor): Points in shape [1, M, 3] or [M, 3],
\
3 dimensions are [x, y, z] in LiDAR coordinate.
Returns:
torch.Tensor: The index of boxes each point lies in with shape
\
of (B, M, T).
"""
from
.box_3d_mode
import
Box3DMode
# to lidar
points_lidar
=
points
.
clone
()
points_lidar
=
points_lidar
[...,
[
1
,
0
,
2
]]
points_lidar
[...,
1
]
*=
-
1
if
points
.
dim
()
==
2
:
points_lidar
=
points_lidar
.
unsqueeze
(
0
)
else
:
assert
points
.
dim
()
==
3
and
points_lidar
.
shape
[
0
]
==
1
boxes_lidar
=
self
.
convert_to
(
Box3DMode
.
LIDAR
).
tensor
boxes_lidar
=
boxes_lidar
.
to
(
points
.
device
).
unsqueeze
(
0
)
box_idxs_of_pts
=
points_in_boxes_batch
(
points_lidar
,
boxes_lidar
)
return
box_idxs_of_pts
.
squeeze
(
0
)
def
enlarged_box
(
self
,
extra_width
):
"""Enlarge the length, width and height boxes.
...
...
@@ -331,13 +304,12 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
-
1
,
3
)
surface_rot
=
rot_mat_T
.
repeat
(
6
,
1
,
1
)
surface_3d
=
torch
.
matmul
(
surface_3d
.
unsqueeze
(
-
2
),
surface_rot
.
transpose
(
2
,
1
)
).
squeeze
(
-
2
)
surface_3d
=
torch
.
matmul
(
surface_3d
.
unsqueeze
(
-
2
),
surface_rot
).
squeeze
(
-
2
)
surface_center
=
center
.
repeat
(
1
,
6
,
1
).
reshape
(
-
1
,
3
)
+
surface_3d
line_rot
=
rot_mat_T
.
repeat
(
12
,
1
,
1
)
line_3d
=
torch
.
matmul
(
line_3d
.
unsqueeze
(
-
2
),
line_rot
.
transpose
(
2
,
1
)).
squeeze
(
-
2
)
line_3d
=
torch
.
matmul
(
line_3d
.
unsqueeze
(
-
2
),
line_rot
).
squeeze
(
-
2
)
line_center
=
center
.
repeat
(
1
,
12
,
1
).
reshape
(
-
1
,
3
)
+
line_3d
return
surface_center
,
line_center
mmdet3d/core/bbox/structures/lidar_box3d.py
View file @
2eebdc2d
...
...
@@ -3,7 +3,6 @@ import numpy as np
import
torch
from
mmdet3d.core.points
import
BasePoints
from
mmdet3d.ops.roiaware_pool3d
import
points_in_boxes_gpu
from
.base_box3d
import
BaseInstance3DBoxes
from
.utils
import
limit_period
,
rotation_3d_in_axis
...
...
@@ -15,16 +14,16 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
.. code-block:: none
up z x front (yaw=
-0.5*pi
)
^ ^
| /
| /
(yaw=
-
pi) left y <------ 0
-------- (yaw=0)
up z x front (yaw=
0
)
^ ^
| /
| /
(yaw=
0.5*
pi) left y <------ 0
The relative coordinate of bottom center in a LiDAR box is (0.5, 0.5, 0),
and the yaw is around the z axis, thus the rotation axis=2.
The yaw is 0 at the
nega
tive direction of
y
axis, and
de
creases from
the
nega
tive direction of
y
to the positive direction of
x
.
The yaw is 0 at the
posi
tive direction of
x
axis, and
in
creases from
the
posi
tive direction of
x
to the positive direction of
y
.
A refactor is ongoing to make the three coordinate systems
easier to understand and convert between each other.
...
...
@@ -36,6 +35,7 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
boxes.
"""
YAW_AXIS
=
2
@
property
def
gravity_center
(
self
):
...
...
@@ -65,7 +65,7 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
/ | / |
(x0, y0, z1) + ----------- + + (x1, y1, z0)
| / . | /
| / ori
i
gn | /
| / orig
i
n | /
left y<-------- + ----------- + (x0, y1, z0)
(x0, y0, z0)
"""
...
...
@@ -83,7 +83,8 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
# rotate around z axis
corners
=
rotation_3d_in_axis
(
corners
,
self
.
tensor
[:,
6
],
axis
=
2
)
corners
=
rotation_3d_in_axis
(
corners
,
self
.
tensor
[:,
6
],
axis
=
self
.
YAW_AXIS
)
corners
+=
self
.
tensor
[:,
:
3
].
view
(
-
1
,
1
,
3
)
return
corners
...
...
@@ -115,8 +116,8 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
return
bev_boxes
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
rotation
matrix.
"""Rotate boxes with points (optional) with the given angle or
rotation
matrix.
Args:
angles (float | torch.Tensor | np.ndarray):
...
...
@@ -125,28 +126,29 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
Points to rotate. Defaults to None.
Returns:
tuple or None: When ``points`` is None, the function returns
\
None, otherwise it returns the rotated points and the
\
tuple or None: When ``points`` is None, the function returns
None, otherwise it returns the rotated points and the
rotation matrix ``rot_mat_T``.
"""
if
not
isinstance
(
angle
,
torch
.
Tensor
):
angle
=
self
.
tensor
.
new_tensor
(
angle
)
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
f
'invalid rotation angle shape
{
angle
.
shape
}
'
if
angle
.
numel
()
==
1
:
rot_sin
=
torch
.
sin
(
angle
)
rot_cos
=
torch
.
cos
(
angle
)
rot_mat_T
=
self
.
tensor
.
new_tensor
([[
rot_cos
,
-
rot_sin
,
0
]
,
[
rot_sin
,
rot_cos
,
0
]
,
[
0
,
0
,
1
]]
)
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
rotation_3d_in_axis
(
self
.
tensor
[:,
0
:
3
],
angle
,
axis
=
self
.
YAW_AXIS
,
return_mat
=
True
)
else
:
rot_mat_T
=
angle
rot_sin
=
rot_mat_T
[
1
,
0
]
rot_sin
=
rot_mat_T
[
0
,
1
]
rot_cos
=
rot_mat_T
[
0
,
0
]
angle
=
np
.
arctan2
(
rot_sin
,
rot_cos
)
self
.
tensor
[:,
0
:
3
]
=
self
.
tensor
[:,
0
:
3
]
@
rot_mat_T
self
.
tensor
[:,
:
3
]
=
self
.
tensor
[:,
:
3
]
@
rot_mat_T
self
.
tensor
[:,
6
]
+=
angle
if
self
.
tensor
.
shape
[
1
]
==
9
:
...
...
@@ -157,11 +159,10 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
if
isinstance
(
points
,
torch
.
Tensor
):
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
elif
isinstance
(
points
,
np
.
ndarray
):
rot_mat_T
=
rot_mat_T
.
numpy
()
rot_mat_T
=
rot_mat_T
.
cpu
().
numpy
()
points
[:,
:
3
]
=
np
.
dot
(
points
[:,
:
3
],
rot_mat_T
)
elif
isinstance
(
points
,
BasePoints
):
# clockwise
points
.
rotate
(
-
angle
)
points
.
rotate
(
rot_mat_T
)
else
:
raise
ValueError
return
points
,
rot_mat_T
...
...
@@ -183,11 +184,11 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
if
bev_direction
==
'horizontal'
:
self
.
tensor
[:,
1
::
7
]
=
-
self
.
tensor
[:,
1
::
7
]
if
self
.
with_yaw
:
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
+
np
.
pi
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
elif
bev_direction
==
'vertical'
:
self
.
tensor
[:,
0
::
7
]
=
-
self
.
tensor
[:,
0
::
7
]
if
self
.
with_yaw
:
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
+
np
.
pi
if
points
is
not
None
:
assert
isinstance
(
points
,
(
torch
.
Tensor
,
np
.
ndarray
,
BasePoints
))
...
...
@@ -233,7 +234,7 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
to LiDAR. This requires a transformation matrix.
Returns:
:obj:`BaseInstance3DBoxes`:
\
:obj:`BaseInstance3DBoxes`:
The converted box of the same type in the ``dst`` mode.
"""
from
.box_3d_mode
import
Box3DMode
...
...
@@ -254,17 +255,3 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
# bottom center z minus extra_width
enlarged_boxes
[:,
2
]
-=
extra_width
return
self
.
new_box
(
enlarged_boxes
)
def
points_in_boxes
(
self
,
points
):
"""Find the box which the points are in.
Args:
points (torch.Tensor): Points in shape (N, 3).
Returns:
torch.Tensor: The index of box where each point are in.
"""
box_idx
=
points_in_boxes_gpu
(
points
.
unsqueeze
(
0
),
self
.
tensor
.
unsqueeze
(
0
).
to
(
points
.
device
)).
squeeze
(
0
)
return
box_idx
Prev
1
2
3
4
5
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment