Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
mmdetection3d
Commits
32a4328b
Unverified
Commit
32a4328b
authored
Feb 24, 2022
by
Wenwei Zhang
Committed by
GitHub
Feb 24, 2022
Browse files
Bump version to V1.0.0rc0
Bump version to V1.0.0rc0
parents
86cc487c
a8817998
Changes
414
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1350 additions
and
637 deletions
+1350
-637
mmdet3d/core/bbox/coders/partial_bin_based_bbox_coder.py
mmdet3d/core/bbox/coders/partial_bin_based_bbox_coder.py
+1
-1
mmdet3d/core/bbox/coders/pgd_bbox_coder.py
mmdet3d/core/bbox/coders/pgd_bbox_coder.py
+128
-0
mmdet3d/core/bbox/coders/point_xyzwhlr_bbox_coder.py
mmdet3d/core/bbox/coders/point_xyzwhlr_bbox_coder.py
+117
-0
mmdet3d/core/bbox/coders/smoke_bbox_coder.py
mmdet3d/core/bbox/coders/smoke_bbox_coder.py
+208
-0
mmdet3d/core/bbox/iou_calculators/iou3d_calculator.py
mmdet3d/core/bbox/iou_calculators/iou3d_calculator.py
+27
-19
mmdet3d/core/bbox/samplers/iou_neg_piecewise_sampler.py
mmdet3d/core/bbox/samplers/iou_neg_piecewise_sampler.py
+33
-8
mmdet3d/core/bbox/structures/__init__.py
mmdet3d/core/bbox/structures/__init__.py
+4
-3
mmdet3d/core/bbox/structures/base_box3d.py
mmdet3d/core/bbox/structures/base_box3d.py
+173
-52
mmdet3d/core/bbox/structures/box_3d_mode.py
mmdet3d/core/bbox/structures/box_3d_mode.py
+49
-18
mmdet3d/core/bbox/structures/cam_box3d.py
mmdet3d/core/bbox/structures/cam_box3d.py
+111
-81
mmdet3d/core/bbox/structures/coord_3d_mode.py
mmdet3d/core/bbox/structures/coord_3d_mode.py
+63
-110
mmdet3d/core/bbox/structures/depth_box3d.py
mmdet3d/core/bbox/structures/depth_box3d.py
+34
-107
mmdet3d/core/bbox/structures/lidar_box3d.py
mmdet3d/core/bbox/structures/lidar_box3d.py
+34
-94
mmdet3d/core/bbox/structures/utils.py
mmdet3d/core/bbox/structures/utils.py
+173
-52
mmdet3d/core/bbox/transforms.py
mmdet3d/core/bbox/transforms.py
+5
-5
mmdet3d/core/evaluation/indoor_eval.py
mmdet3d/core/evaluation/indoor_eval.py
+8
-9
mmdet3d/core/evaluation/kitti_utils/eval.py
mmdet3d/core/evaluation/kitti_utils/eval.py
+157
-54
mmdet3d/core/evaluation/kitti_utils/rotate_iou.py
mmdet3d/core/evaluation/kitti_utils/rotate_iou.py
+18
-18
mmdet3d/core/evaluation/lyft_eval.py
mmdet3d/core/evaluation/lyft_eval.py
+6
-5
mmdet3d/core/evaluation/seg_eval.py
mmdet3d/core/evaluation/seg_eval.py
+1
-1
No files found.
mmdet3d/core/bbox/coders/partial_bin_based_bbox_coder.py
View file @
32a4328b
...
@@ -29,7 +29,7 @@ class PartialBinBasedBBoxCoder(BaseBBoxCoder):
...
@@ -29,7 +29,7 @@ class PartialBinBasedBBoxCoder(BaseBBoxCoder):
"""Encode ground truth to prediction targets.
"""Encode ground truth to prediction targets.
Args:
Args:
gt_bboxes_3d (BaseInstance3DBoxes): Ground truth bboxes
\
gt_bboxes_3d (BaseInstance3DBoxes): Ground truth bboxes
with shape (n, 7).
with shape (n, 7).
gt_labels_3d (torch.Tensor): Ground truth classes.
gt_labels_3d (torch.Tensor): Ground truth classes.
...
...
mmdet3d/core/bbox/coders/pgd_bbox_coder.py
0 → 100644
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
import
numpy
as
np
import
torch
from
torch.nn
import
functional
as
F
from
mmdet.core.bbox.builder
import
BBOX_CODERS
from
.fcos3d_bbox_coder
import
FCOS3DBBoxCoder
@
BBOX_CODERS
.
register_module
()
class
PGDBBoxCoder
(
FCOS3DBBoxCoder
):
"""Bounding box coder for PGD."""
def
encode
(
self
,
gt_bboxes_3d
,
gt_labels_3d
,
gt_bboxes
,
gt_labels
):
# TODO: refactor the encoder codes in the FCOS3D and PGD head
pass
def
decode_2d
(
self
,
bbox
,
scale
,
stride
,
max_regress_range
,
training
,
pred_keypoints
=
False
,
pred_bbox2d
=
True
):
"""Decode regressed 2D attributes.
Args:
bbox (torch.Tensor): Raw bounding box predictions in shape
[N, C, H, W].
scale (tuple[`Scale`]): Learnable scale parameters.
stride (int): Stride for a specific feature level.
max_regress_range (int): Maximum regression range for a specific
feature level.
training (bool): Whether the decoding is in the training
procedure.
pred_keypoints (bool, optional): Whether to predict keypoints.
Defaults to False.
pred_bbox2d (bool, optional): Whether to predict 2D bounding
boxes. Defaults to False.
Returns:
torch.Tensor: Decoded boxes.
"""
clone_bbox
=
bbox
.
clone
()
if
pred_keypoints
:
scale_kpts
=
scale
[
3
]
# 2 dimension of offsets x 8 corners of a 3D bbox
bbox
[:,
self
.
bbox_code_size
:
self
.
bbox_code_size
+
16
]
=
\
torch
.
tanh
(
scale_kpts
(
clone_bbox
[
:,
self
.
bbox_code_size
:
self
.
bbox_code_size
+
16
]).
float
())
if
pred_bbox2d
:
scale_bbox2d
=
scale
[
-
1
]
# The last four dimensions are offsets to four sides of a 2D bbox
bbox
[:,
-
4
:]
=
scale_bbox2d
(
clone_bbox
[:,
-
4
:]).
float
()
if
self
.
norm_on_bbox
:
if
pred_bbox2d
:
bbox
[:,
-
4
:]
=
F
.
relu
(
bbox
.
clone
()[:,
-
4
:])
if
not
training
:
if
pred_keypoints
:
bbox
[
:,
self
.
bbox_code_size
:
self
.
bbox_code_size
+
16
]
*=
\
max_regress_range
if
pred_bbox2d
:
bbox
[:,
-
4
:]
*=
stride
else
:
if
pred_bbox2d
:
bbox
[:,
-
4
:]
=
bbox
.
clone
()[:,
-
4
:].
exp
()
return
bbox
def
decode_prob_depth
(
self
,
depth_cls_preds
,
depth_range
,
depth_unit
,
division
,
num_depth_cls
):
"""Decode probabilistic depth map.
Args:
depth_cls_preds (torch.Tensor): Depth probabilistic map in shape
[..., self.num_depth_cls] (raw output before softmax).
depth_range (tuple[float]): Range of depth estimation.
depth_unit (int): Unit of depth range division.
division (str): Depth division method. Options include 'uniform',
'linear', 'log', 'loguniform'.
num_depth_cls (int): Number of depth classes.
Returns:
torch.Tensor: Decoded probabilistic depth estimation.
"""
if
division
==
'uniform'
:
depth_multiplier
=
depth_unit
*
\
depth_cls_preds
.
new_tensor
(
list
(
range
(
num_depth_cls
))).
reshape
([
1
,
-
1
])
prob_depth_preds
=
(
F
.
softmax
(
depth_cls_preds
.
clone
(),
dim
=-
1
)
*
depth_multiplier
).
sum
(
dim
=-
1
)
return
prob_depth_preds
elif
division
==
'linear'
:
split_pts
=
depth_cls_preds
.
new_tensor
(
list
(
range
(
num_depth_cls
))).
reshape
([
1
,
-
1
])
depth_multiplier
=
depth_range
[
0
]
+
(
depth_range
[
1
]
-
depth_range
[
0
])
/
\
(
num_depth_cls
*
(
num_depth_cls
-
1
))
*
\
(
split_pts
*
(
split_pts
+
1
))
prob_depth_preds
=
(
F
.
softmax
(
depth_cls_preds
.
clone
(),
dim
=-
1
)
*
depth_multiplier
).
sum
(
dim
=-
1
)
return
prob_depth_preds
elif
division
==
'log'
:
split_pts
=
depth_cls_preds
.
new_tensor
(
list
(
range
(
num_depth_cls
))).
reshape
([
1
,
-
1
])
start
=
max
(
depth_range
[
0
],
1
)
end
=
depth_range
[
1
]
depth_multiplier
=
(
np
.
log
(
start
)
+
split_pts
*
np
.
log
(
end
/
start
)
/
(
num_depth_cls
-
1
)).
exp
()
prob_depth_preds
=
(
F
.
softmax
(
depth_cls_preds
.
clone
(),
dim
=-
1
)
*
depth_multiplier
).
sum
(
dim
=-
1
)
return
prob_depth_preds
elif
division
==
'loguniform'
:
split_pts
=
depth_cls_preds
.
new_tensor
(
list
(
range
(
num_depth_cls
))).
reshape
([
1
,
-
1
])
start
=
max
(
depth_range
[
0
],
1
)
end
=
depth_range
[
1
]
log_multiplier
=
np
.
log
(
start
)
+
\
split_pts
*
np
.
log
(
end
/
start
)
/
(
num_depth_cls
-
1
)
prob_depth_preds
=
(
F
.
softmax
(
depth_cls_preds
.
clone
(),
dim
=-
1
)
*
log_multiplier
).
sum
(
dim
=-
1
).
exp
()
return
prob_depth_preds
else
:
raise
NotImplementedError
mmdet3d/core/bbox/coders/point_xyzwhlr_bbox_coder.py
0 → 100644
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
import
numpy
as
np
import
torch
from
mmdet.core.bbox
import
BaseBBoxCoder
from
mmdet.core.bbox.builder
import
BBOX_CODERS
@
BBOX_CODERS
.
register_module
()
class
PointXYZWHLRBBoxCoder
(
BaseBBoxCoder
):
"""Point based bbox coder for 3D boxes.
Args:
code_size (int): The dimension of boxes to be encoded.
use_mean_size (bool, optional): Whether using anchors based on class.
Defaults to True.
mean_size (list[list[float]], optional): Mean size of bboxes in
each class. Defaults to None.
"""
def
__init__
(
self
,
code_size
=
7
,
use_mean_size
=
True
,
mean_size
=
None
):
super
(
PointXYZWHLRBBoxCoder
,
self
).
__init__
()
self
.
code_size
=
code_size
self
.
use_mean_size
=
use_mean_size
if
self
.
use_mean_size
:
self
.
mean_size
=
torch
.
from_numpy
(
np
.
array
(
mean_size
)).
float
()
assert
self
.
mean_size
.
min
()
>
0
,
\
f
'The min of mean_size should > 0, however currently it is '
\
f
'
{
self
.
mean_size
.
min
()
}
, please check it in your config.'
def
encode
(
self
,
gt_bboxes_3d
,
points
,
gt_labels_3d
=
None
):
"""Encode ground truth to prediction targets.
Args:
gt_bboxes_3d (:obj:`BaseInstance3DBoxes`): Ground truth bboxes
with shape (N, 7 + C).
points (torch.Tensor): Point cloud with shape (N, 3).
gt_labels_3d (torch.Tensor, optional): Ground truth classes.
Defaults to None.
Returns:
torch.Tensor: Encoded boxes with shape (N, 8 + C).
"""
gt_bboxes_3d
[:,
3
:
6
]
=
torch
.
clamp_min
(
gt_bboxes_3d
[:,
3
:
6
],
min
=
1e-5
)
xg
,
yg
,
zg
,
dxg
,
dyg
,
dzg
,
rg
,
*
cgs
=
torch
.
split
(
gt_bboxes_3d
,
1
,
dim
=-
1
)
xa
,
ya
,
za
=
torch
.
split
(
points
,
1
,
dim
=-
1
)
if
self
.
use_mean_size
:
assert
gt_labels_3d
.
max
()
<=
self
.
mean_size
.
shape
[
0
]
-
1
,
\
f
'the max gt label
{
gt_labels_3d
.
max
()
}
is bigger than'
\
f
'anchor types
{
self
.
mean_size
.
shape
[
0
]
-
1
}
.'
self
.
mean_size
=
self
.
mean_size
.
to
(
gt_labels_3d
.
device
)
point_anchor_size
=
self
.
mean_size
[
gt_labels_3d
]
dxa
,
dya
,
dza
=
torch
.
split
(
point_anchor_size
,
1
,
dim
=-
1
)
diagonal
=
torch
.
sqrt
(
dxa
**
2
+
dya
**
2
)
xt
=
(
xg
-
xa
)
/
diagonal
yt
=
(
yg
-
ya
)
/
diagonal
zt
=
(
zg
-
za
)
/
dza
dxt
=
torch
.
log
(
dxg
/
dxa
)
dyt
=
torch
.
log
(
dyg
/
dya
)
dzt
=
torch
.
log
(
dzg
/
dza
)
else
:
xt
=
(
xg
-
xa
)
yt
=
(
yg
-
ya
)
zt
=
(
zg
-
za
)
dxt
=
torch
.
log
(
dxg
)
dyt
=
torch
.
log
(
dyg
)
dzt
=
torch
.
log
(
dzg
)
return
torch
.
cat
(
[
xt
,
yt
,
zt
,
dxt
,
dyt
,
dzt
,
torch
.
cos
(
rg
),
torch
.
sin
(
rg
),
*
cgs
],
dim
=-
1
)
def
decode
(
self
,
box_encodings
,
points
,
pred_labels_3d
=
None
):
"""Decode predicted parts and points to bbox3d.
Args:
box_encodings (torch.Tensor): Encoded boxes with shape (N, 8 + C).
points (torch.Tensor): Point cloud with shape (N, 3).
pred_labels_3d (torch.Tensor): Bbox predicted labels (N, M).
Returns:
torch.Tensor: Decoded boxes with shape (N, 7 + C)
"""
xt
,
yt
,
zt
,
dxt
,
dyt
,
dzt
,
cost
,
sint
,
*
cts
=
torch
.
split
(
box_encodings
,
1
,
dim
=-
1
)
xa
,
ya
,
za
=
torch
.
split
(
points
,
1
,
dim
=-
1
)
if
self
.
use_mean_size
:
assert
pred_labels_3d
.
max
()
<=
self
.
mean_size
.
shape
[
0
]
-
1
,
\
f
'The max pred label
{
pred_labels_3d
.
max
()
}
is bigger than'
\
f
'anchor types
{
self
.
mean_size
.
shape
[
0
]
-
1
}
.'
self
.
mean_size
=
self
.
mean_size
.
to
(
pred_labels_3d
.
device
)
point_anchor_size
=
self
.
mean_size
[
pred_labels_3d
]
dxa
,
dya
,
dza
=
torch
.
split
(
point_anchor_size
,
1
,
dim
=-
1
)
diagonal
=
torch
.
sqrt
(
dxa
**
2
+
dya
**
2
)
xg
=
xt
*
diagonal
+
xa
yg
=
yt
*
diagonal
+
ya
zg
=
zt
*
dza
+
za
dxg
=
torch
.
exp
(
dxt
)
*
dxa
dyg
=
torch
.
exp
(
dyt
)
*
dya
dzg
=
torch
.
exp
(
dzt
)
*
dza
else
:
xg
=
xt
+
xa
yg
=
yt
+
ya
zg
=
zt
+
za
dxg
,
dyg
,
dzg
=
torch
.
split
(
torch
.
exp
(
box_encodings
[...,
3
:
6
]),
1
,
dim
=-
1
)
rg
=
torch
.
atan2
(
sint
,
cost
)
return
torch
.
cat
([
xg
,
yg
,
zg
,
dxg
,
dyg
,
dzg
,
rg
,
*
cts
],
dim
=-
1
)
mmdet3d/core/bbox/coders/smoke_bbox_coder.py
0 → 100644
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
import
numpy
as
np
import
torch
from
mmdet.core.bbox
import
BaseBBoxCoder
from
mmdet.core.bbox.builder
import
BBOX_CODERS
@
BBOX_CODERS
.
register_module
()
class
SMOKECoder
(
BaseBBoxCoder
):
"""Bbox Coder for SMOKE.
Args:
base_depth (tuple[float]): Depth references for decode box depth.
base_dims (tuple[tuple[float]]): Dimension references [l, h, w]
for decode box dimension for each category.
code_size (int): The dimension of boxes to be encoded.
"""
def
__init__
(
self
,
base_depth
,
base_dims
,
code_size
):
super
(
SMOKECoder
,
self
).
__init__
()
self
.
base_depth
=
base_depth
self
.
base_dims
=
base_dims
self
.
bbox_code_size
=
code_size
def
encode
(
self
,
locations
,
dimensions
,
orientations
,
input_metas
):
"""Encode CameraInstance3DBoxes by locations, dimensions, orientations.
Args:
locations (Tensor): Center location for 3D boxes.
(N, 3)
dimensions (Tensor): Dimensions for 3D boxes.
shape (N, 3)
orientations (Tensor): Orientations for 3D boxes.
shape (N, 1)
input_metas (list[dict]): Meta information of each image, e.g.,
image size, scaling factor, etc.
Return:
:obj:`CameraInstance3DBoxes`: 3D bboxes of batch images,
shape (N, bbox_code_size).
"""
bboxes
=
torch
.
cat
((
locations
,
dimensions
,
orientations
),
dim
=
1
)
assert
bboxes
.
shape
[
1
]
==
self
.
bbox_code_size
,
'bboxes shape dose not'
\
'match the bbox_code_size.'
batch_bboxes
=
input_metas
[
0
][
'box_type_3d'
](
bboxes
,
box_dim
=
self
.
bbox_code_size
)
return
batch_bboxes
def
decode
(
self
,
reg
,
points
,
labels
,
cam2imgs
,
trans_mats
,
locations
=
None
):
"""Decode regression into locations, dimensions, orientations.
Args:
reg (Tensor): Batch regression for each predict center2d point.
shape: (batch * K (max_objs), C)
points(Tensor): Batch projected bbox centers on image plane.
shape: (batch * K (max_objs) , 2)
labels (Tensor): Batch predict class label for each predict
center2d point.
shape: (batch, K (max_objs))
cam2imgs (Tensor): Batch images' camera intrinsic matrix.
shape: kitti (batch, 4, 4) nuscenes (batch, 3, 3)
trans_mats (Tensor): transformation matrix from original image
to feature map.
shape: (batch, 3, 3)
locations (None | Tensor): if locations is None, this function
is used to decode while inference, otherwise, it's used while
training using the ground truth 3d bbox locations.
shape: (batch * K (max_objs), 3)
Return:
tuple(Tensor): The tuple has components below:
- locations (Tensor): Centers of 3D boxes.
shape: (batch * K (max_objs), 3)
- dimensions (Tensor): Dimensions of 3D boxes.
shape: (batch * K (max_objs), 3)
- orientations (Tensor): Orientations of 3D
boxes.
shape: (batch * K (max_objs), 1)
"""
depth_offsets
=
reg
[:,
0
]
centers2d_offsets
=
reg
[:,
1
:
3
]
dimensions_offsets
=
reg
[:,
3
:
6
]
orientations
=
reg
[:,
6
:
8
]
depths
=
self
.
_decode_depth
(
depth_offsets
)
# get the 3D Bounding box's center location.
pred_locations
=
self
.
_decode_location
(
points
,
centers2d_offsets
,
depths
,
cam2imgs
,
trans_mats
)
pred_dimensions
=
self
.
_decode_dimension
(
labels
,
dimensions_offsets
)
if
locations
is
None
:
pred_orientations
=
self
.
_decode_orientation
(
orientations
,
pred_locations
)
else
:
pred_orientations
=
self
.
_decode_orientation
(
orientations
,
locations
)
return
pred_locations
,
pred_dimensions
,
pred_orientations
def
_decode_depth
(
self
,
depth_offsets
):
"""Transform depth offset to depth."""
base_depth
=
depth_offsets
.
new_tensor
(
self
.
base_depth
)
depths
=
depth_offsets
*
base_depth
[
1
]
+
base_depth
[
0
]
return
depths
def
_decode_location
(
self
,
points
,
centers2d_offsets
,
depths
,
cam2imgs
,
trans_mats
):
"""Retrieve objects location in camera coordinate based on projected
points.
Args:
points (Tensor): Projected points on feature map in (x, y)
shape: (batch * K, 2)
centers2d_offset (Tensor): Project points offset in
(delta_x, delta_y). shape: (batch * K, 2)
depths (Tensor): Object depth z.
shape: (batch * K)
cam2imgs (Tensor): Batch camera intrinsics matrix.
shape: kitti (batch, 4, 4) nuscenes (batch, 3, 3)
trans_mats (Tensor): transformation matrix from original image
to feature map.
shape: (batch, 3, 3)
"""
# number of points
N
=
centers2d_offsets
.
shape
[
0
]
# batch_size
N_batch
=
cam2imgs
.
shape
[
0
]
batch_id
=
torch
.
arange
(
N_batch
).
unsqueeze
(
1
)
obj_id
=
batch_id
.
repeat
(
1
,
N
//
N_batch
).
flatten
()
trans_mats_inv
=
trans_mats
.
inverse
()[
obj_id
]
cam2imgs_inv
=
cam2imgs
.
inverse
()[
obj_id
]
centers2d
=
points
+
centers2d_offsets
centers2d_extend
=
torch
.
cat
((
centers2d
,
centers2d
.
new_ones
(
N
,
1
)),
dim
=
1
)
# expand project points as [N, 3, 1]
centers2d_extend
=
centers2d_extend
.
unsqueeze
(
-
1
)
# transform project points back on original image
centers2d_img
=
torch
.
matmul
(
trans_mats_inv
,
centers2d_extend
)
centers2d_img
=
centers2d_img
*
depths
.
view
(
N
,
-
1
,
1
)
if
cam2imgs
.
shape
[
1
]
==
4
:
centers2d_img
=
torch
.
cat
(
(
centers2d_img
,
centers2d
.
new_ones
(
N
,
1
,
1
)),
dim
=
1
)
locations
=
torch
.
matmul
(
cam2imgs_inv
,
centers2d_img
).
squeeze
(
2
)
return
locations
[:,
:
3
]
def
_decode_dimension
(
self
,
labels
,
dims_offset
):
"""Transform dimension offsets to dimension according to its category.
Args:
labels (Tensor): Each points' category id.
shape: (N, K)
dims_offset (Tensor): Dimension offsets.
shape: (N, 3)
"""
labels
=
labels
.
flatten
().
long
()
base_dims
=
dims_offset
.
new_tensor
(
self
.
base_dims
)
dims_select
=
base_dims
[
labels
,
:]
dimensions
=
dims_offset
.
exp
()
*
dims_select
return
dimensions
def
_decode_orientation
(
self
,
ori_vector
,
locations
):
"""Retrieve object orientation.
Args:
ori_vector (Tensor): Local orientation in [sin, cos] format.
shape: (N, 2)
locations (Tensor): Object location.
shape: (N, 3)
Return:
Tensor: yaw(Orientation). Notice that the yaw's
range is [-np.pi, np.pi].
shape:(N, 1)
"""
assert
len
(
ori_vector
)
==
len
(
locations
)
locations
=
locations
.
view
(
-
1
,
3
)
rays
=
torch
.
atan
(
locations
[:,
0
]
/
(
locations
[:,
2
]
+
1e-7
))
alphas
=
torch
.
atan
(
ori_vector
[:,
0
]
/
(
ori_vector
[:,
1
]
+
1e-7
))
# get cosine value positive and negative index.
cos_pos_inds
=
(
ori_vector
[:,
1
]
>=
0
).
nonzero
(
as_tuple
=
False
)
cos_neg_inds
=
(
ori_vector
[:,
1
]
<
0
).
nonzero
(
as_tuple
=
False
)
alphas
[
cos_pos_inds
]
-=
np
.
pi
/
2
alphas
[
cos_neg_inds
]
+=
np
.
pi
/
2
# retrieve object rotation y angle.
yaws
=
alphas
+
rays
larger_inds
=
(
yaws
>
np
.
pi
).
nonzero
(
as_tuple
=
False
)
small_inds
=
(
yaws
<
-
np
.
pi
).
nonzero
(
as_tuple
=
False
)
if
len
(
larger_inds
)
!=
0
:
yaws
[
larger_inds
]
-=
2
*
np
.
pi
if
len
(
small_inds
)
!=
0
:
yaws
[
small_inds
]
+=
2
*
np
.
pi
yaws
=
yaws
.
unsqueeze
(
-
1
)
return
yaws
mmdet3d/core/bbox/iou_calculators/iou3d_calculator.py
View file @
32a4328b
...
@@ -31,15 +31,17 @@ class BboxOverlapsNearest3D(object):
...
@@ -31,15 +31,17 @@ class BboxOverlapsNearest3D(object):
between each aligned pair of bboxes1 and bboxes2.
between each aligned pair of bboxes1 and bboxes2.
Args:
Args:
bboxes1 (torch.Tensor): shape (N, 7+N) [x, y, z, h, w, l, ry, v].
bboxes1 (torch.Tensor): shape (N, 7+N)
bboxes2 (torch.Tensor): shape (M, 7+N) [x, y, z, h, w, l, ry, v].
[x, y, z, x_size, y_size, z_size, ry, v].
bboxes2 (torch.Tensor): shape (M, 7+N)
[x, y, z, x_size, y_size, z_size, ry, v].
mode (str): "iou" (intersection over union) or iof
mode (str): "iou" (intersection over union) or iof
(intersection over foreground).
(intersection over foreground).
is_aligned (bool): Whether the calculation is aligned.
is_aligned (bool): Whether the calculation is aligned.
Return:
Return:
torch.Tensor: If ``is_aligned`` is ``True``, return ious between
\
torch.Tensor: If ``is_aligned`` is ``True``, return ious between
bboxes1 and bboxes2 with shape (M, N). If ``is_aligned`` is
\
bboxes1 and bboxes2 with shape (M, N). If ``is_aligned`` is
``False``, return shape is M.
``False``, return shape is M.
"""
"""
return
bbox_overlaps_nearest_3d
(
bboxes1
,
bboxes2
,
mode
,
is_aligned
,
return
bbox_overlaps_nearest_3d
(
bboxes1
,
bboxes2
,
mode
,
is_aligned
,
...
@@ -74,13 +76,15 @@ class BboxOverlaps3D(object):
...
@@ -74,13 +76,15 @@ class BboxOverlaps3D(object):
calculate the actual 3D IoUs of boxes.
calculate the actual 3D IoUs of boxes.
Args:
Args:
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z, h, w, l, ry].
bboxes1 (torch.Tensor): with shape (N, 7+C),
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z, h, w, l, ry].
(x, y, z, x_size, y_size, z_size, ry, v*).
bboxes2 (torch.Tensor): with shape (M, 7+C),
(x, y, z, x_size, y_size, z_size, ry, v*).
mode (str): "iou" (intersection over union) or
mode (str): "iou" (intersection over union) or
iof (intersection over foreground).
iof (intersection over foreground).
Return:
Return:
torch.Tensor: Bbox overlaps results of bboxes1 and bboxes2
\
torch.Tensor: Bbox overlaps results of bboxes1 and bboxes2
with shape (M, N) (aligned mode is not supported currently).
with shape (M, N) (aligned mode is not supported currently).
"""
"""
return
bbox_overlaps_3d
(
bboxes1
,
bboxes2
,
mode
,
self
.
coordinate
)
return
bbox_overlaps_3d
(
bboxes1
,
bboxes2
,
mode
,
self
.
coordinate
)
...
@@ -102,7 +106,7 @@ def bbox_overlaps_nearest_3d(bboxes1,
...
@@ -102,7 +106,7 @@ def bbox_overlaps_nearest_3d(bboxes1,
Note:
Note:
This function first finds the nearest 2D boxes in bird eye view
This function first finds the nearest 2D boxes in bird eye view
(BEV), and then calculates the 2D IoU using :meth:`bbox_overlaps`.
(BEV), and then calculates the 2D IoU using :meth:`bbox_overlaps`.
Ths IoU calculator :class:`BboxOverlapsNearest3D` uses this
Th
i
s IoU calculator :class:`BboxOverlapsNearest3D` uses this
function to calculate IoUs of boxes.
function to calculate IoUs of boxes.
If ``is_aligned`` is ``False``, then it calculates the ious between
If ``is_aligned`` is ``False``, then it calculates the ious between
...
@@ -110,15 +114,17 @@ def bbox_overlaps_nearest_3d(bboxes1,
...
@@ -110,15 +114,17 @@ def bbox_overlaps_nearest_3d(bboxes1,
aligned pair of bboxes1 and bboxes2.
aligned pair of bboxes1 and bboxes2.
Args:
Args:
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z, h, w, l, ry, v].
bboxes1 (torch.Tensor): with shape (N, 7+C),
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z, h, w, l, ry, v].
(x, y, z, x_size, y_size, z_size, ry, v*).
bboxes2 (torch.Tensor): with shape (M, 7+C),
(x, y, z, x_size, y_size, z_size, ry, v*).
mode (str): "iou" (intersection over union) or iof
mode (str): "iou" (intersection over union) or iof
(intersection over foreground).
(intersection over foreground).
is_aligned (bool): Whether the calculation is aligned
is_aligned (bool): Whether the calculation is aligned
Return:
Return:
torch.Tensor: If ``is_aligned`` is ``True``, return ious between
\
torch.Tensor: If ``is_aligned`` is ``True``, return ious between
bboxes1 and bboxes2 with shape (M, N). If ``is_aligned`` is
\
bboxes1 and bboxes2 with shape (M, N). If ``is_aligned`` is
``False``, return shape is M.
``False``, return shape is M.
"""
"""
assert
bboxes1
.
size
(
-
1
)
==
bboxes2
.
size
(
-
1
)
>=
7
assert
bboxes1
.
size
(
-
1
)
==
bboxes2
.
size
(
-
1
)
>=
7
...
@@ -148,14 +154,16 @@ def bbox_overlaps_3d(bboxes1, bboxes2, mode='iou', coordinate='camera'):
...
@@ -148,14 +154,16 @@ def bbox_overlaps_3d(bboxes1, bboxes2, mode='iou', coordinate='camera'):
calculate the actual IoUs of boxes.
calculate the actual IoUs of boxes.
Args:
Args:
bboxes1 (torch.Tensor): shape (N, 7+C) [x, y, z, h, w, l, ry].
bboxes1 (torch.Tensor): with shape (N, 7+C),
bboxes2 (torch.Tensor): shape (M, 7+C) [x, y, z, h, w, l, ry].
(x, y, z, x_size, y_size, z_size, ry, v*).
bboxes2 (torch.Tensor): with shape (M, 7+C),
(x, y, z, x_size, y_size, z_size, ry, v*).
mode (str): "iou" (intersection over union) or
mode (str): "iou" (intersection over union) or
iof (intersection over foreground).
iof (intersection over foreground).
coordinate (str): 'camera' or 'lidar' coordinate system.
coordinate (str): 'camera' or 'lidar' coordinate system.
Return:
Return:
torch.Tensor: Bbox overlaps results of bboxes1 and bboxes2
\
torch.Tensor: Bbox overlaps results of bboxes1 and bboxes2
with shape (M, N) (aligned mode is not supported currently).
with shape (M, N) (aligned mode is not supported currently).
"""
"""
assert
bboxes1
.
size
(
-
1
)
==
bboxes2
.
size
(
-
1
)
>=
7
assert
bboxes1
.
size
(
-
1
)
==
bboxes2
.
size
(
-
1
)
>=
7
...
@@ -185,7 +193,7 @@ class AxisAlignedBboxOverlaps3D(object):
...
@@ -185,7 +193,7 @@ class AxisAlignedBboxOverlaps3D(object):
mode (str): "iou" (intersection over union) or "giou" (generalized
mode (str): "iou" (intersection over union) or "giou" (generalized
intersection over union).
intersection over union).
is_aligned (bool, optional): If True, then m and n must be equal.
is_aligned (bool, optional): If True, then m and n must be equal.
Default False.
Default
s to
False.
Returns:
Returns:
Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,)
Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,)
"""
"""
...
@@ -219,9 +227,9 @@ def axis_aligned_bbox_overlaps_3d(bboxes1,
...
@@ -219,9 +227,9 @@ def axis_aligned_bbox_overlaps_3d(bboxes1,
mode (str): "iou" (intersection over union) or "giou" (generalized
mode (str): "iou" (intersection over union) or "giou" (generalized
intersection over union).
intersection over union).
is_aligned (bool, optional): If True, then m and n must be equal.
is_aligned (bool, optional): If True, then m and n must be equal.
Default False.
Default
s to
False.
eps (float, optional): A value added to the denominator for numerical
eps (float, optional): A value added to the denominator for numerical
stability. Default 1e-6.
stability. Default
s to
1e-6.
Returns:
Returns:
Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,)
Tensor: shape (m, n) if ``is_aligned`` is False else shape (m,)
...
@@ -250,7 +258,7 @@ def axis_aligned_bbox_overlaps_3d(bboxes1,
...
@@ -250,7 +258,7 @@ def axis_aligned_bbox_overlaps_3d(bboxes1,
"""
"""
assert
mode
in
[
'iou'
,
'giou'
],
f
'Unsupported mode
{
mode
}
'
assert
mode
in
[
'iou'
,
'giou'
],
f
'Unsupported mode
{
mode
}
'
# Either the boxes are empty or the length of boxes's last dimens
t
ion is 6
# Either the boxes are empty or the length of boxes's last dimension is 6
assert
(
bboxes1
.
size
(
-
1
)
==
6
or
bboxes1
.
size
(
0
)
==
0
)
assert
(
bboxes1
.
size
(
-
1
)
==
6
or
bboxes1
.
size
(
0
)
==
0
)
assert
(
bboxes2
.
size
(
-
1
)
==
6
or
bboxes2
.
size
(
0
)
==
0
)
assert
(
bboxes2
.
size
(
-
1
)
==
6
or
bboxes2
.
size
(
0
)
==
0
)
...
...
mmdet3d/core/bbox/samplers/iou_neg_piecewise_sampler.py
View file @
32a4328b
...
@@ -9,8 +9,8 @@ from . import RandomSampler, SamplingResult
...
@@ -9,8 +9,8 @@ from . import RandomSampler, SamplingResult
class
IoUNegPiecewiseSampler
(
RandomSampler
):
class
IoUNegPiecewiseSampler
(
RandomSampler
):
"""IoU Piece-wise Sampling.
"""IoU Piece-wise Sampling.
Sampling negtive proposals according to a list of IoU thresholds.
Sampling neg
a
tive proposals according to a list of IoU thresholds.
The negtive proposals are divided into several pieces according
The neg
a
tive proposals are divided into several pieces according
to `neg_iou_piece_thrs`. And the ratio of each piece is indicated
to `neg_iou_piece_thrs`. And the ratio of each piece is indicated
by `neg_piece_fractions`.
by `neg_piece_fractions`.
...
@@ -18,11 +18,11 @@ class IoUNegPiecewiseSampler(RandomSampler):
...
@@ -18,11 +18,11 @@ class IoUNegPiecewiseSampler(RandomSampler):
num (int): Number of proposals.
num (int): Number of proposals.
pos_fraction (float): The fraction of positive proposals.
pos_fraction (float): The fraction of positive proposals.
neg_piece_fractions (list): A list contains fractions that indicates
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
neg_iou_piece_thrs (list): A list contains IoU thresholds that
indicate the upper bound of this piece.
indicate the upper bound of this piece.
neg_pos_ub (float): The total ratio to limit the upper bound
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.
add_gt_as_proposals (bool): Whether to add gt as proposals.
"""
"""
...
@@ -59,8 +59,8 @@ class IoUNegPiecewiseSampler(RandomSampler):
...
@@ -59,8 +59,8 @@ class IoUNegPiecewiseSampler(RandomSampler):
neg_inds
=
torch
.
nonzero
(
assign_result
.
gt_inds
==
0
,
as_tuple
=
False
)
neg_inds
=
torch
.
nonzero
(
assign_result
.
gt_inds
==
0
,
as_tuple
=
False
)
if
neg_inds
.
numel
()
!=
0
:
if
neg_inds
.
numel
()
!=
0
:
neg_inds
=
neg_inds
.
squeeze
(
1
)
neg_inds
=
neg_inds
.
squeeze
(
1
)
if
len
(
neg_inds
)
<=
num_expected
:
if
len
(
neg_inds
)
<=
0
:
return
neg_inds
return
neg_inds
.
squeeze
(
1
)
else
:
else
:
neg_inds_choice
=
neg_inds
.
new_zeros
([
0
])
neg_inds_choice
=
neg_inds
.
new_zeros
([
0
])
extend_num
=
0
extend_num
=
0
...
@@ -88,12 +88,38 @@ class IoUNegPiecewiseSampler(RandomSampler):
...
@@ -88,12 +88,38 @@ class IoUNegPiecewiseSampler(RandomSampler):
neg_inds_choice
=
torch
.
cat
(
neg_inds_choice
=
torch
.
cat
(
[
neg_inds_choice
,
neg_inds
[
piece_neg_inds
]],
dim
=
0
)
[
neg_inds_choice
,
neg_inds
[
piece_neg_inds
]],
dim
=
0
)
extend_num
+=
piece_expected_num
-
len
(
piece_neg_inds
)
extend_num
+=
piece_expected_num
-
len
(
piece_neg_inds
)
# for the last piece
if
piece_inds
==
self
.
neg_piece_num
-
1
:
extend_neg_num
=
num_expected
-
len
(
neg_inds_choice
)
# if the numbers of nagetive samples > 0, we will
# randomly select num_expected samples in last piece
if
piece_neg_inds
.
numel
()
>
0
:
rand_idx
=
torch
.
randint
(
low
=
0
,
high
=
piece_neg_inds
.
numel
(),
size
=
(
extend_neg_num
,
)).
long
()
neg_inds_choice
=
torch
.
cat
(
[
neg_inds_choice
,
piece_neg_inds
[
rand_idx
]],
dim
=
0
)
# if the numbers of nagetive samples == 0, we will
# randomly select num_expected samples in all
# previous pieces
else
:
rand_idx
=
torch
.
randint
(
low
=
0
,
high
=
neg_inds_choice
.
numel
(),
size
=
(
extend_neg_num
,
)).
long
()
neg_inds_choice
=
torch
.
cat
(
[
neg_inds_choice
,
neg_inds_choice
[
rand_idx
]],
dim
=
0
)
else
:
else
:
piece_choice
=
self
.
random_choice
(
piece_neg_inds
,
piece_choice
=
self
.
random_choice
(
piece_neg_inds
,
piece_expected_num
)
piece_expected_num
)
neg_inds_choice
=
torch
.
cat
(
neg_inds_choice
=
torch
.
cat
(
[
neg_inds_choice
,
neg_inds
[
piece_choice
]],
dim
=
0
)
[
neg_inds_choice
,
neg_inds
[
piece_choice
]],
dim
=
0
)
extend_num
=
0
extend_num
=
0
assert
len
(
neg_inds_choice
)
==
num_expected
return
neg_inds_choice
return
neg_inds_choice
def
sample
(
self
,
def
sample
(
self
,
...
@@ -111,7 +137,7 @@ class IoUNegPiecewiseSampler(RandomSampler):
...
@@ -111,7 +137,7 @@ class IoUNegPiecewiseSampler(RandomSampler):
assign_result (:obj:`AssignResult`): Bbox assigning results.
assign_result (:obj:`AssignResult`): Bbox assigning results.
bboxes (torch.Tensor): Boxes to be sampled from.
bboxes (torch.Tensor): Boxes to be sampled from.
gt_bboxes (torch.Tensor): Ground truth bboxes.
gt_bboxes (torch.Tensor): Ground truth bboxes.
gt_labels (torch.Tensor, optional): Class labels of ground truth
\
gt_labels (torch.Tensor, optional): Class labels of ground truth
bboxes.
bboxes.
Returns:
Returns:
...
@@ -145,7 +171,6 @@ class IoUNegPiecewiseSampler(RandomSampler):
...
@@ -145,7 +171,6 @@ class IoUNegPiecewiseSampler(RandomSampler):
num_expected_neg
=
neg_upper_bound
num_expected_neg
=
neg_upper_bound
neg_inds
=
self
.
neg_sampler
.
_sample_neg
(
neg_inds
=
self
.
neg_sampler
.
_sample_neg
(
assign_result
,
num_expected_neg
,
bboxes
=
bboxes
,
**
kwargs
)
assign_result
,
num_expected_neg
,
bboxes
=
bboxes
,
**
kwargs
)
neg_inds
=
neg_inds
.
unique
()
sampling_result
=
SamplingResult
(
pos_inds
,
neg_inds
,
bboxes
,
gt_bboxes
,
sampling_result
=
SamplingResult
(
pos_inds
,
neg_inds
,
bboxes
,
gt_bboxes
,
assign_result
,
gt_flags
)
assign_result
,
gt_flags
)
...
...
mmdet3d/core/bbox/structures/__init__.py
View file @
32a4328b
...
@@ -6,12 +6,13 @@ from .coord_3d_mode import Coord3DMode
...
@@ -6,12 +6,13 @@ from .coord_3d_mode import Coord3DMode
from
.depth_box3d
import
DepthInstance3DBoxes
from
.depth_box3d
import
DepthInstance3DBoxes
from
.lidar_box3d
import
LiDARInstance3DBoxes
from
.lidar_box3d
import
LiDARInstance3DBoxes
from
.utils
import
(
get_box_type
,
get_proj_mat_by_coord_type
,
limit_period
,
from
.utils
import
(
get_box_type
,
get_proj_mat_by_coord_type
,
limit_period
,
mono_cam_box2vis
,
points_cam2img
,
rotation_3d_in_axis
,
mono_cam_box2vis
,
points_cam2img
,
points_img2cam
,
xywhr2xyxyr
)
rotation_3d_in_axis
,
xywhr2xyxyr
)
__all__
=
[
__all__
=
[
'Box3DMode'
,
'BaseInstance3DBoxes'
,
'LiDARInstance3DBoxes'
,
'Box3DMode'
,
'BaseInstance3DBoxes'
,
'LiDARInstance3DBoxes'
,
'CameraInstance3DBoxes'
,
'DepthInstance3DBoxes'
,
'xywhr2xyxyr'
,
'CameraInstance3DBoxes'
,
'DepthInstance3DBoxes'
,
'xywhr2xyxyr'
,
'get_box_type'
,
'rotation_3d_in_axis'
,
'limit_period'
,
'points_cam2img'
,
'get_box_type'
,
'rotation_3d_in_axis'
,
'limit_period'
,
'points_cam2img'
,
'Coord3DMode'
,
'mono_cam_box2vis'
,
'get_proj_mat_by_coord_type'
'points_img2cam'
,
'Coord3DMode'
,
'mono_cam_box2vis'
,
'get_proj_mat_by_coord_type'
]
]
mmdet3d/core/bbox/structures/base_box3d.py
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved.
import
warnings
from
abc
import
abstractmethod
import
numpy
as
np
import
numpy
as
np
import
torch
import
torch
from
abc
import
abstractmethod
from
mmdet3d.ops
import
points_in_boxes_all
,
points_in_boxes_part
from
mmdet3d.ops.iou3d
import
iou3d_cuda
from
mmdet3d.ops.iou3d
import
iou3d_cuda
from
.utils
import
limit_period
,
xywhr2xyxyr
from
.utils
import
limit_period
,
xywhr2xyxyr
...
@@ -18,12 +21,12 @@ class BaseInstance3DBoxes(object):
...
@@ -18,12 +21,12 @@ class BaseInstance3DBoxes(object):
tensor (torch.Tensor | np.ndarray | list): a N x box_dim matrix.
tensor (torch.Tensor | np.ndarray | list): a N x box_dim matrix.
box_dim (int): Number of the dimension of a box.
box_dim (int): Number of the dimension of a box.
Each row is (x, y, z, x_size, y_size, z_size, yaw).
Each row is (x, y, z, x_size, y_size, z_size, yaw).
Default to 7.
Default
s
to 7.
with_yaw (bool): Whether the box is with yaw rotation.
with_yaw (bool): Whether the box is with yaw rotation.
If False, the value of yaw will be set to 0 as minmax boxes.
If False, the value of yaw will be set to 0 as minmax boxes.
Default to True.
Default
s
to True.
origin (tuple[float]
): The r
elative position of
origin in
the box.
origin (tuple[float]
, optional): R
elative position of the box
origin
.
Default to (0.5, 0.5, 0). This will guide the box be converted to
Default
s
to (0.5, 0.5, 0). This will guide the box be converted to
(0.5, 0.5, 0) mode.
(0.5, 0.5, 0) mode.
Attributes:
Attributes:
...
@@ -72,27 +75,29 @@ class BaseInstance3DBoxes(object):
...
@@ -72,27 +75,29 @@ class BaseInstance3DBoxes(object):
@
property
@
property
def
dims
(
self
):
def
dims
(
self
):
"""torch.Tensor:
Corner
s of each box
with siz
e (N,
8,
3)."""
"""torch.Tensor:
Size dimension
s of each box
in shap
e (N, 3)."""
return
self
.
tensor
[:,
3
:
6
]
return
self
.
tensor
[:,
3
:
6
]
@
property
@
property
def
yaw
(
self
):
def
yaw
(
self
):
"""torch.Tensor: A vector with yaw of each box."""
"""torch.Tensor: A vector with yaw of each box
in shape (N, )
."""
return
self
.
tensor
[:,
6
]
return
self
.
tensor
[:,
6
]
@
property
@
property
def
height
(
self
):
def
height
(
self
):
"""torch.Tensor: A vector with height of each box."""
"""torch.Tensor: A vector with height of each box
in shape (N, )
."""
return
self
.
tensor
[:,
5
]
return
self
.
tensor
[:,
5
]
@
property
@
property
def
top_height
(
self
):
def
top_height
(
self
):
"""torch.Tensor: A vector with the top height of each box."""
"""torch.Tensor:
A vector with the top height of each box in shape (N, )."""
return
self
.
bottom_height
+
self
.
height
return
self
.
bottom_height
+
self
.
height
@
property
@
property
def
bottom_height
(
self
):
def
bottom_height
(
self
):
"""torch.Tensor: A vector with bottom's height of each box."""
"""torch.Tensor:
A vector with bottom's height of each box in shape (N, )."""
return
self
.
tensor
[:,
2
]
return
self
.
tensor
[:,
2
]
@
property
@
property
...
@@ -100,58 +105,114 @@ class BaseInstance3DBoxes(object):
...
@@ -100,58 +105,114 @@ class BaseInstance3DBoxes(object):
"""Calculate the center of all the boxes.
"""Calculate the center of all the boxes.
Note:
Note:
In
the
MMDetection3D's convention, the bottom center is
In MMDetection3D's convention, the bottom center is
usually taken as the default center.
usually taken as the default center.
The relative position of the centers in different kinds of
The relative position of the centers in different kinds of
boxes are different, e.g., the relative center of a boxes is
boxes are different, e.g., the relative center of a boxes is
(0.5, 1.0, 0.5) in camera and (0.5, 0.5, 0) in lidar.
(0.5, 1.0, 0.5) in camera and (0.5, 0.5, 0) in lidar.
It is recommended to use ``bottom_center`` or ``gravity_center``
It is recommended to use ``bottom_center`` or ``gravity_center``
for
more
clear usage.
for clear
er
usage.
Returns:
Returns:
torch.Tensor: A tensor with center of each box.
torch.Tensor: A tensor with center of each box
in shape (N, 3)
.
"""
"""
return
self
.
bottom_center
return
self
.
bottom_center
@
property
@
property
def
bottom_center
(
self
):
def
bottom_center
(
self
):
"""torch.Tensor: A tensor with center of each box."""
"""torch.Tensor: A tensor with center of each box
in shape (N, 3)
."""
return
self
.
tensor
[:,
:
3
]
return
self
.
tensor
[:,
:
3
]
@
property
@
property
def
gravity_center
(
self
):
def
gravity_center
(
self
):
"""torch.Tensor: A tensor with center of each box."""
"""torch.Tensor: A tensor with center of each box
in shape (N, 3)
."""
pass
pass
@
property
@
property
def
corners
(
self
):
def
corners
(
self
):
"""torch.Tensor: a tensor with 8 corners of each box."""
"""torch.Tensor:
a tensor with 8 corners of each box in shape (N, 8, 3)."""
pass
pass
@
property
def
bev
(
self
):
"""torch.Tensor: 2D BEV box of each box with rotation
in XYWHR format, in shape (N, 5)."""
return
self
.
tensor
[:,
[
0
,
1
,
3
,
4
,
6
]]
@
property
def
nearest_bev
(
self
):
"""torch.Tensor: A tensor of 2D BEV box of each box
without rotation."""
# Obtain BEV boxes with rotation in XYWHR format
bev_rotated_boxes
=
self
.
bev
# convert the rotation to a valid range
rotations
=
bev_rotated_boxes
[:,
-
1
]
normed_rotations
=
torch
.
abs
(
limit_period
(
rotations
,
0.5
,
np
.
pi
))
# find the center of boxes
conditions
=
(
normed_rotations
>
np
.
pi
/
4
)[...,
None
]
bboxes_xywh
=
torch
.
where
(
conditions
,
bev_rotated_boxes
[:,
[
0
,
1
,
3
,
2
]],
bev_rotated_boxes
[:,
:
4
])
centers
=
bboxes_xywh
[:,
:
2
]
dims
=
bboxes_xywh
[:,
2
:]
bev_boxes
=
torch
.
cat
([
centers
-
dims
/
2
,
centers
+
dims
/
2
],
dim
=-
1
)
return
bev_boxes
def
in_range_bev
(
self
,
box_range
):
"""Check whether the boxes are in the given range.
Args:
box_range (list | torch.Tensor): the range of box
(x_min, y_min, x_max, y_max)
Note:
The original implementation of SECOND checks whether boxes in
a range by checking whether the points are in a convex
polygon, we reduce the burden for simpler cases.
Returns:
torch.Tensor: Whether each box is inside the reference range.
"""
in_range_flags
=
((
self
.
bev
[:,
0
]
>
box_range
[
0
])
&
(
self
.
bev
[:,
1
]
>
box_range
[
1
])
&
(
self
.
bev
[:,
0
]
<
box_range
[
2
])
&
(
self
.
bev
[:,
1
]
<
box_range
[
3
]))
return
in_range_flags
@
abstractmethod
@
abstractmethod
def
rotate
(
self
,
angle
,
points
=
None
):
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
"""Rotate boxes with points (optional) with the given angle or
rotation
rotation
matrix.
matrix.
Args:
Args:
angle (float | torch.Tensor | np.ndarray):
angle (float | torch.Tensor | np.ndarray):
Rotation angle or rotation matrix.
Rotation angle or rotation matrix.
points (torch.Tensor, numpy.ndarray, :obj:`BasePoints`, optional):
points (torch.Tensor | numpy.ndarray |
:obj:`BasePoints`, optional):
Points to rotate. Defaults to None.
Points to rotate. Defaults to None.
"""
"""
pass
pass
@
abstractmethod
@
abstractmethod
def
flip
(
self
,
bev_direction
=
'horizontal'
):
def
flip
(
self
,
bev_direction
=
'horizontal'
):
"""Flip the boxes in BEV along given BEV direction."""
"""Flip the boxes in BEV along given BEV direction.
Args:
bev_direction (str, optional): Direction by which to flip.
Can be chosen from 'horizontal' and 'vertical'.
Defaults to 'horizontal'.
"""
pass
pass
def
translate
(
self
,
trans_vector
):
def
translate
(
self
,
trans_vector
):
"""Translate boxes with the given translation vector.
"""Translate boxes with the given translation vector.
Args:
Args:
trans_vector (torch.Tensor): Translation vector of size
1x3
.
trans_vector (torch.Tensor): Translation vector of size
(1, 3)
.
"""
"""
if
not
isinstance
(
trans_vector
,
torch
.
Tensor
):
if
not
isinstance
(
trans_vector
,
torch
.
Tensor
):
trans_vector
=
self
.
tensor
.
new_tensor
(
trans_vector
)
trans_vector
=
self
.
tensor
.
new_tensor
(
trans_vector
)
...
@@ -170,7 +231,7 @@ class BaseInstance3DBoxes(object):
...
@@ -170,7 +231,7 @@ class BaseInstance3DBoxes(object):
polygon, we try to reduce the burden for simpler cases.
polygon, we try to reduce the burden for simpler cases.
Returns:
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.
inside the reference range.
"""
"""
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
...
@@ -181,34 +242,21 @@ class BaseInstance3DBoxes(object):
...
@@ -181,34 +242,21 @@ class BaseInstance3DBoxes(object):
&
(
self
.
tensor
[:,
2
]
<
box_range
[
5
]))
&
(
self
.
tensor
[:,
2
]
<
box_range
[
5
]))
return
in_range_flags
return
in_range_flags
@
abstractmethod
def
in_range_bev
(
self
,
box_range
):
"""Check whether the boxes are in the given range.
Args:
box_range (list | torch.Tensor): The range of box
in order of (x_min, y_min, x_max, y_max).
Returns:
torch.Tensor: Indicating whether each box is inside
\
the reference range.
"""
pass
@
abstractmethod
@
abstractmethod
def
convert_to
(
self
,
dst
,
rt_mat
=
None
):
def
convert_to
(
self
,
dst
,
rt_mat
=
None
):
"""Convert self to ``dst`` mode.
"""Convert self to ``dst`` mode.
Args:
Args:
dst (:obj:`Box3DMode`): The target Box mode.
dst (:obj:`Box3DMode`): The target Box mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
rt_mat (np.ndarray | torch.Tensor, optional): The rotation and
matrix between different coordinates. Defaults to None.
translation matrix between different coordinates.
Defaults to None.
The conversion from `src` coordinates to `dst` coordinates
The conversion from `src` coordinates to `dst` coordinates
usually comes along the change of sensors, e.g., from camera
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
to LiDAR. This requires a transformation matrix.
Returns:
Returns:
:obj:`BaseInstance3DBoxes`: The converted box of the same type
\
:obj:`BaseInstance3DBoxes`: The converted box of the same type
in the `dst` mode.
in the `dst` mode.
"""
"""
pass
pass
...
@@ -220,28 +268,29 @@ class BaseInstance3DBoxes(object):
...
@@ -220,28 +268,29 @@ class BaseInstance3DBoxes(object):
scale_factors (float): Scale factors to scale the boxes.
scale_factors (float): Scale factors to scale the boxes.
"""
"""
self
.
tensor
[:,
:
6
]
*=
scale_factor
self
.
tensor
[:,
:
6
]
*=
scale_factor
self
.
tensor
[:,
7
:]
*=
scale_factor
self
.
tensor
[:,
7
:]
*=
scale_factor
# velocity
def
limit_yaw
(
self
,
offset
=
0.5
,
period
=
np
.
pi
):
def
limit_yaw
(
self
,
offset
=
0.5
,
period
=
np
.
pi
):
"""Limit the yaw to a given period and offset.
"""Limit the yaw to a given period and offset.
Args:
Args:
offset (float): The offset of the yaw.
offset (float
, optional
): The offset of the yaw.
Defaults to 0.5.
period (float): The expected period.
period (float
, optional
): The expected period.
Defaults to np.pi.
"""
"""
self
.
tensor
[:,
6
]
=
limit_period
(
self
.
tensor
[:,
6
],
offset
,
period
)
self
.
tensor
[:,
6
]
=
limit_period
(
self
.
tensor
[:,
6
],
offset
,
period
)
def
nonempty
(
self
,
threshold
:
float
=
0.0
):
def
nonempty
(
self
,
threshold
=
0.0
):
"""Find boxes that are non-empty.
"""Find boxes that are non-empty.
A box is considered empty,
A box is considered empty,
if either of its side is no larger than threshold.
if either of its side is no larger than threshold.
Args:
Args:
threshold (float): The threshold of minimal sizes.
threshold (float, optional): The threshold of minimal sizes.
Defaults to 0.0.
Returns:
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 is empty (False) or non-empty (True).
"""
"""
box
=
self
.
tensor
box
=
self
.
tensor
...
@@ -267,8 +316,8 @@ class BaseInstance3DBoxes(object):
...
@@ -267,8 +316,8 @@ class BaseInstance3DBoxes(object):
subject to Pytorch's indexing semantics.
subject to Pytorch's indexing semantics.
Returns:
Returns:
:obj:`BaseInstance3DBoxes`: A new object of
\
:obj:`BaseInstance3DBoxes`: A new object of
:class:`BaseInstance
s
3DBoxes` after indexing.
:class:`BaseInstance3DBoxes` after indexing.
"""
"""
original_type
=
type
(
self
)
original_type
=
type
(
self
)
if
isinstance
(
item
,
int
):
if
isinstance
(
item
,
int
):
...
@@ -319,7 +368,7 @@ class BaseInstance3DBoxes(object):
...
@@ -319,7 +368,7 @@ class BaseInstance3DBoxes(object):
device (str | :obj:`torch.device`): The name of the device.
device (str | :obj:`torch.device`): The name of the device.
Returns:
Returns:
:obj:`BaseInstance3DBoxes`: A new boxes object on the
\
:obj:`BaseInstance3DBoxes`: A new boxes object on the
specific device.
specific device.
"""
"""
original_type
=
type
(
self
)
original_type
=
type
(
self
)
...
@@ -332,7 +381,7 @@ class BaseInstance3DBoxes(object):
...
@@ -332,7 +381,7 @@ class BaseInstance3DBoxes(object):
"""Clone the Boxes.
"""Clone the Boxes.
Returns:
Returns:
:obj:`BaseInstance3DBoxes`: Box object with the same properties
\
:obj:`BaseInstance3DBoxes`: Box object with the same properties
as self.
as self.
"""
"""
original_type
=
type
(
self
)
original_type
=
type
(
self
)
...
@@ -363,7 +412,7 @@ class BaseInstance3DBoxes(object):
...
@@ -363,7 +412,7 @@ class BaseInstance3DBoxes(object):
Args:
Args:
boxes1 (:obj:`BaseInstance3DBoxes`): Boxes 1 contain N boxes.
boxes1 (:obj:`BaseInstance3DBoxes`): Boxes 1 contain N boxes.
boxes2 (:obj:`BaseInstance3DBoxes`): Boxes 2 contain M boxes.
boxes2 (:obj:`BaseInstance3DBoxes`): Boxes 2 contain M boxes.
mode (str, optional): Mode of
iou
calculation. Defaults to 'iou'.
mode (str, optional): Mode of
IoU
calculation. Defaults to 'iou'.
Returns:
Returns:
torch.Tensor: Calculated iou of boxes.
torch.Tensor: Calculated iou of boxes.
...
@@ -444,14 +493,14 @@ class BaseInstance3DBoxes(object):
...
@@ -444,14 +493,14 @@ class BaseInstance3DBoxes(object):
def
new_box
(
self
,
data
):
def
new_box
(
self
,
data
):
"""Create a new box object with 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.
as self and self.tensor, respectively.
Args:
Args:
data (torch.Tensor | numpy.array | list): Data to be copied.
data (torch.Tensor | numpy.array | list): Data to be copied.
Returns:
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``.
the object's other properties are similar to ``self``.
"""
"""
new_tensor
=
self
.
tensor
.
new_tensor
(
data
)
\
new_tensor
=
self
.
tensor
.
new_tensor
(
data
)
\
...
@@ -459,3 +508,75 @@ class BaseInstance3DBoxes(object):
...
@@ -459,3 +508,75 @@ class BaseInstance3DBoxes(object):
original_type
=
type
(
self
)
original_type
=
type
(
self
)
return
original_type
(
return
original_type
(
new_tensor
,
box_dim
=
self
.
box_dim
,
with_yaw
=
self
.
with_yaw
)
new_tensor
,
box_dim
=
self
.
box_dim
,
with_yaw
=
self
.
with_yaw
)
def
points_in_boxes_part
(
self
,
points
,
boxes_override
=
None
):
"""Find the box in which each point is.
Args:
points (torch.Tensor): Points in shape (1, M, 3) or (M, 3),
3 dimensions are (x, y, z) in LiDAR or depth coordinate.
boxes_override (torch.Tensor, optional): Boxes to override
`self.tensor`. Defaults to None.
Returns:
torch.Tensor: The index of the first box that each point
is in, in shape (M, ). Default value is -1
(if the point is not enclosed by any box).
Note:
If a point is enclosed by multiple boxes, the index of the
first box will be returned.
"""
if
boxes_override
is
not
None
:
boxes
=
boxes_override
else
:
boxes
=
self
.
tensor
if
points
.
dim
()
==
2
:
points
=
points
.
unsqueeze
(
0
)
box_idx
=
points_in_boxes_part
(
points
,
boxes
.
unsqueeze
(
0
).
to
(
points
.
device
)).
squeeze
(
0
)
return
box_idx
def
points_in_boxes_all
(
self
,
points
,
boxes_override
=
None
):
"""Find all boxes in which each point is.
Args:
points (torch.Tensor): Points in shape (1, M, 3) or (M, 3),
3 dimensions are (x, y, z) in LiDAR or depth coordinate.
boxes_override (torch.Tensor, optional): Boxes to override
`self.tensor`. Defaults to None.
Returns:
torch.Tensor: A tensor indicating whether a point is in a box,
in shape (M, T). T is the number of boxes. Denote this
tensor as A, if the m^th point is in the t^th box, then
`A[m, t] == 1`, elsewise `A[m, t] == 0`.
"""
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_all
(
points_clone
,
boxes
)
return
box_idxs_of_pts
.
squeeze
(
0
)
def
points_in_boxes
(
self
,
points
,
boxes_override
=
None
):
warnings
.
warn
(
'DeprecationWarning: points_in_boxes is a '
'deprecated method, please consider using '
'points_in_boxes_part.'
)
return
self
.
points_in_boxes_part
(
points
,
boxes_override
)
def
points_in_boxes_batch
(
self
,
points
,
boxes_override
=
None
):
warnings
.
warn
(
'DeprecationWarning: points_in_boxes_batch is a '
'deprecated method, please consider using '
'points_in_boxes_all.'
)
return
self
.
points_in_boxes_all
(
points
,
boxes_override
)
mmdet3d/core/bbox/structures/box_3d_mode.py
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved.
from
enum
import
IntEnum
,
unique
import
numpy
as
np
import
numpy
as
np
import
torch
import
torch
from
enum
import
IntEnum
,
unique
from
.base_box3d
import
BaseInstance3DBoxes
from
.base_box3d
import
BaseInstance3DBoxes
from
.cam_box3d
import
CameraInstance3DBoxes
from
.cam_box3d
import
CameraInstance3DBoxes
from
.depth_box3d
import
DepthInstance3DBoxes
from
.depth_box3d
import
DepthInstance3DBoxes
from
.lidar_box3d
import
LiDARInstance3DBoxes
from
.lidar_box3d
import
LiDARInstance3DBoxes
from
.utils
import
limit_period
@
unique
@
unique
...
@@ -61,23 +63,28 @@ class Box3DMode(IntEnum):
...
@@ -61,23 +63,28 @@ class Box3DMode(IntEnum):
DEPTH
=
2
DEPTH
=
2
@
staticmethod
@
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.
"""Convert boxes from `src` mode to `dst` mode.
Args:
Args:
box (tuple | list | np.ndarray |
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.
Can be a k-tuple, k-list or an Nxk array/tensor, where k = 7.
src (:obj:`Box3DMode`): The src Box mode.
src (:obj:`Box3DMode`): The src Box mode.
dst (:obj:`Box3DMode`): The target Box mode.
dst (:obj:`Box3DMode`): The target Box mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
rt_mat (np.ndarray | torch.Tensor, optional): The rotation and
matrix between different coordinates. Defaults to None.
translation matrix between different coordinates.
Defaults to None.
The conversion from `src` coordinates to `dst` coordinates
The conversion from `src` coordinates to `dst` coordinates
usually comes along the change of sensors, e.g., from camera
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
to LiDAR. This requires a transformation matrix.
with_yaw (bool, optional): If `box` is an instance of
:obj:`BaseInstance3DBoxes`, whether or not it has a yaw angle.
Defaults to True.
Returns:
Returns:
(tuple | list | np.ndarray | torch.Tensor | BaseInstance3DBoxes):
\
(tuple | list | np.ndarray | torch.Tensor |
:obj:`BaseInstance3DBoxes`):
The converted box of the same type.
The converted box of the same type.
"""
"""
if
src
==
dst
:
if
src
==
dst
:
...
@@ -100,32 +107,53 @@ class Box3DMode(IntEnum):
...
@@ -100,32 +107,53 @@ class Box3DMode(IntEnum):
else
:
else
:
arr
=
box
.
clone
()
arr
=
box
.
clone
()
if
is_Instance3DBoxes
:
with_yaw
=
box
.
with_yaw
# convert box from `src` mode to `dst` mode.
# convert box from `src` mode to `dst` mode.
x_size
,
y_size
,
z_size
=
arr
[...,
3
:
4
],
arr
[...,
4
:
5
],
arr
[...,
5
:
6
]
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
src
==
Box3DMode
.
LIDAR
and
dst
==
Box3DMode
.
CAM
:
if
rt_mat
is
None
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
0
,
0
,
-
1
],
[
1
,
0
,
0
]])
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
:
elif
src
==
Box3DMode
.
CAM
and
dst
==
Box3DMode
.
LIDAR
:
if
rt_mat
is
None
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
0
,
1
],
[
-
1
,
0
,
0
],
[
0
,
-
1
,
0
]])
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
:
elif
src
==
Box3DMode
.
DEPTH
and
dst
==
Box3DMode
.
CAM
:
if
rt_mat
is
None
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
1
,
0
,
0
],
[
0
,
0
,
1
],
[
0
,
-
1
,
0
]])
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
)
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
:
elif
src
==
Box3DMode
.
CAM
and
dst
==
Box3DMode
.
DEPTH
:
if
rt_mat
is
None
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
1
,
0
,
0
],
[
0
,
0
,
-
1
],
[
0
,
1
,
0
]])
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
)
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
:
elif
src
==
Box3DMode
.
LIDAR
and
dst
==
Box3DMode
.
DEPTH
:
if
rt_mat
is
None
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
1
,
0
,
0
],
[
0
,
0
,
1
]])
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
:
elif
src
==
Box3DMode
.
DEPTH
and
dst
==
Box3DMode
.
LIDAR
:
if
rt_mat
is
None
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
1
,
0
],
[
-
1
,
0
,
0
],
[
0
,
0
,
1
]])
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
:
else
:
raise
NotImplementedError
(
raise
NotImplementedError
(
f
'Conversion from Box3DMode
{
src
}
to
{
dst
}
'
f
'Conversion from Box3DMode
{
src
}
to
{
dst
}
'
...
@@ -135,13 +163,17 @@ class Box3DMode(IntEnum):
...
@@ -135,13 +163,17 @@ class Box3DMode(IntEnum):
rt_mat
=
arr
.
new_tensor
(
rt_mat
)
rt_mat
=
arr
.
new_tensor
(
rt_mat
)
if
rt_mat
.
size
(
1
)
==
4
:
if
rt_mat
.
size
(
1
)
==
4
:
extended_xyz
=
torch
.
cat
(
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
()
xyz
=
extended_xyz
@
rt_mat
.
t
()
else
:
else
:
xyz
=
arr
[
:
,
:
3
]
@
rt_mat
.
t
()
xyz
=
arr
[
...
,
:
3
]
@
rt_mat
.
t
()
remains
=
arr
[...,
6
:]
if
with_yaw
:
arr
=
torch
.
cat
([
xyz
[:,
:
3
],
xyz_size
,
remains
],
dim
=-
1
)
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
# convert arr to the original type
original_type
=
type
(
box
)
original_type
=
type
(
box
)
...
@@ -160,7 +192,6 @@ class Box3DMode(IntEnum):
...
@@ -160,7 +192,6 @@ class Box3DMode(IntEnum):
raise
NotImplementedError
(
raise
NotImplementedError
(
f
'Conversion to
{
dst
}
through
{
original_type
}
'
f
'Conversion to
{
dst
}
through
{
original_type
}
'
' is not supported yet'
)
' is not supported yet'
)
return
target_type
(
return
target_type
(
arr
,
box_dim
=
arr
.
size
(
-
1
),
with_yaw
=
with_yaw
)
arr
,
box_dim
=
arr
.
size
(
-
1
),
with_yaw
=
box
.
with_yaw
)
else
:
else
:
return
arr
return
arr
mmdet3d/core/bbox/structures/cam_box3d.py
View file @
32a4328b
...
@@ -2,9 +2,9 @@
...
@@ -2,9 +2,9 @@
import
numpy
as
np
import
numpy
as
np
import
torch
import
torch
from
mmdet3d.core
.points
import
BasePoints
from
..
.points
import
BasePoints
from
.base_box3d
import
BaseInstance3DBoxes
from
.base_box3d
import
BaseInstance3DBoxes
from
.utils
import
limit_period
,
rotation_3d_in_axis
from
.utils
import
rotation_3d_in_axis
,
yaw2local
class
CameraInstance3DBoxes
(
BaseInstance3DBoxes
):
class
CameraInstance3DBoxes
(
BaseInstance3DBoxes
):
...
@@ -28,16 +28,14 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -28,16 +28,14 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
The yaw is 0 at the positive direction of x axis, and decreases from
The yaw is 0 at the positive direction of x axis, and decreases from
the positive direction of x to the positive direction of z.
the positive direction of x to the positive direction of z.
A refactor is ongoing to make the three coordinate systems
easier to understand and convert between each other.
Attributes:
Attributes:
tensor (torch.Tensor): Float matrix
of N x
box_dim.
tensor (torch.Tensor): Float matrix
in shape (N,
box_dim
)
.
box_dim (int): Integer indicat
es
the dimension of a box
box_dim (int): Integer indicat
ing
the dimension of a box
Each row is (x, y, z, x_size, y_size, z_size, yaw, ...).
Each row is (x, y, z, x_size, y_size, z_size, yaw, ...).
with_yaw (bool): If True, the value of yaw will be set to 0 as
minmax
with_yaw (bool): If True, the value of yaw will be set to 0 as
boxes.
axis-aligned boxes tightly enclosing the original
boxes.
"""
"""
YAW_AXIS
=
1
def
__init__
(
self
,
def
__init__
(
self
,
tensor
,
tensor
,
...
@@ -76,23 +74,39 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -76,23 +74,39 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
@
property
@
property
def
height
(
self
):
def
height
(
self
):
"""torch.Tensor: A vector with height of each box."""
"""torch.Tensor: A vector with height of each box
in shape (N, )
."""
return
self
.
tensor
[:,
4
]
return
self
.
tensor
[:,
4
]
@
property
@
property
def
top_height
(
self
):
def
top_height
(
self
):
"""torch.Tensor: A vector with the top height of each box."""
"""torch.Tensor:
A vector with the top height of each box in shape (N, )."""
# the positive direction is down rather than up
# the positive direction is down rather than up
return
self
.
bottom_height
-
self
.
height
return
self
.
bottom_height
-
self
.
height
@
property
@
property
def
bottom_height
(
self
):
def
bottom_height
(
self
):
"""torch.Tensor: A vector with bottom's height of each box."""
"""torch.Tensor:
A vector with bottom's height of each box in shape (N, )."""
return
self
.
tensor
[:,
1
]
return
self
.
tensor
[:,
1
]
@
property
def
local_yaw
(
self
):
"""torch.Tensor:
A vector with local yaw of each box in shape (N, ).
local_yaw equals to alpha in kitti, which is commonly
used in monocular 3D object detection task, so only
:obj:`CameraInstance3DBoxes` has the property.
"""
yaw
=
self
.
yaw
loc
=
self
.
gravity_center
local_yaw
=
yaw2local
(
yaw
,
loc
)
return
local_yaw
@
property
@
property
def
gravity_center
(
self
):
def
gravity_center
(
self
):
"""torch.Tensor: A tensor with center of each box."""
"""torch.Tensor: A tensor with center of each box
in shape (N, 3)
."""
bottom_center
=
self
.
bottom_center
bottom_center
=
self
.
bottom_center
gravity_center
=
torch
.
zeros_like
(
bottom_center
)
gravity_center
=
torch
.
zeros_like
(
bottom_center
)
gravity_center
[:,
[
0
,
2
]]
=
bottom_center
[:,
[
0
,
2
]]
gravity_center
[:,
[
0
,
2
]]
=
bottom_center
[:,
[
0
,
2
]]
...
@@ -137,82 +151,66 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -137,82 +151,66 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
corners_norm
=
corners_norm
-
dims
.
new_tensor
([
0.5
,
1
,
0.5
])
corners_norm
=
corners_norm
-
dims
.
new_tensor
([
0.5
,
1
,
0.5
])
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
# rotate around y
axis
corners
=
rotation_3d_in_
axis
(
corners
=
rotation_3d_in_axis
(
corners
,
self
.
tensor
[:,
6
],
axis
=
1
)
corners
,
self
.
tensor
[:,
6
],
axis
=
self
.
YAW_AXIS
)
corners
+=
self
.
tensor
[:,
:
3
].
view
(
-
1
,
1
,
3
)
corners
+=
self
.
tensor
[:,
:
3
].
view
(
-
1
,
1
,
3
)
return
corners
return
corners
@
property
@
property
def
bev
(
self
):
def
bev
(
self
):
"""torch.Tensor: A n x 5 tensor of 2D BEV box of each box
"""torch.Tensor: 2D BEV box of each box with rotation
with rotation in XYWHR format."""
in XYWHR format, in shape (N, 5)."""
return
self
.
tensor
[:,
[
0
,
2
,
3
,
5
,
6
]]
bev
=
self
.
tensor
[:,
[
0
,
2
,
3
,
5
,
6
]].
clone
()
# positive direction of the gravity axis
@
property
# in cam coord system points to the earth
def
nearest_bev
(
self
):
# so the bev yaw angle needs to be reversed
"""torch.Tensor: A tensor of 2D BEV box of each box
bev
[:,
-
1
]
=
-
bev
[:,
-
1
]
without rotation."""
return
bev
# Obtain BEV boxes with rotation in XZWHR format
bev_rotated_boxes
=
self
.
bev
# convert the rotation to a valid range
rotations
=
bev_rotated_boxes
[:,
-
1
]
normed_rotations
=
torch
.
abs
(
limit_period
(
rotations
,
0.5
,
np
.
pi
))
# find the center of boxes
conditions
=
(
normed_rotations
>
np
.
pi
/
4
)[...,
None
]
bboxes_xywh
=
torch
.
where
(
conditions
,
bev_rotated_boxes
[:,
[
0
,
1
,
3
,
2
]],
bev_rotated_boxes
[:,
:
4
])
centers
=
bboxes_xywh
[:,
:
2
]
dims
=
bboxes_xywh
[:,
2
:]
bev_boxes
=
torch
.
cat
([
centers
-
dims
/
2
,
centers
+
dims
/
2
],
dim
=-
1
)
return
bev_boxes
def
rotate
(
self
,
angle
,
points
=
None
):
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
"""Rotate boxes with points (optional) with the given angle or
rotation
rotation
matrix.
matrix.
Args:
Args:
angle (float | torch.Tensor | np.ndarray):
angle (float | torch.Tensor | np.ndarray):
Rotation angle or rotation matrix.
Rotation angle or rotation matrix.
points (torch.Tensor
, numpy
.ndarray
,
:obj:`BasePoints`, optional):
points (torch.Tensor
| np
.ndarray
|
:obj:`BasePoints`, optional):
Points to rotate. Defaults to None.
Points to rotate. Defaults to None.
Returns:
Returns:
tuple or None: When ``points`` is None, the function returns
\
tuple or None: When ``points`` is None, the function returns
None, otherwise it returns the rotated points and the
\
None, otherwise it returns the rotated points and the
rotation matrix ``rot_mat_T``.
rotation matrix ``rot_mat_T``.
"""
"""
if
not
isinstance
(
angle
,
torch
.
Tensor
):
if
not
isinstance
(
angle
,
torch
.
Tensor
):
angle
=
self
.
tensor
.
new_tensor
(
angle
)
angle
=
self
.
tensor
.
new_tensor
(
angle
)
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
f
'invalid rotation angle shape
{
angle
.
shape
}
'
f
'invalid rotation angle shape
{
angle
.
shape
}
'
if
angle
.
numel
()
==
1
:
if
angle
.
numel
()
==
1
:
rot_sin
=
torch
.
sin
(
angle
)
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
rotation_3d_in_axis
(
rot_cos
=
torch
.
cos
(
angle
)
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
self
.
tensor
.
new_tensor
([[
rot_cos
,
0
,
-
rot_sin
]
,
angle
,
[
0
,
1
,
0
]
,
axis
=
self
.
YAW_AXIS
,
[
rot_sin
,
0
,
rot_cos
]]
)
return_mat
=
True
)
else
:
else
:
rot_mat_T
=
angle
rot_mat_T
=
angle
rot_sin
=
rot_mat_T
[
2
,
0
]
rot_sin
=
rot_mat_T
[
2
,
0
]
rot_cos
=
rot_mat_T
[
0
,
0
]
rot_cos
=
rot_mat_T
[
0
,
0
]
angle
=
np
.
arctan2
(
rot_sin
,
rot_cos
)
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
self
.
tensor
[:,
6
]
+=
angle
if
points
is
not
None
:
if
points
is
not
None
:
if
isinstance
(
points
,
torch
.
Tensor
):
if
isinstance
(
points
,
torch
.
Tensor
):
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
elif
isinstance
(
points
,
np
.
ndarray
):
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
)
points
[:,
:
3
]
=
np
.
dot
(
points
[:,
:
3
],
rot_mat_T
)
elif
isinstance
(
points
,
BasePoints
):
elif
isinstance
(
points
,
BasePoints
):
# clockwise
points
.
rotate
(
rot_mat_T
)
points
.
rotate
(
-
angle
)
else
:
else
:
raise
ValueError
raise
ValueError
return
points
,
rot_mat_T
return
points
,
rot_mat_T
...
@@ -224,7 +222,7 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -224,7 +222,7 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
Args:
Args:
bev_direction (str): Flip direction (horizontal or vertical).
bev_direction (str): Flip direction (horizontal or vertical).
points (torch.Tensor
, numpy
.ndarray
,
:obj:`BasePoints`,
None
):
points (torch.Tensor
| np
.ndarray
|
:obj:`BasePoints`,
optional
):
Points to flip. Defaults to None.
Points to flip. Defaults to None.
Returns:
Returns:
...
@@ -251,28 +249,6 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -251,28 +249,6 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
points
.
flip
(
bev_direction
)
points
.
flip
(
bev_direction
)
return
points
return
points
def
in_range_bev
(
self
,
box_range
):
"""Check whether the boxes are in the given range.
Args:
box_range (list | torch.Tensor): The range of box
(x_min, z_min, x_max, z_max).
Note:
The original implementation of SECOND checks whether boxes in
a range by checking whether the points are in a convex
polygon, we reduce the burden for simpler cases.
Returns:
torch.Tensor: Indicating whether each box is inside
\
the reference range.
"""
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
&
(
self
.
tensor
[:,
2
]
>
box_range
[
1
])
&
(
self
.
tensor
[:,
0
]
<
box_range
[
2
])
&
(
self
.
tensor
[:,
2
]
<
box_range
[
3
]))
return
in_range_flags
@
classmethod
@
classmethod
def
height_overlaps
(
cls
,
boxes1
,
boxes2
,
mode
=
'iou'
):
def
height_overlaps
(
cls
,
boxes1
,
boxes2
,
mode
=
'iou'
):
"""Calculate height overlaps of two boxes.
"""Calculate height overlaps of two boxes.
...
@@ -296,8 +272,8 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -296,8 +272,8 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
boxes2_top_height
=
boxes2
.
top_height
.
view
(
1
,
-
1
)
boxes2_top_height
=
boxes2
.
top_height
.
view
(
1
,
-
1
)
boxes2_bottom_height
=
boxes2
.
bottom_height
.
view
(
1
,
-
1
)
boxes2_bottom_height
=
boxes2
.
bottom_height
.
view
(
1
,
-
1
)
#
In camera coordinate system
#
positive direction of the gravity axis
#
from up to down is the positive direction
#
in cam coord system points to the earth
heighest_of_bottom
=
torch
.
min
(
boxes1_bottom_height
,
heighest_of_bottom
=
torch
.
min
(
boxes1_bottom_height
,
boxes2_bottom_height
)
boxes2_bottom_height
)
lowest_of_top
=
torch
.
max
(
boxes1_top_height
,
boxes2_top_height
)
lowest_of_top
=
torch
.
max
(
boxes1_top_height
,
boxes2_top_height
)
...
@@ -309,16 +285,70 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -309,16 +285,70 @@ class CameraInstance3DBoxes(BaseInstance3DBoxes):
Args:
Args:
dst (:obj:`Box3DMode`): The target Box mode.
dst (:obj:`Box3DMode`): The target Box mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
rt_mat (np.ndarray | torch.Tensor, optional): The rotation and
matrix between different coordinates. Defaults to None.
translation matrix between different coordinates.
Defaults to None.
The conversion from ``src`` coordinates to ``dst`` coordinates
The conversion from ``src`` coordinates to ``dst`` coordinates
usually comes along the change of sensors, e.g., from camera
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
to LiDAR. This requires a transformation matrix.
Returns:
Returns:
:obj:`BaseInstance3DBoxes`:
\
:obj:`BaseInstance3DBoxes`:
The converted box of the same type in the ``dst`` mode.
The converted box of the same type in the ``dst`` mode.
"""
"""
from
.box_3d_mode
import
Box3DMode
from
.box_3d_mode
import
Box3DMode
return
Box3DMode
.
convert
(
return
Box3DMode
.
convert
(
box
=
self
,
src
=
Box3DMode
.
CAM
,
dst
=
dst
,
rt_mat
=
rt_mat
)
box
=
self
,
src
=
Box3DMode
.
CAM
,
dst
=
dst
,
rt_mat
=
rt_mat
)
def
points_in_boxes_part
(
self
,
points
,
boxes_override
=
None
):
"""Find the box in which each point is.
Args:
points (torch.Tensor): Points in shape (1, M, 3) or (M, 3),
3 dimensions are (x, y, z) in LiDAR or depth coordinate.
boxes_override (torch.Tensor, optional): Boxes to override
`self.tensor `. Defaults to None.
Returns:
torch.Tensor: The index of the box in which
each point is, in shape (M, ). Default value is -1
(if the point is not enclosed by any box).
"""
from
.coord_3d_mode
import
Coord3DMode
points_lidar
=
Coord3DMode
.
convert
(
points
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
if
boxes_override
is
not
None
:
boxes_lidar
=
boxes_override
else
:
boxes_lidar
=
Coord3DMode
.
convert
(
self
.
tensor
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
box_idx
=
super
().
points_in_boxes_part
(
points_lidar
,
boxes_lidar
)
return
box_idx
def
points_in_boxes_all
(
self
,
points
,
boxes_override
=
None
):
"""Find all boxes in which each point is.
Args:
points (torch.Tensor): Points in shape (1, M, 3) or (M, 3),
3 dimensions are (x, y, z) in LiDAR or depth coordinate.
boxes_override (torch.Tensor, optional): Boxes to override
`self.tensor `. Defaults to None.
Returns:
torch.Tensor: The index of all boxes in which each point is,
in shape (B, M, T).
"""
from
.coord_3d_mode
import
Coord3DMode
points_lidar
=
Coord3DMode
.
convert
(
points
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
if
boxes_override
is
not
None
:
boxes_lidar
=
boxes_override
else
:
boxes_lidar
=
Coord3DMode
.
convert
(
self
.
tensor
,
Coord3DMode
.
CAM
,
Coord3DMode
.
LIDAR
)
box_idx
=
super
().
points_in_boxes_all
(
points_lidar
,
boxes_lidar
)
return
box_idx
mmdet3d/core/bbox/structures/coord_3d_mode.py
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved.
from
enum
import
IntEnum
,
unique
import
numpy
as
np
import
numpy
as
np
import
torch
import
torch
from
enum
import
IntEnum
,
unique
from
mmdet3d.core.points
import
(
BasePoints
,
CameraPoints
,
DepthPoints
,
from
...points
import
BasePoints
,
CameraPoints
,
DepthPoints
,
LiDARPoints
LiDARPoints
)
from
.base_box3d
import
BaseInstance3DBoxes
from
.base_box3d
import
BaseInstance3DBoxes
from
.cam_box3d
import
CameraInstance3DBoxes
from
.box_3d_mode
import
Box3DMode
from
.depth_box3d
import
DepthInstance3DBoxes
from
.lidar_box3d
import
LiDARInstance3DBoxes
@
unique
@
unique
...
@@ -64,119 +62,75 @@ class Coord3DMode(IntEnum):
...
@@ -64,119 +62,75 @@ class Coord3DMode(IntEnum):
DEPTH
=
2
DEPTH
=
2
@
staticmethod
@
staticmethod
def
convert
(
input
,
src
,
dst
,
rt_mat
=
None
):
def
convert
(
input
,
src
,
dst
,
rt_mat
=
None
,
with_yaw
=
True
,
is_point
=
True
):
"""Convert boxes or points from `src` mode to `dst` mode."""
"""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, optional): 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
):
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
):
elif
isinstance
(
input
,
BasePoints
):
return
Coord3DMode
.
convert_point
(
input
,
src
,
dst
,
rt_mat
=
rt_mat
)
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
:
else
:
raise
NotImplementedError
raise
NotImplementedError
@
staticmethod
@
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.
"""Convert boxes from `src` mode to `dst` mode.
Args:
Args:
box (tuple | list | np.ndarray |
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.
Can be a k-tuple, k-list or an Nxk array/tensor, where k = 7.
src (:obj:`CoordMode`): The src Box mode.
src (:obj:`Box3DMode`): The src Box mode.
dst (:obj:`CoordMode`): The target Box mode.
dst (:obj:`Box3DMode`): The target Box mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
rt_mat (np.ndarray | torch.Tensor, optional): The rotation and
matrix between different coordinates. Defaults to None.
translation matrix between different coordinates.
Defaults to None.
The conversion from `src` coordinates to `dst` coordinates
The conversion from `src` coordinates to `dst` coordinates
usually comes along the change of sensors, e.g., from camera
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
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:
Returns:
(tuple | list | np.ndarray | torch.Tensor | BaseInstance3DBoxes):
\
(tuple | list | np.ndarray | torch.Tensor |
:obj:`BaseInstance3DBoxes`):
The converted box of the same type.
The converted box of the same type.
"""
"""
if
src
==
dst
:
return
Box3DMode
.
convert
(
box
,
src
,
dst
,
rt_mat
=
rt_mat
)
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
@
staticmethod
@
staticmethod
def
convert_point
(
point
,
src
,
dst
,
rt_mat
=
None
):
def
convert_point
(
point
,
src
,
dst
,
rt_mat
=
None
):
...
@@ -184,18 +138,19 @@ class Coord3DMode(IntEnum):
...
@@ -184,18 +138,19 @@ class Coord3DMode(IntEnum):
Args:
Args:
point (tuple | list | np.ndarray |
point (tuple | list | np.ndarray |
torch.Tensor | BasePoints):
torch.Tensor |
:obj:`
BasePoints
`
):
Can be a k-tuple, k-list or an Nxk array/tensor.
Can be a k-tuple, k-list or an Nxk array/tensor.
src (:obj:`CoordMode`): The src Point mode.
src (:obj:`CoordMode`): The src Point mode.
dst (:obj:`CoordMode`): The target Point mode.
dst (:obj:`CoordMode`): The target Point mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
rt_mat (np.ndarray | torch.Tensor, optional): The rotation and
matrix between different coordinates. Defaults to None.
translation matrix between different coordinates.
Defaults to None.
The conversion from `src` coordinates to `dst` coordinates
The conversion from `src` coordinates to `dst` coordinates
usually comes along the change of sensors, e.g., from camera
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
to LiDAR. This requires a transformation matrix.
Returns:
Returns:
(tuple | list | np.ndarray | torch.Tensor | BasePoints):
\
(tuple | list | np.ndarray | torch.Tensor |
:obj:`
BasePoints
`
):
The converted point of the same type.
The converted point of the same type.
"""
"""
if
src
==
dst
:
if
src
==
dst
:
...
@@ -219,8 +174,6 @@ class Coord3DMode(IntEnum):
...
@@ -219,8 +174,6 @@ class Coord3DMode(IntEnum):
arr
=
point
.
clone
()
arr
=
point
.
clone
()
# convert point from `src` mode to `dst` mode.
# 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
src
==
Coord3DMode
.
LIDAR
and
dst
==
Coord3DMode
.
CAM
:
if
rt_mat
is
None
:
if
rt_mat
is
None
:
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
0
,
0
,
-
1
],
[
1
,
0
,
0
]])
rt_mat
=
arr
.
new_tensor
([[
0
,
-
1
,
0
],
[
0
,
0
,
-
1
],
[
1
,
0
,
0
]])
...
@@ -248,13 +201,13 @@ class Coord3DMode(IntEnum):
...
@@ -248,13 +201,13 @@ class Coord3DMode(IntEnum):
rt_mat
=
arr
.
new_tensor
(
rt_mat
)
rt_mat
=
arr
.
new_tensor
(
rt_mat
)
if
rt_mat
.
size
(
1
)
==
4
:
if
rt_mat
.
size
(
1
)
==
4
:
extended_xyz
=
torch
.
cat
(
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
()
xyz
=
extended_xyz
@
rt_mat
.
t
()
else
:
else
:
xyz
=
arr
[
:
,
:
3
]
@
rt_mat
.
t
()
xyz
=
arr
[
...
,
:
3
]
@
rt_mat
.
t
()
remains
=
arr
[
:
,
3
:]
remains
=
arr
[
...
,
3
:]
arr
=
torch
.
cat
([
xyz
[
:
,
:
3
],
remains
],
dim
=-
1
)
arr
=
torch
.
cat
([
xyz
[
...
,
:
3
],
remains
],
dim
=-
1
)
# convert arr to the original type
# convert arr to the original type
original_type
=
type
(
point
)
original_type
=
type
(
point
)
...
...
mmdet3d/core/bbox/structures/depth_box3d.py
View file @
32a4328b
...
@@ -3,9 +3,8 @@ import numpy as np
...
@@ -3,9 +3,8 @@ import numpy as np
import
torch
import
torch
from
mmdet3d.core.points
import
BasePoints
from
mmdet3d.core.points
import
BasePoints
from
mmdet3d.ops
import
points_in_boxes_batch
from
.base_box3d
import
BaseInstance3DBoxes
from
.base_box3d
import
BaseInstance3DBoxes
from
.utils
import
limit_period
,
rotation_3d_in_axis
from
.utils
import
rotation_3d_in_axis
class
DepthInstance3DBoxes
(
BaseInstance3DBoxes
):
class
DepthInstance3DBoxes
(
BaseInstance3DBoxes
):
...
@@ -38,10 +37,11 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -38,10 +37,11 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
boxes.
boxes.
"""
"""
YAW_AXIS
=
2
@
property
@
property
def
gravity_center
(
self
):
def
gravity_center
(
self
):
"""torch.Tensor: A tensor with center of each box."""
"""torch.Tensor: A tensor with center of each box
in shape (N, 3)
."""
bottom_center
=
self
.
bottom_center
bottom_center
=
self
.
bottom_center
gravity_center
=
torch
.
zeros_like
(
bottom_center
)
gravity_center
=
torch
.
zeros_like
(
bottom_center
)
gravity_center
[:,
:
2
]
=
bottom_center
[:,
:
2
]
gravity_center
[:,
:
2
]
=
bottom_center
[:,
:
2
]
...
@@ -85,73 +85,50 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -85,73 +85,50 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
# rotate around z axis
# 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
)
corners
+=
self
.
tensor
[:,
:
3
].
view
(
-
1
,
1
,
3
)
return
corners
return
corners
@
property
def
bev
(
self
):
"""torch.Tensor: A n x 5 tensor of 2D BEV box of each box
in XYWHR format."""
return
self
.
tensor
[:,
[
0
,
1
,
3
,
4
,
6
]]
@
property
def
nearest_bev
(
self
):
"""torch.Tensor: A tensor of 2D BEV box of each box
without rotation."""
# Obtain BEV boxes with rotation in XYWHR format
bev_rotated_boxes
=
self
.
bev
# convert the rotation to a valid range
rotations
=
bev_rotated_boxes
[:,
-
1
]
normed_rotations
=
torch
.
abs
(
limit_period
(
rotations
,
0.5
,
np
.
pi
))
# find the center of boxes
conditions
=
(
normed_rotations
>
np
.
pi
/
4
)[...,
None
]
bboxes_xywh
=
torch
.
where
(
conditions
,
bev_rotated_boxes
[:,
[
0
,
1
,
3
,
2
]],
bev_rotated_boxes
[:,
:
4
])
centers
=
bboxes_xywh
[:,
:
2
]
dims
=
bboxes_xywh
[:,
2
:]
bev_boxes
=
torch
.
cat
([
centers
-
dims
/
2
,
centers
+
dims
/
2
],
dim
=-
1
)
return
bev_boxes
def
rotate
(
self
,
angle
,
points
=
None
):
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
"""Rotate boxes with points (optional) with the given angle or
rotation
rotation
matrix.
matrix.
Args:
Args:
angle (float | torch.Tensor | np.ndarray):
angle (float | torch.Tensor | np.ndarray):
Rotation angle or rotation matrix.
Rotation angle or rotation matrix.
points (torch.Tensor
, numpy
.ndarray
,
:obj:`BasePoints`, optional):
points (torch.Tensor
| np
.ndarray
|
:obj:`BasePoints`, optional):
Points to rotate. Defaults to None.
Points to rotate. Defaults to None.
Returns:
Returns:
tuple or None: When ``points`` is None, the function returns
\
tuple or None: When ``points`` is None, the function returns
None, otherwise it returns the rotated points and the
\
None, otherwise it returns the rotated points and the
rotation matrix ``rot_mat_T``.
rotation matrix ``rot_mat_T``.
"""
"""
if
not
isinstance
(
angle
,
torch
.
Tensor
):
if
not
isinstance
(
angle
,
torch
.
Tensor
):
angle
=
self
.
tensor
.
new_tensor
(
angle
)
angle
=
self
.
tensor
.
new_tensor
(
angle
)
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
f
'invalid rotation angle shape
{
angle
.
shape
}
'
f
'invalid rotation angle shape
{
angle
.
shape
}
'
if
angle
.
numel
()
==
1
:
if
angle
.
numel
()
==
1
:
rot_sin
=
torch
.
sin
(
angle
)
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
rotation_3d_in_axis
(
rot_cos
=
torch
.
cos
(
angle
)
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
self
.
tensor
.
new_tensor
([[
rot_cos
,
-
rot_sin
,
0
]
,
angle
,
[
rot_sin
,
rot_cos
,
0
]
,
axis
=
self
.
YAW_AXIS
,
[
0
,
0
,
1
]]).
T
return_mat
=
True
)
else
:
else
:
rot_mat_T
=
angle
.
T
rot_mat_T
=
angle
rot_sin
=
rot_mat_T
[
0
,
1
]
rot_sin
=
rot_mat_T
[
0
,
1
]
rot_cos
=
rot_mat_T
[
0
,
0
]
rot_cos
=
rot_mat_T
[
0
,
0
]
angle
=
np
.
arctan2
(
rot_sin
,
rot_cos
)
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
:
if
self
.
with_yaw
:
self
.
tensor
[:,
6
]
-
=
angle
self
.
tensor
[:,
6
]
+
=
angle
else
:
else
:
# for axis-aligned boxes, we take the new
# enclosing axis-aligned boxes after rotation
corners_rot
=
self
.
corners
@
rot_mat_T
corners_rot
=
self
.
corners
@
rot_mat_T
new_x_size
=
corners_rot
[...,
0
].
max
(
new_x_size
=
corners_rot
[...,
0
].
max
(
dim
=
1
,
keepdim
=
True
)[
0
]
-
corners_rot
[...,
0
].
min
(
dim
=
1
,
keepdim
=
True
)[
0
]
-
corners_rot
[...,
0
].
min
(
...
@@ -165,11 +142,10 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -165,11 +142,10 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
if
isinstance
(
points
,
torch
.
Tensor
):
if
isinstance
(
points
,
torch
.
Tensor
):
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
elif
isinstance
(
points
,
np
.
ndarray
):
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
)
points
[:,
:
3
]
=
np
.
dot
(
points
[:,
:
3
],
rot_mat_T
)
elif
isinstance
(
points
,
BasePoints
):
elif
isinstance
(
points
,
BasePoints
):
# anti-clockwise
points
.
rotate
(
rot_mat_T
)
points
.
rotate
(
angle
)
else
:
else
:
raise
ValueError
raise
ValueError
return
points
,
rot_mat_T
return
points
,
rot_mat_T
...
@@ -180,8 +156,9 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -180,8 +156,9 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
In Depth coordinates, it flips x (horizontal) or y (vertical) axis.
In Depth coordinates, it flips x (horizontal) or y (vertical) axis.
Args:
Args:
bev_direction (str): Flip direction (horizontal or vertical).
bev_direction (str, optional): Flip direction
points (torch.Tensor, numpy.ndarray, :obj:`BasePoints`, None):
(horizontal or vertical). Defaults to 'horizontal'.
points (torch.Tensor | np.ndarray | :obj:`BasePoints`, optional):
Points to flip. Defaults to None.
Points to flip. Defaults to None.
Returns:
Returns:
...
@@ -208,75 +185,26 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -208,75 +185,26 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
points
.
flip
(
bev_direction
)
points
.
flip
(
bev_direction
)
return
points
return
points
def
in_range_bev
(
self
,
box_range
):
"""Check whether the boxes are in the given range.
Args:
box_range (list | torch.Tensor): The range of box
(x_min, y_min, x_max, y_max).
Note:
In the original implementation of SECOND, checking whether
a box in the range checks whether the points are in a convex
polygon, we try to reduce the burdun for simpler cases.
Returns:
torch.Tensor: Indicating whether each box is inside
\
the reference range.
"""
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
&
(
self
.
tensor
[:,
1
]
>
box_range
[
1
])
&
(
self
.
tensor
[:,
0
]
<
box_range
[
2
])
&
(
self
.
tensor
[:,
1
]
<
box_range
[
3
]))
return
in_range_flags
def
convert_to
(
self
,
dst
,
rt_mat
=
None
):
def
convert_to
(
self
,
dst
,
rt_mat
=
None
):
"""Convert self to ``dst`` mode.
"""Convert self to ``dst`` mode.
Args:
Args:
dst (:obj:`Box3DMode`): The target Box mode.
dst (:obj:`Box3DMode`): The target Box mode.
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
rt_mat (np.ndarray | torch.Tensor, optional): The rotation and
matrix between different coordinates. Defaults to None.
translation matrix between different coordinates.
Defaults to None.
The conversion from ``src`` coordinates to ``dst`` coordinates
The conversion from ``src`` coordinates to ``dst`` coordinates
usually comes along the change of sensors, e.g., from camera
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
to LiDAR. This requires a transformation matrix.
Returns:
Returns:
:obj:`DepthInstance3DBoxes`:
\
:obj:`DepthInstance3DBoxes`:
The converted box of the same type in the ``dst`` mode.
The converted box of the same type in the ``dst`` mode.
"""
"""
from
.box_3d_mode
import
Box3DMode
from
.box_3d_mode
import
Box3DMode
return
Box3DMode
.
convert
(
return
Box3DMode
.
convert
(
box
=
self
,
src
=
Box3DMode
.
DEPTH
,
dst
=
dst
,
rt_mat
=
rt_mat
)
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
):
def
enlarged_box
(
self
,
extra_width
):
"""Enlarge the length, width and height boxes.
"""Enlarge the length, width and height boxes.
...
@@ -284,7 +212,7 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -284,7 +212,7 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
extra_width (float | torch.Tensor): Extra width to enlarge the box.
extra_width (float | torch.Tensor): Extra width to enlarge the box.
Returns:
Returns:
:obj:`
LiDAR
Instance3DBoxes`: Enlarged boxes.
:obj:`
Depth
Instance3DBoxes`: Enlarged boxes.
"""
"""
enlarged_boxes
=
self
.
tensor
.
clone
()
enlarged_boxes
=
self
.
tensor
.
clone
()
enlarged_boxes
[:,
3
:
6
]
+=
extra_width
*
2
enlarged_boxes
[:,
3
:
6
]
+=
extra_width
*
2
...
@@ -331,13 +259,12 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -331,13 +259,12 @@ class DepthInstance3DBoxes(BaseInstance3DBoxes):
-
1
,
3
)
-
1
,
3
)
surface_rot
=
rot_mat_T
.
repeat
(
6
,
1
,
1
)
surface_rot
=
rot_mat_T
.
repeat
(
6
,
1
,
1
)
surface_3d
=
torch
.
matmul
(
surface_3d
=
torch
.
matmul
(
surface_3d
.
unsqueeze
(
-
2
),
surface_3d
.
unsqueeze
(
-
2
),
surface_rot
.
transpose
(
2
,
1
)
).
squeeze
(
-
2
)
surface_rot
).
squeeze
(
-
2
)
surface_center
=
center
.
repeat
(
1
,
6
,
1
).
reshape
(
-
1
,
3
)
+
surface_3d
surface_center
=
center
.
repeat
(
1
,
6
,
1
).
reshape
(
-
1
,
3
)
+
surface_3d
line_rot
=
rot_mat_T
.
repeat
(
12
,
1
,
1
)
line_rot
=
rot_mat_T
.
repeat
(
12
,
1
,
1
)
line_3d
=
torch
.
matmul
(
line_3d
=
torch
.
matmul
(
line_3d
.
unsqueeze
(
-
2
),
line_rot
).
squeeze
(
-
2
)
line_3d
.
unsqueeze
(
-
2
),
line_rot
.
transpose
(
2
,
1
)).
squeeze
(
-
2
)
line_center
=
center
.
repeat
(
1
,
12
,
1
).
reshape
(
-
1
,
3
)
+
line_3d
line_center
=
center
.
repeat
(
1
,
12
,
1
).
reshape
(
-
1
,
3
)
+
line_3d
return
surface_center
,
line_center
return
surface_center
,
line_center
mmdet3d/core/bbox/structures/lidar_box3d.py
View file @
32a4328b
...
@@ -3,9 +3,8 @@ import numpy as np
...
@@ -3,9 +3,8 @@ import numpy as np
import
torch
import
torch
from
mmdet3d.core.points
import
BasePoints
from
mmdet3d.core.points
import
BasePoints
from
mmdet3d.ops.roiaware_pool3d
import
points_in_boxes_gpu
from
.base_box3d
import
BaseInstance3DBoxes
from
.base_box3d
import
BaseInstance3DBoxes
from
.utils
import
limit_period
,
rotation_3d_in_axis
from
.utils
import
rotation_3d_in_axis
class
LiDARInstance3DBoxes
(
BaseInstance3DBoxes
):
class
LiDARInstance3DBoxes
(
BaseInstance3DBoxes
):
...
@@ -15,16 +14,16 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -15,16 +14,16 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
.. code-block:: none
.. code-block:: none
up z x front (yaw=
-0.5*pi
)
up z x front (yaw=
0
)
^ ^
^ ^
| /
| /
| /
| /
(yaw=
-
pi) left y <------ 0
-------- (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),
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.
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 yaw is 0 at the
posi
tive direction of
x
axis, and
in
creases from
the
nega
tive direction of
y
to the positive direction of
x
.
the
posi
tive direction of
x
to the positive direction of
y
.
A refactor is ongoing to make the three coordinate systems
A refactor is ongoing to make the three coordinate systems
easier to understand and convert between each other.
easier to understand and convert between each other.
...
@@ -36,10 +35,11 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -36,10 +35,11 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
with_yaw (bool): If True, the value of yaw will be set to 0 as minmax
boxes.
boxes.
"""
"""
YAW_AXIS
=
2
@
property
@
property
def
gravity_center
(
self
):
def
gravity_center
(
self
):
"""torch.Tensor: A tensor with center of each box."""
"""torch.Tensor: A tensor with center of each box
in shape (N, 3)
."""
bottom_center
=
self
.
bottom_center
bottom_center
=
self
.
bottom_center
gravity_center
=
torch
.
zeros_like
(
bottom_center
)
gravity_center
=
torch
.
zeros_like
(
bottom_center
)
gravity_center
[:,
:
2
]
=
bottom_center
[:,
:
2
]
gravity_center
[:,
:
2
]
=
bottom_center
[:,
:
2
]
...
@@ -83,70 +83,45 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -83,70 +83,45 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
corners
=
dims
.
view
([
-
1
,
1
,
3
])
*
corners_norm
.
reshape
([
1
,
8
,
3
])
# rotate around z axis
# 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
)
corners
+=
self
.
tensor
[:,
:
3
].
view
(
-
1
,
1
,
3
)
return
corners
return
corners
@
property
def
bev
(
self
):
"""torch.Tensor: 2D BEV box of each box with rotation
in XYWHR format."""
return
self
.
tensor
[:,
[
0
,
1
,
3
,
4
,
6
]]
@
property
def
nearest_bev
(
self
):
"""torch.Tensor: A tensor of 2D BEV box of each box
without rotation."""
# Obtain BEV boxes with rotation in XYWHR format
bev_rotated_boxes
=
self
.
bev
# convert the rotation to a valid range
rotations
=
bev_rotated_boxes
[:,
-
1
]
normed_rotations
=
torch
.
abs
(
limit_period
(
rotations
,
0.5
,
np
.
pi
))
# find the center of boxes
conditions
=
(
normed_rotations
>
np
.
pi
/
4
)[...,
None
]
bboxes_xywh
=
torch
.
where
(
conditions
,
bev_rotated_boxes
[:,
[
0
,
1
,
3
,
2
]],
bev_rotated_boxes
[:,
:
4
])
centers
=
bboxes_xywh
[:,
:
2
]
dims
=
bboxes_xywh
[:,
2
:]
bev_boxes
=
torch
.
cat
([
centers
-
dims
/
2
,
centers
+
dims
/
2
],
dim
=-
1
)
return
bev_boxes
def
rotate
(
self
,
angle
,
points
=
None
):
def
rotate
(
self
,
angle
,
points
=
None
):
"""Rotate boxes with points (optional) with the given angle or
\
"""Rotate boxes with points (optional) with the given angle or
rotation
rotation
matrix.
matrix.
Args:
Args:
angles (float | torch.Tensor | np.ndarray):
angles (float | torch.Tensor | np.ndarray):
Rotation angle or rotation matrix.
Rotation angle or rotation matrix.
points (torch.Tensor
, numpy
.ndarray
,
:obj:`BasePoints`, optional):
points (torch.Tensor
| np
.ndarray
|
:obj:`BasePoints`, optional):
Points to rotate. Defaults to None.
Points to rotate. Defaults to None.
Returns:
Returns:
tuple or None: When ``points`` is None, the function returns
\
tuple or None: When ``points`` is None, the function returns
None, otherwise it returns the rotated points and the
\
None, otherwise it returns the rotated points and the
rotation matrix ``rot_mat_T``.
rotation matrix ``rot_mat_T``.
"""
"""
if
not
isinstance
(
angle
,
torch
.
Tensor
):
if
not
isinstance
(
angle
,
torch
.
Tensor
):
angle
=
self
.
tensor
.
new_tensor
(
angle
)
angle
=
self
.
tensor
.
new_tensor
(
angle
)
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
assert
angle
.
shape
==
torch
.
Size
([
3
,
3
])
or
angle
.
numel
()
==
1
,
\
f
'invalid rotation angle shape
{
angle
.
shape
}
'
f
'invalid rotation angle shape
{
angle
.
shape
}
'
if
angle
.
numel
()
==
1
:
if
angle
.
numel
()
==
1
:
rot_sin
=
torch
.
sin
(
angle
)
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
rotation_3d_in_axis
(
rot_cos
=
torch
.
cos
(
angle
)
self
.
tensor
[:,
0
:
3
],
rot_mat_T
=
self
.
tensor
.
new_tensor
([[
rot_cos
,
-
rot_sin
,
0
]
,
angle
,
[
rot_sin
,
rot_cos
,
0
]
,
axis
=
self
.
YAW_AXIS
,
[
0
,
0
,
1
]]
)
return_mat
=
True
)
else
:
else
:
rot_mat_T
=
angle
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
]
rot_cos
=
rot_mat_T
[
0
,
0
]
angle
=
np
.
arctan2
(
rot_sin
,
rot_cos
)
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
self
.
tensor
[:,
6
]
+=
angle
if
self
.
tensor
.
shape
[
1
]
==
9
:
if
self
.
tensor
.
shape
[
1
]
==
9
:
...
@@ -157,11 +132,10 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -157,11 +132,10 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
if
isinstance
(
points
,
torch
.
Tensor
):
if
isinstance
(
points
,
torch
.
Tensor
):
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
points
[:,
:
3
]
=
points
[:,
:
3
]
@
rot_mat_T
elif
isinstance
(
points
,
np
.
ndarray
):
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
)
points
[:,
:
3
]
=
np
.
dot
(
points
[:,
:
3
],
rot_mat_T
)
elif
isinstance
(
points
,
BasePoints
):
elif
isinstance
(
points
,
BasePoints
):
# clockwise
points
.
rotate
(
rot_mat_T
)
points
.
rotate
(
-
angle
)
else
:
else
:
raise
ValueError
raise
ValueError
return
points
,
rot_mat_T
return
points
,
rot_mat_T
...
@@ -173,7 +147,7 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -173,7 +147,7 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
Args:
Args:
bev_direction (str): Flip direction (horizontal or vertical).
bev_direction (str): Flip direction (horizontal or vertical).
points (torch.Tensor
, numpy
.ndarray
,
:obj:`BasePoints`,
None
):
points (torch.Tensor
| np
.ndarray
|
:obj:`BasePoints`,
optional
):
Points to flip. Defaults to None.
Points to flip. Defaults to None.
Returns:
Returns:
...
@@ -183,11 +157,11 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -183,11 +157,11 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
if
bev_direction
==
'horizontal'
:
if
bev_direction
==
'horizontal'
:
self
.
tensor
[:,
1
::
7
]
=
-
self
.
tensor
[:,
1
::
7
]
self
.
tensor
[:,
1
::
7
]
=
-
self
.
tensor
[:,
1
::
7
]
if
self
.
with_yaw
:
if
self
.
with_yaw
:
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
+
np
.
pi
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
elif
bev_direction
==
'vertical'
:
elif
bev_direction
==
'vertical'
:
self
.
tensor
[:,
0
::
7
]
=
-
self
.
tensor
[:,
0
::
7
]
self
.
tensor
[:,
0
::
7
]
=
-
self
.
tensor
[:,
0
::
7
]
if
self
.
with_yaw
:
if
self
.
with_yaw
:
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
self
.
tensor
[:,
6
]
=
-
self
.
tensor
[:,
6
]
+
np
.
pi
if
points
is
not
None
:
if
points
is
not
None
:
assert
isinstance
(
points
,
(
torch
.
Tensor
,
np
.
ndarray
,
BasePoints
))
assert
isinstance
(
points
,
(
torch
.
Tensor
,
np
.
ndarray
,
BasePoints
))
...
@@ -200,40 +174,20 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -200,40 +174,20 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
points
.
flip
(
bev_direction
)
points
.
flip
(
bev_direction
)
return
points
return
points
def
in_range_bev
(
self
,
box_range
):
"""Check whether the boxes are in the given range.
Args:
box_range (list | torch.Tensor): the range of box
(x_min, y_min, x_max, y_max)
Note:
The original implementation of SECOND checks whether boxes in
a range by checking whether the points are in a convex
polygon, we reduce the burden for simpler cases.
Returns:
torch.Tensor: Whether each box is inside the reference range.
"""
in_range_flags
=
((
self
.
tensor
[:,
0
]
>
box_range
[
0
])
&
(
self
.
tensor
[:,
1
]
>
box_range
[
1
])
&
(
self
.
tensor
[:,
0
]
<
box_range
[
2
])
&
(
self
.
tensor
[:,
1
]
<
box_range
[
3
]))
return
in_range_flags
def
convert_to
(
self
,
dst
,
rt_mat
=
None
):
def
convert_to
(
self
,
dst
,
rt_mat
=
None
):
"""Convert self to ``dst`` mode.
"""Convert self to ``dst`` mode.
Args:
Args:
dst (:obj:`Box3DMode`): the target Box mode
dst (:obj:`Box3DMode`): the target Box mode
rt_mat (np.ndarray | torch.Tensor): The rotation and translation
rt_mat (np.ndarray | torch.Tensor, optional): The rotation and
matrix between different coordinates. Defaults to None.
translation matrix between different coordinates.
Defaults to None.
The conversion from ``src`` coordinates to ``dst`` coordinates
The conversion from ``src`` coordinates to ``dst`` coordinates
usually comes along the change of sensors, e.g., from camera
usually comes along the change of sensors, e.g., from camera
to LiDAR. This requires a transformation matrix.
to LiDAR. This requires a transformation matrix.
Returns:
Returns:
:obj:`BaseInstance3DBoxes`:
\
:obj:`BaseInstance3DBoxes`:
The converted box of the same type in the ``dst`` mode.
The converted box of the same type in the ``dst`` mode.
"""
"""
from
.box_3d_mode
import
Box3DMode
from
.box_3d_mode
import
Box3DMode
...
@@ -254,17 +208,3 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
...
@@ -254,17 +208,3 @@ class LiDARInstance3DBoxes(BaseInstance3DBoxes):
# bottom center z minus extra_width
# bottom center z minus extra_width
enlarged_boxes
[:,
2
]
-=
extra_width
enlarged_boxes
[:,
2
]
-=
extra_width
return
self
.
new_box
(
enlarged_boxes
)
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
mmdet3d/core/bbox/structures/utils.py
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved.
from
logging
import
warning
import
numpy
as
np
import
numpy
as
np
import
torch
import
torch
from
logging
import
warning
from
mmdet3d.core.utils
import
array_converter
@
array_converter
(
apply_to
=
(
'val'
,
))
def
limit_period
(
val
,
offset
=
0.5
,
period
=
np
.
pi
):
def
limit_period
(
val
,
offset
=
0.5
,
period
=
np
.
pi
):
"""Limit the value into a period for periodic function.
"""Limit the value into a period for periodic function.
Args:
Args:
val (torch.Tensor): The value to be converted.
val (torch.Tensor
| np.ndarray
): The value to be converted.
offset (float, optional): Offset to set the value range.
\
offset (float, optional): Offset to set the value range.
Defaults to 0.5.
Defaults to 0.5.
period ([type], optional): Period of the value. Defaults to np.pi.
period ([type], optional): Period of the value. Defaults to np.pi.
Returns:
Returns:
torch.Tensor: Value in the range of
\
(
torch.Tensor
| np.ndarray)
: Value in the range of
[-offset * period, (1-offset) * period]
[-offset * period, (1-offset) * period]
"""
"""
return
val
-
torch
.
floor
(
val
/
period
+
offset
)
*
period
limited_val
=
val
-
torch
.
floor
(
val
/
period
+
offset
)
*
period
return
limited_val
def
rotation_3d_in_axis
(
points
,
angles
,
axis
=
0
):
@
array_converter
(
apply_to
=
(
'points'
,
'angles'
))
def
rotation_3d_in_axis
(
points
,
angles
,
axis
=
0
,
return_mat
=
False
,
clockwise
=
False
):
"""Rotate points by angles according to axis.
"""Rotate points by angles according to axis.
Args:
Args:
points (torch.Tensor): Points of shape (N, M, 3).
points (np.ndarray | torch.Tensor | list | tuple ):
angles (torch.Tensor): Vector of angles in shape (N,)
Points of shape (N, M, 3).
angles (np.ndarray | torch.Tensor | list | tuple | float):
Vector of angles in shape (N,)
axis (int, optional): The axis to be rotated. Defaults to 0.
axis (int, optional): The axis to be rotated. Defaults to 0.
return_mat: Whether or not return the rotation matrix (transposed).
Defaults to False.
clockwise: Whether the rotation is clockwise. Defaults to False.
Raises:
Raises:
ValueError: when the axis is not in range [0, 1, 2], it will
\
ValueError: when the axis is not in range [0, 1, 2], it will
raise value error.
raise value error.
Returns:
Returns:
torch.Tensor: Rotated points in shape (N, M, 3)
(
torch.Tensor
| np.ndarray)
: Rotated points in shape (N, M, 3)
.
"""
"""
batch_free
=
len
(
points
.
shape
)
==
2
if
batch_free
:
points
=
points
[
None
]
if
isinstance
(
angles
,
float
)
or
len
(
angles
.
shape
)
==
0
:
angles
=
torch
.
full
(
points
.
shape
[:
1
],
angles
)
assert
len
(
points
.
shape
)
==
3
and
len
(
angles
.
shape
)
==
1
\
and
points
.
shape
[
0
]
==
angles
.
shape
[
0
],
f
'Incorrect shape of points '
\
f
'angles:
{
points
.
shape
}
,
{
angles
.
shape
}
'
assert
points
.
shape
[
-
1
]
in
[
2
,
3
],
\
f
'Points size should be 2 or 3 instead of
{
points
.
shape
[
-
1
]
}
'
rot_sin
=
torch
.
sin
(
angles
)
rot_sin
=
torch
.
sin
(
angles
)
rot_cos
=
torch
.
cos
(
angles
)
rot_cos
=
torch
.
cos
(
angles
)
ones
=
torch
.
ones_like
(
rot_cos
)
ones
=
torch
.
ones_like
(
rot_cos
)
zeros
=
torch
.
zeros_like
(
rot_cos
)
zeros
=
torch
.
zeros_like
(
rot_cos
)
if
axis
==
1
:
rot_mat_T
=
torch
.
stack
([
if
points
.
shape
[
-
1
]
==
3
:
torch
.
stack
([
rot_cos
,
zeros
,
-
rot_sin
]),
if
axis
==
1
or
axis
==
-
2
:
torch
.
stack
([
zeros
,
ones
,
zeros
]),
rot_mat_T
=
torch
.
stack
([
torch
.
stack
([
rot_sin
,
zeros
,
rot_cos
])
torch
.
stack
([
rot_cos
,
zeros
,
-
rot_sin
]),
])
torch
.
stack
([
zeros
,
ones
,
zeros
]),
elif
axis
==
2
or
axis
==
-
1
:
torch
.
stack
([
rot_sin
,
zeros
,
rot_cos
])
rot_mat_T
=
torch
.
stack
([
])
torch
.
stack
([
rot_cos
,
-
rot_sin
,
zeros
]),
elif
axis
==
2
or
axis
==
-
1
:
torch
.
stack
([
rot_sin
,
rot_cos
,
zeros
]),
rot_mat_T
=
torch
.
stack
([
torch
.
stack
([
zeros
,
zeros
,
ones
])
torch
.
stack
([
rot_cos
,
rot_sin
,
zeros
]),
])
torch
.
stack
([
-
rot_sin
,
rot_cos
,
zeros
]),
elif
axis
==
0
:
torch
.
stack
([
zeros
,
zeros
,
ones
])
])
elif
axis
==
0
or
axis
==
-
3
:
rot_mat_T
=
torch
.
stack
([
torch
.
stack
([
ones
,
zeros
,
zeros
]),
torch
.
stack
([
zeros
,
rot_cos
,
rot_sin
]),
torch
.
stack
([
zeros
,
-
rot_sin
,
rot_cos
])
])
else
:
raise
ValueError
(
f
'axis should in range '
f
'[-3, -2, -1, 0, 1, 2], got
{
axis
}
'
)
else
:
rot_mat_T
=
torch
.
stack
([
rot_mat_T
=
torch
.
stack
([
torch
.
stack
([
zeros
,
rot_cos
,
-
rot_sin
]),
torch
.
stack
([
rot_cos
,
rot_sin
]),
torch
.
stack
([
zeros
,
rot_sin
,
rot_cos
]),
torch
.
stack
([
-
rot_sin
,
rot_cos
])
torch
.
stack
([
ones
,
zeros
,
zeros
])
])
])
if
clockwise
:
rot_mat_T
=
rot_mat_T
.
transpose
(
0
,
1
)
if
points
.
shape
[
0
]
==
0
:
points_new
=
points
else
:
else
:
raise
ValueError
(
f
'axis should in range [0, 1, 2], got
{
axis
}
'
)
points_new
=
torch
.
einsum
(
'aij,jka->aik'
,
points
,
rot_mat_T
)
if
batch_free
:
points_new
=
points_new
.
squeeze
(
0
)
return
torch
.
einsum
(
'aij,jka->aik'
,
(
points
,
rot_mat_T
))
if
return_mat
:
rot_mat_T
=
torch
.
einsum
(
'jka->ajk'
,
rot_mat_T
)
if
batch_free
:
rot_mat_T
=
rot_mat_T
.
squeeze
(
0
)
return
points_new
,
rot_mat_T
else
:
return
points_new
@
array_converter
(
apply_to
=
(
'boxes_xywhr'
,
))
def
xywhr2xyxyr
(
boxes_xywhr
):
def
xywhr2xyxyr
(
boxes_xywhr
):
"""Convert a rotated boxes in XYWHR format to XYXYR format.
"""Convert a rotated boxes in XYWHR format to XYXYR format.
Args:
Args:
boxes_xywhr (torch.Tensor): Rotated boxes in XYWHR format.
boxes_xywhr (torch.Tensor
| np.ndarray
): Rotated boxes in XYWHR format.
Returns:
Returns:
torch.Tensor: Converted boxes in XYXYR format.
(
torch.Tensor
| np.ndarray)
: Converted boxes in XYXYR format.
"""
"""
boxes
=
torch
.
zeros_like
(
boxes_xywhr
)
boxes
=
torch
.
zeros_like
(
boxes_xywhr
)
half_w
=
boxes_xywhr
[
:
,
2
]
/
2
half_w
=
boxes_xywhr
[
...
,
2
]
/
2
half_h
=
boxes_xywhr
[
:
,
3
]
/
2
half_h
=
boxes_xywhr
[
...
,
3
]
/
2
boxes
[
:
,
0
]
=
boxes_xywhr
[
:
,
0
]
-
half_w
boxes
[
...
,
0
]
=
boxes_xywhr
[
...
,
0
]
-
half_w
boxes
[
:
,
1
]
=
boxes_xywhr
[
:
,
1
]
-
half_h
boxes
[
...
,
1
]
=
boxes_xywhr
[
...
,
1
]
-
half_h
boxes
[
:
,
2
]
=
boxes_xywhr
[
:
,
0
]
+
half_w
boxes
[
...
,
2
]
=
boxes_xywhr
[
...
,
0
]
+
half_w
boxes
[
:
,
3
]
=
boxes_xywhr
[
:
,
1
]
+
half_h
boxes
[
...
,
3
]
=
boxes_xywhr
[
...
,
1
]
+
half_h
boxes
[
:
,
4
]
=
boxes_xywhr
[
:
,
4
]
boxes
[
...
,
4
]
=
boxes_xywhr
[
...
,
4
]
return
boxes
return
boxes
...
@@ -91,6 +146,10 @@ def get_box_type(box_type):
...
@@ -91,6 +146,10 @@ def get_box_type(box_type):
box_type (str): The type of box structure.
box_type (str): The type of box structure.
The valid value are "LiDAR", "Camera", or "Depth".
The valid value are "LiDAR", "Camera", or "Depth".
Raises:
ValueError: A ValueError is raised when `box_type`
does not belong to the three valid types.
Returns:
Returns:
tuple: Box type and box mode.
tuple: Box type and box mode.
"""
"""
...
@@ -113,21 +172,24 @@ def get_box_type(box_type):
...
@@ -113,21 +172,24 @@ def get_box_type(box_type):
return
box_type_3d
,
box_mode_3d
return
box_type_3d
,
box_mode_3d
@
array_converter
(
apply_to
=
(
'points_3d'
,
'proj_mat'
))
def
points_cam2img
(
points_3d
,
proj_mat
,
with_depth
=
False
):
def
points_cam2img
(
points_3d
,
proj_mat
,
with_depth
=
False
):
"""Project points
from
camera coordi
c
ates to image coordinates.
"""Project points
in
camera coordi
n
ates to image coordinates.
Args:
Args:
points_3d (torch.Tensor): Points in shape (N, 3).
points_3d (torch.Tensor | np.ndarray): Points in shape (N, 3)
proj_mat (torch.Tensor): Transformation matrix between coordinates.
proj_mat (torch.Tensor | np.ndarray):
Transformation matrix between coordinates.
with_depth (bool, optional): Whether to keep depth in the output.
with_depth (bool, optional): Whether to keep depth in the output.
Defaults to False.
Defaults to False.
Returns:
Returns:
torch.Tensor: Points in image coordinates with shape [N, 2].
(torch.Tensor | np.ndarray): Points in image coordinates,
with shape [N, 2] if `with_depth=False`, else [N, 3].
"""
"""
points_num
=
list
(
points_3d
.
shape
)[:
-
1
]
points_shape
=
list
(
points_3d
.
shape
)
points_shape
[
-
1
]
=
1
points_shape
=
np
.
concatenate
([
points_num
,
[
1
]],
axis
=
0
).
tolist
()
assert
len
(
proj_mat
.
shape
)
==
2
,
'The dimension of the projection'
\
assert
len
(
proj_mat
.
shape
)
==
2
,
'The dimension of the projection'
\
f
' matrix should be 2 instead of
{
len
(
proj_mat
.
shape
)
}
.'
f
' matrix should be 2 instead of
{
len
(
proj_mat
.
shape
)
}
.'
d1
,
d2
=
proj_mat
.
shape
[:
2
]
d1
,
d2
=
proj_mat
.
shape
[:
2
]
...
@@ -140,17 +202,52 @@ def points_cam2img(points_3d, proj_mat, with_depth=False):
...
@@ -140,17 +202,52 @@ def points_cam2img(points_3d, proj_mat, with_depth=False):
proj_mat_expanded
[:
d1
,
:
d2
]
=
proj_mat
proj_mat_expanded
[:
d1
,
:
d2
]
=
proj_mat
proj_mat
=
proj_mat_expanded
proj_mat
=
proj_mat_expanded
# previous implementation use new_zeros, new_one y
e
ilds better results
# previous implementation use new_zeros, new_one yi
e
lds better results
points_4
=
torch
.
cat
(
points_4
=
torch
.
cat
(
[
points_3d
,
points_3d
.
new_ones
(
points_shape
)],
dim
=-
1
)
[
points_3d
,
points_3d
.
new_ones
(
*
points_shape
)],
dim
=-
1
)
point_2d
=
torch
.
matmul
(
points_4
,
proj_mat
.
t
())
point_2d
=
points_4
@
proj_mat
.
T
point_2d_res
=
point_2d
[...,
:
2
]
/
point_2d
[...,
2
:
3
]
point_2d_res
=
point_2d
[...,
:
2
]
/
point_2d
[...,
2
:
3
]
if
with_depth
:
if
with_depth
:
return
torch
.
cat
([
point_2d_res
,
point_2d
[...,
2
:
3
]],
dim
=-
1
)
point_2d_res
=
torch
.
cat
([
point_2d_res
,
point_2d
[...,
2
:
3
]],
dim
=-
1
)
return
point_2d_res
return
point_2d_res
@
array_converter
(
apply_to
=
(
'points'
,
'cam2img'
))
def
points_img2cam
(
points
,
cam2img
):
"""Project points in image coordinates to camera coordinates.
Args:
points (torch.Tensor): 2.5D points in 2D images, [N, 3],
3 corresponds with x, y in the image and depth.
cam2img (torch.Tensor): Camera intrinsic matrix. The shape can be
[3, 3], [3, 4] or [4, 4].
Returns:
torch.Tensor: points in 3D space. [N, 3],
3 corresponds with x, y, z in 3D space.
"""
assert
cam2img
.
shape
[
0
]
<=
4
assert
cam2img
.
shape
[
1
]
<=
4
assert
points
.
shape
[
1
]
==
3
xys
=
points
[:,
:
2
]
depths
=
points
[:,
2
].
view
(
-
1
,
1
)
unnormed_xys
=
torch
.
cat
([
xys
*
depths
,
depths
],
dim
=
1
)
pad_cam2img
=
torch
.
eye
(
4
,
dtype
=
xys
.
dtype
,
device
=
xys
.
device
)
pad_cam2img
[:
cam2img
.
shape
[
0
],
:
cam2img
.
shape
[
1
]]
=
cam2img
inv_pad_cam2img
=
torch
.
inverse
(
pad_cam2img
).
transpose
(
0
,
1
)
# Do operation in homogeneous coordinates.
num_points
=
unnormed_xys
.
shape
[
0
]
homo_xys
=
torch
.
cat
([
unnormed_xys
,
xys
.
new_ones
((
num_points
,
1
))],
dim
=
1
)
points3D
=
torch
.
mm
(
homo_xys
,
inv_pad_cam2img
)[:,
:
3
]
return
points3D
def
mono_cam_box2vis
(
cam_box
):
def
mono_cam_box2vis
(
cam_box
):
"""This is a post-processing function on the bboxes from Mono-3D task. If
"""This is a post-processing function on the bboxes from Mono-3D task. If
we want to perform projection visualization, we need to:
we want to perform projection visualization, we need to:
...
@@ -162,9 +259,9 @@ def mono_cam_box2vis(cam_box):
...
@@ -162,9 +259,9 @@ def mono_cam_box2vis(cam_box):
After applying this function, we can project and draw it on 2D images.
After applying this function, we can project and draw it on 2D images.
Args:
Args:
cam_box (:obj:`CameraInstance3DBoxes`): 3D bbox in camera coordinate
\
cam_box (:obj:`CameraInstance3DBoxes`): 3D bbox in camera coordinate
system before conversion. Could be gt bbox loaded from dataset
or
\
system before conversion. Could be gt bbox loaded from dataset
network prediction output.
or
network prediction output.
Returns:
Returns:
:obj:`CameraInstance3DBoxes`: Box after conversion.
:obj:`CameraInstance3DBoxes`: Box after conversion.
...
@@ -212,3 +309,27 @@ def get_proj_mat_by_coord_type(img_meta, coord_type):
...
@@ -212,3 +309,27 @@ def get_proj_mat_by_coord_type(img_meta, coord_type):
mapping
=
{
'LIDAR'
:
'lidar2img'
,
'DEPTH'
:
'depth2img'
,
'CAMERA'
:
'cam2img'
}
mapping
=
{
'LIDAR'
:
'lidar2img'
,
'DEPTH'
:
'depth2img'
,
'CAMERA'
:
'cam2img'
}
assert
coord_type
in
mapping
.
keys
()
assert
coord_type
in
mapping
.
keys
()
return
img_meta
[
mapping
[
coord_type
]]
return
img_meta
[
mapping
[
coord_type
]]
def
yaw2local
(
yaw
,
loc
):
"""Transform global yaw to local yaw (alpha in kitti) in camera
coordinates, ranges from -pi to pi.
Args:
yaw (torch.Tensor): A vector with local yaw of each box.
shape: (N, )
loc (torch.Tensor): gravity center of each box.
shape: (N, 3)
Returns:
torch.Tensor: local yaw (alpha in kitti).
"""
local_yaw
=
yaw
-
torch
.
atan2
(
loc
[:,
0
],
loc
[:,
2
])
larger_idx
=
(
local_yaw
>
np
.
pi
).
nonzero
(
as_tuple
=
False
)
small_idx
=
(
local_yaw
<
-
np
.
pi
).
nonzero
(
as_tuple
=
False
)
if
len
(
larger_idx
)
!=
0
:
local_yaw
[
larger_idx
]
-=
2
*
np
.
pi
if
len
(
small_idx
)
!=
0
:
local_yaw
[
small_idx
]
+=
2
*
np
.
pi
return
local_yaw
mmdet3d/core/bbox/transforms.py
View file @
32a4328b
...
@@ -32,7 +32,7 @@ def bbox3d2roi(bbox_list):
...
@@ -32,7 +32,7 @@ def bbox3d2roi(bbox_list):
corresponding to a batch of images.
corresponding to a batch of images.
Returns:
Returns:
torch.Tensor: Region of interests in shape (n, c), where
\
torch.Tensor: Region of interests in shape (n, c), where
the channels are in order of [batch_ind, x, y ...].
the channels are in order of [batch_ind, x, y ...].
"""
"""
rois_list
=
[]
rois_list
=
[]
...
@@ -51,10 +51,10 @@ def bbox3d2result(bboxes, scores, labels, attrs=None):
...
@@ -51,10 +51,10 @@ def bbox3d2result(bboxes, scores, labels, attrs=None):
"""Convert detection results to a list of numpy arrays.
"""Convert detection results to a list of numpy arrays.
Args:
Args:
bboxes (torch.Tensor): Bounding boxes with shape
of (n
, 5).
bboxes (torch.Tensor): Bounding boxes with shape
(N
, 5).
labels (torch.Tensor): Labels with shape
of (n
, ).
labels (torch.Tensor): Labels with shape
(N
, ).
scores (torch.Tensor): Scores with shape
of (n
, ).
scores (torch.Tensor): Scores with shape
(N
, ).
attrs (torch.Tensor, optional): Attributes with shape
of (n
, ).
\
attrs (torch.Tensor, optional): Attributes with shape
(N
, ).
Defaults to None.
Defaults to None.
Returns:
Returns:
...
...
mmdet3d/core/evaluation/indoor_eval.py
View file @
32a4328b
...
@@ -9,9 +9,9 @@ def average_precision(recalls, precisions, mode='area'):
...
@@ -9,9 +9,9 @@ def average_precision(recalls, precisions, mode='area'):
"""Calculate average precision (for single or multiple scales).
"""Calculate average precision (for single or multiple scales).
Args:
Args:
recalls (np.ndarray): Recalls with shape of (num_scales, num_dets)
\
recalls (np.ndarray): Recalls with shape of (num_scales, num_dets)
or (num_dets, ).
or (num_dets, ).
precisions (np.ndarray): Precisions with shape of
\
precisions (np.ndarray): Precisions with shape of
(num_scales, num_dets) or (num_dets, ).
(num_scales, num_dets) or (num_dets, ).
mode (str): 'area' or '11points', 'area' means calculating the area
mode (str): 'area' or '11points', 'area' means calculating the area
under precision-recall curve, '11points' means calculating
under precision-recall curve, '11points' means calculating
...
@@ -58,13 +58,13 @@ def eval_det_cls(pred, gt, iou_thr=None):
...
@@ -58,13 +58,13 @@ def eval_det_cls(pred, gt, iou_thr=None):
single class.
single class.
Args:
Args:
pred (dict): Predictions mapping from image id to bounding boxes
\
pred (dict): Predictions mapping from image id to bounding boxes
and scores.
and scores.
gt (dict): Ground truths mapping from image id to bounding boxes.
gt (dict): Ground truths mapping from image id to bounding boxes.
iou_thr (list[float]): A list of iou thresholds.
iou_thr (list[float]): A list of iou thresholds.
Return:
Return:
tuple (np.ndarray, np.ndarray, float): Recalls, precisions and
\
tuple (np.ndarray, np.ndarray, float): Recalls, precisions and
average precision.
average precision.
"""
"""
...
@@ -170,10 +170,9 @@ def eval_map_recall(pred, gt, ovthresh=None):
...
@@ -170,10 +170,9 @@ def eval_map_recall(pred, gt, ovthresh=None):
Args:
Args:
pred (dict): Information of detection results,
pred (dict): Information of detection results,
which maps class_id and predictions.
which maps class_id and predictions.
gt (dict): Information of ground truths, which maps class_id and
\
gt (dict): Information of ground truths, which maps class_id and
ground truths.
ground truths.
ovthresh (list[float]): iou threshold.
ovthresh (list[float], optional): iou threshold. Default: None.
Default: None.
Return:
Return:
tuple[dict]: dict results of recall, AP, and precision for all classes.
tuple[dict]: dict results of recall, AP, and precision for all classes.
...
@@ -218,12 +217,12 @@ def indoor_eval(gt_annos,
...
@@ -218,12 +217,12 @@ def indoor_eval(gt_annos,
includes the following keys
includes the following keys
- labels_3d (torch.Tensor): Labels of boxes.
- labels_3d (torch.Tensor): Labels of boxes.
- boxes_3d (:obj:`BaseInstance3DBoxes`):
\
- boxes_3d (:obj:`BaseInstance3DBoxes`):
3D bounding boxes in Depth coordinate.
3D bounding boxes in Depth coordinate.
- scores_3d (torch.Tensor): Scores of boxes.
- scores_3d (torch.Tensor): Scores of boxes.
metric (list[float]): IoU thresholds for computing average precisions.
metric (list[float]): IoU thresholds for computing average precisions.
label2cat (dict): Map from label to category.
label2cat (dict): Map from label to category.
logger (logging.Logger | str
| None
): The way to print the mAP
logger (logging.Logger | str
, optional
): The way to print the mAP
summary. See `mmdet.utils.print_log()` for details. Default: None.
summary. See `mmdet.utils.print_log()` for details. Default: None.
Return:
Return:
...
...
mmdet3d/core/evaluation/kitti_utils/eval.py
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved.
import
gc
import
gc
import
io
as
sysio
import
io
as
sysio
import
numba
import
numba
import
numpy
as
np
import
numpy
as
np
...
@@ -569,13 +570,20 @@ def eval_class(gt_annos,
...
@@ -569,13 +570,20 @@ def eval_class(gt_annos,
return
ret_dict
return
ret_dict
def
get_mAP
(
prec
):
def
get_mAP
11
(
prec
):
sums
=
0
sums
=
0
for
i
in
range
(
0
,
prec
.
shape
[
-
1
],
4
):
for
i
in
range
(
0
,
prec
.
shape
[
-
1
],
4
):
sums
=
sums
+
prec
[...,
i
]
sums
=
sums
+
prec
[...,
i
]
return
sums
/
11
*
100
return
sums
/
11
*
100
def
get_mAP40
(
prec
):
sums
=
0
for
i
in
range
(
1
,
prec
.
shape
[
-
1
]):
sums
=
sums
+
prec
[...,
i
]
return
sums
/
40
*
100
def
print_str
(
value
,
*
arg
,
sstream
=
None
):
def
print_str
(
value
,
*
arg
,
sstream
=
None
):
if
sstream
is
None
:
if
sstream
is
None
:
sstream
=
sysio
.
StringIO
()
sstream
=
sysio
.
StringIO
()
...
@@ -592,8 +600,10 @@ def do_eval(gt_annos,
...
@@ -592,8 +600,10 @@ def do_eval(gt_annos,
eval_types
=
[
'bbox'
,
'bev'
,
'3d'
]):
eval_types
=
[
'bbox'
,
'bev'
,
'3d'
]):
# min_overlaps: [num_minoverlap, metric, num_class]
# min_overlaps: [num_minoverlap, metric, num_class]
difficultys
=
[
0
,
1
,
2
]
difficultys
=
[
0
,
1
,
2
]
mAP_bbox
=
None
mAP11_bbox
=
None
mAP_aos
=
None
mAP11_aos
=
None
mAP40_bbox
=
None
mAP40_aos
=
None
if
'bbox'
in
eval_types
:
if
'bbox'
in
eval_types
:
ret
=
eval_class
(
ret
=
eval_class
(
gt_annos
,
gt_annos
,
...
@@ -604,22 +614,29 @@ def do_eval(gt_annos,
...
@@ -604,22 +614,29 @@ def do_eval(gt_annos,
min_overlaps
,
min_overlaps
,
compute_aos
=
(
'aos'
in
eval_types
))
compute_aos
=
(
'aos'
in
eval_types
))
# ret: [num_class, num_diff, num_minoverlap, num_sample_points]
# ret: [num_class, num_diff, num_minoverlap, num_sample_points]
mAP_bbox
=
get_mAP
(
ret
[
'precision'
])
mAP11_bbox
=
get_mAP11
(
ret
[
'precision'
])
mAP40_bbox
=
get_mAP40
(
ret
[
'precision'
])
if
'aos'
in
eval_types
:
if
'aos'
in
eval_types
:
mAP_aos
=
get_mAP
(
ret
[
'orientation'
])
mAP11_aos
=
get_mAP11
(
ret
[
'orientation'
])
mAP40_aos
=
get_mAP40
(
ret
[
'orientation'
])
mAP_bev
=
None
mAP11_bev
=
None
mAP40_bev
=
None
if
'bev'
in
eval_types
:
if
'bev'
in
eval_types
:
ret
=
eval_class
(
gt_annos
,
dt_annos
,
current_classes
,
difficultys
,
1
,
ret
=
eval_class
(
gt_annos
,
dt_annos
,
current_classes
,
difficultys
,
1
,
min_overlaps
)
min_overlaps
)
mAP_bev
=
get_mAP
(
ret
[
'precision'
])
mAP11_bev
=
get_mAP11
(
ret
[
'precision'
])
mAP40_bev
=
get_mAP40
(
ret
[
'precision'
])
mAP_3d
=
None
mAP11_3d
=
None
mAP40_3d
=
None
if
'3d'
in
eval_types
:
if
'3d'
in
eval_types
:
ret
=
eval_class
(
gt_annos
,
dt_annos
,
current_classes
,
difficultys
,
2
,
ret
=
eval_class
(
gt_annos
,
dt_annos
,
current_classes
,
difficultys
,
2
,
min_overlaps
)
min_overlaps
)
mAP_3d
=
get_mAP
(
ret
[
'precision'
])
mAP11_3d
=
get_mAP11
(
ret
[
'precision'
])
return
mAP_bbox
,
mAP_bev
,
mAP_3d
,
mAP_aos
mAP40_3d
=
get_mAP40
(
ret
[
'precision'
])
return
(
mAP11_bbox
,
mAP11_bev
,
mAP11_3d
,
mAP11_aos
,
mAP40_bbox
,
mAP40_bev
,
mAP40_3d
,
mAP40_aos
)
def
do_coco_style_eval
(
gt_annos
,
dt_annos
,
current_classes
,
overlap_ranges
,
def
do_coco_style_eval
(
gt_annos
,
dt_annos
,
current_classes
,
overlap_ranges
,
...
@@ -629,9 +646,10 @@ def do_coco_style_eval(gt_annos, dt_annos, current_classes, overlap_ranges,
...
@@ -629,9 +646,10 @@ def do_coco_style_eval(gt_annos, dt_annos, current_classes, overlap_ranges,
for
i
in
range
(
overlap_ranges
.
shape
[
1
]):
for
i
in
range
(
overlap_ranges
.
shape
[
1
]):
for
j
in
range
(
overlap_ranges
.
shape
[
2
]):
for
j
in
range
(
overlap_ranges
.
shape
[
2
]):
min_overlaps
[:,
i
,
j
]
=
np
.
linspace
(
*
overlap_ranges
[:,
i
,
j
])
min_overlaps
[:,
i
,
j
]
=
np
.
linspace
(
*
overlap_ranges
[:,
i
,
j
])
mAP_bbox
,
mAP_bev
,
mAP_3d
,
mAP_aos
=
do_eval
(
gt_annos
,
dt_annos
,
mAP_bbox
,
mAP_bev
,
mAP_3d
,
mAP_aos
,
_
,
_
,
\
current_classes
,
min_overlaps
,
_
,
_
=
do_eval
(
gt_annos
,
dt_annos
,
compute_aos
)
current_classes
,
min_overlaps
,
compute_aos
)
# ret: [num_class, num_diff, num_minoverlap]
# ret: [num_class, num_diff, num_minoverlap]
mAP_bbox
=
mAP_bbox
.
mean
(
-
1
)
mAP_bbox
=
mAP_bbox
.
mean
(
-
1
)
mAP_bev
=
mAP_bev
.
mean
(
-
1
)
mAP_bev
=
mAP_bev
.
mean
(
-
1
)
...
@@ -703,33 +721,109 @@ def kitti_eval(gt_annos,
...
@@ -703,33 +721,109 @@ def kitti_eval(gt_annos,
if
compute_aos
:
if
compute_aos
:
eval_types
.
append
(
'aos'
)
eval_types
.
append
(
'aos'
)
mAPbbox
,
mAPbev
,
mAP3d
,
mAPaos
=
do_eval
(
gt_annos
,
dt_annos
,
mAP11_bbox
,
mAP11_bev
,
mAP11_3d
,
mAP11_aos
,
mAP40_bbox
,
mAP40_bev
,
\
current_classes
,
min_overlaps
,
mAP40_3d
,
mAP40_aos
=
do_eval
(
gt_annos
,
dt_annos
,
eval_types
)
current_classes
,
min_overlaps
,
eval_types
)
ret_dict
=
{}
ret_dict
=
{}
difficulty
=
[
'easy'
,
'moderate'
,
'hard'
]
difficulty
=
[
'easy'
,
'moderate'
,
'hard'
]
# calculate AP11
result
+=
'
\n
----------- AP11 Results ------------
\n\n
'
for
j
,
curcls
in
enumerate
(
current_classes
):
for
j
,
curcls
in
enumerate
(
current_classes
):
# mAP threshold array: [num_minoverlap, metric, class]
# mAP threshold array: [num_minoverlap, metric, class]
# mAP result: [num_class, num_diff, num_minoverlap]
# mAP result: [num_class, num_diff, num_minoverlap]
curcls_name
=
class_to_name
[
curcls
]
curcls_name
=
class_to_name
[
curcls
]
for
i
in
range
(
min_overlaps
.
shape
[
0
]):
for
i
in
range
(
min_overlaps
.
shape
[
0
]):
# prepare results for print
# prepare results for print
result
+=
(
'{} AP@{:.2f}, {:.2f}, {:.2f}:
\n
'
.
format
(
result
+=
(
'{} AP
11
@{:.2f}, {:.2f}, {:.2f}:
\n
'
.
format
(
curcls_name
,
*
min_overlaps
[
i
,
:,
j
]))
curcls_name
,
*
min_overlaps
[
i
,
:,
j
]))
if
mAPbbox
is
not
None
:
if
mAP11_bbox
is
not
None
:
result
+=
'bbox AP:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
result
+=
'bbox AP11:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAPbbox
[
j
,
:,
i
])
*
mAP11_bbox
[
j
,
:,
i
])
if
mAPbev
is
not
None
:
if
mAP11_bev
is
not
None
:
result
+=
'bev AP:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
result
+=
'bev AP11:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAPbev
[
j
,
:,
i
])
*
mAP11_bev
[
j
,
:,
i
])
if
mAP3d
is
not
None
:
if
mAP11_3d
is
not
None
:
result
+=
'3d AP:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
result
+=
'3d AP11:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP3d
[
j
,
:,
i
])
*
mAP11_3d
[
j
,
:,
i
])
if
compute_aos
:
result
+=
'aos AP11:{:.2f}, {:.2f}, {:.2f}
\n
'
.
format
(
*
mAP11_aos
[
j
,
:,
i
])
# prepare results for logger
for
idx
in
range
(
3
):
if
i
==
0
:
postfix
=
f
'
{
difficulty
[
idx
]
}
_strict'
else
:
postfix
=
f
'
{
difficulty
[
idx
]
}
_loose'
prefix
=
f
'KITTI/
{
curcls_name
}
'
if
mAP11_3d
is
not
None
:
ret_dict
[
f
'
{
prefix
}
_3D_AP11_
{
postfix
}
'
]
=
\
mAP11_3d
[
j
,
idx
,
i
]
if
mAP11_bev
is
not
None
:
ret_dict
[
f
'
{
prefix
}
_BEV_AP11_
{
postfix
}
'
]
=
\
mAP11_bev
[
j
,
idx
,
i
]
if
mAP11_bbox
is
not
None
:
ret_dict
[
f
'
{
prefix
}
_2D_AP11_
{
postfix
}
'
]
=
\
mAP11_bbox
[
j
,
idx
,
i
]
# calculate mAP11 over all classes if there are multiple classes
if
len
(
current_classes
)
>
1
:
# prepare results for print
result
+=
(
'
\n
Overall AP11@{}, {}, {}:
\n
'
.
format
(
*
difficulty
))
if
mAP11_bbox
is
not
None
:
mAP11_bbox
=
mAP11_bbox
.
mean
(
axis
=
0
)
result
+=
'bbox AP11:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP11_bbox
[:,
0
])
if
mAP11_bev
is
not
None
:
mAP11_bev
=
mAP11_bev
.
mean
(
axis
=
0
)
result
+=
'bev AP11:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP11_bev
[:,
0
])
if
mAP11_3d
is
not
None
:
mAP11_3d
=
mAP11_3d
.
mean
(
axis
=
0
)
result
+=
'3d AP11:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP11_3d
[:,
0
])
if
compute_aos
:
mAP11_aos
=
mAP11_aos
.
mean
(
axis
=
0
)
result
+=
'aos AP11:{:.2f}, {:.2f}, {:.2f}
\n
'
.
format
(
*
mAP11_aos
[:,
0
])
# prepare results for logger
for
idx
in
range
(
3
):
postfix
=
f
'
{
difficulty
[
idx
]
}
'
if
mAP11_3d
is
not
None
:
ret_dict
[
f
'KITTI/Overall_3D_AP11_
{
postfix
}
'
]
=
mAP11_3d
[
idx
,
0
]
if
mAP11_bev
is
not
None
:
ret_dict
[
f
'KITTI/Overall_BEV_AP11_
{
postfix
}
'
]
=
\
mAP11_bev
[
idx
,
0
]
if
mAP11_bbox
is
not
None
:
ret_dict
[
f
'KITTI/Overall_2D_AP11_
{
postfix
}
'
]
=
\
mAP11_bbox
[
idx
,
0
]
# Calculate AP40
result
+=
'
\n
----------- AP40 Results ------------
\n\n
'
for
j
,
curcls
in
enumerate
(
current_classes
):
# mAP threshold array: [num_minoverlap, metric, class]
# mAP result: [num_class, num_diff, num_minoverlap]
curcls_name
=
class_to_name
[
curcls
]
for
i
in
range
(
min_overlaps
.
shape
[
0
]):
# prepare results for print
result
+=
(
'{} AP40@{:.2f}, {:.2f}, {:.2f}:
\n
'
.
format
(
curcls_name
,
*
min_overlaps
[
i
,
:,
j
]))
if
mAP40_bbox
is
not
None
:
result
+=
'bbox AP40:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP40_bbox
[
j
,
:,
i
])
if
mAP40_bev
is
not
None
:
result
+=
'bev AP40:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP40_bev
[
j
,
:,
i
])
if
mAP40_3d
is
not
None
:
result
+=
'3d AP40:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP40_3d
[
j
,
:,
i
])
if
compute_aos
:
if
compute_aos
:
result
+=
'aos AP:{:.2f}, {:.2f}, {:.2f}
\n
'
.
format
(
result
+=
'aos AP
40
:{:.2f}, {:.2f}, {:.2f}
\n
'
.
format
(
*
mAPaos
[
j
,
:,
i
])
*
mAP
40_
aos
[
j
,
:,
i
])
# prepare results for logger
# prepare results for logger
for
idx
in
range
(
3
):
for
idx
in
range
(
3
):
...
@@ -738,39 +832,48 @@ def kitti_eval(gt_annos,
...
@@ -738,39 +832,48 @@ def kitti_eval(gt_annos,
else
:
else
:
postfix
=
f
'
{
difficulty
[
idx
]
}
_loose'
postfix
=
f
'
{
difficulty
[
idx
]
}
_loose'
prefix
=
f
'KITTI/
{
curcls_name
}
'
prefix
=
f
'KITTI/
{
curcls_name
}
'
if
mAP3d
is
not
None
:
if
mAP40_3d
is
not
None
:
ret_dict
[
f
'
{
prefix
}
_3D_
{
postfix
}
'
]
=
mAP3d
[
j
,
idx
,
i
]
ret_dict
[
f
'
{
prefix
}
_3D_AP40_
{
postfix
}
'
]
=
\
if
mAPbev
is
not
None
:
mAP40_3d
[
j
,
idx
,
i
]
ret_dict
[
f
'
{
prefix
}
_BEV_
{
postfix
}
'
]
=
mAPbev
[
j
,
idx
,
i
]
if
mAP40_bev
is
not
None
:
if
mAPbbox
is
not
None
:
ret_dict
[
f
'
{
prefix
}
_BEV_AP40_
{
postfix
}
'
]
=
\
ret_dict
[
f
'
{
prefix
}
_2D_
{
postfix
}
'
]
=
mAPbbox
[
j
,
idx
,
i
]
mAP40_bev
[
j
,
idx
,
i
]
if
mAP40_bbox
is
not
None
:
# calculate mAP over all classes if there are multiple classes
ret_dict
[
f
'
{
prefix
}
_2D_AP40_
{
postfix
}
'
]
=
\
mAP40_bbox
[
j
,
idx
,
i
]
# calculate mAP40 over all classes if there are multiple classes
if
len
(
current_classes
)
>
1
:
if
len
(
current_classes
)
>
1
:
# prepare results for print
# prepare results for print
result
+=
(
'
\n
Overall AP@{}, {}, {}:
\n
'
.
format
(
*
difficulty
))
result
+=
(
'
\n
Overall AP40@{}, {}, {}:
\n
'
.
format
(
*
difficulty
))
if
mAPbbox
is
not
None
:
if
mAP40_bbox
is
not
None
:
mAPbbox
=
mAPbbox
.
mean
(
axis
=
0
)
mAP40_bbox
=
mAP40_bbox
.
mean
(
axis
=
0
)
result
+=
'bbox AP:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAPbbox
[:,
0
])
result
+=
'bbox AP40:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
if
mAPbev
is
not
None
:
*
mAP40_bbox
[:,
0
])
mAPbev
=
mAPbev
.
mean
(
axis
=
0
)
if
mAP40_bev
is
not
None
:
result
+=
'bev AP:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAPbev
[:,
0
])
mAP40_bev
=
mAP40_bev
.
mean
(
axis
=
0
)
if
mAP3d
is
not
None
:
result
+=
'bev AP40:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
mAP3d
=
mAP3d
.
mean
(
axis
=
0
)
*
mAP40_bev
[:,
0
])
result
+=
'3d AP:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP3d
[:,
0
])
if
mAP40_3d
is
not
None
:
mAP40_3d
=
mAP40_3d
.
mean
(
axis
=
0
)
result
+=
'3d AP40:{:.4f}, {:.4f}, {:.4f}
\n
'
.
format
(
*
mAP40_3d
[:,
0
])
if
compute_aos
:
if
compute_aos
:
mAPaos
=
mAPaos
.
mean
(
axis
=
0
)
mAP40_aos
=
mAP40_aos
.
mean
(
axis
=
0
)
result
+=
'aos AP:{:.2f}, {:.2f}, {:.2f}
\n
'
.
format
(
*
mAPaos
[:,
0
])
result
+=
'aos AP40:{:.2f}, {:.2f}, {:.2f}
\n
'
.
format
(
*
mAP40_aos
[:,
0
])
# prepare results for logger
# prepare results for logger
for
idx
in
range
(
3
):
for
idx
in
range
(
3
):
postfix
=
f
'
{
difficulty
[
idx
]
}
'
postfix
=
f
'
{
difficulty
[
idx
]
}
'
if
mAP3d
is
not
None
:
if
mAP40_3d
is
not
None
:
ret_dict
[
f
'KITTI/Overall_3D_
{
postfix
}
'
]
=
mAP3d
[
idx
,
0
]
ret_dict
[
f
'KITTI/Overall_3D_AP40_
{
postfix
}
'
]
=
mAP40_3d
[
idx
,
0
]
if
mAPbev
is
not
None
:
if
mAP40_bev
is
not
None
:
ret_dict
[
f
'KITTI/Overall_BEV_
{
postfix
}
'
]
=
mAPbev
[
idx
,
0
]
ret_dict
[
f
'KITTI/Overall_BEV_AP40_
{
postfix
}
'
]
=
\
if
mAPbbox
is
not
None
:
mAP40_bev
[
idx
,
0
]
ret_dict
[
f
'KITTI/Overall_2D_
{
postfix
}
'
]
=
mAPbbox
[
idx
,
0
]
if
mAP40_bbox
is
not
None
:
ret_dict
[
f
'KITTI/Overall_2D_AP40_
{
postfix
}
'
]
=
\
mAP40_bbox
[
idx
,
0
]
return
result
,
ret_dict
return
result
,
ret_dict
...
...
mmdet3d/core/evaluation/kitti_utils/rotate_iou.py
View file @
32a4328b
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
# Author: yanyan, scrin@foxmail.com
# Author: yanyan, scrin@foxmail.com
#####################
#####################
import
math
import
math
import
numba
import
numba
import
numpy
as
np
import
numpy
as
np
from
numba
import
cuda
from
numba
import
cuda
...
@@ -15,13 +16,13 @@ def div_up(m, n):
...
@@ -15,13 +16,13 @@ def div_up(m, n):
return
m
//
n
+
(
m
%
n
>
0
)
return
m
//
n
+
(
m
%
n
>
0
)
@
cuda
.
jit
(
'(float32[:], float32[:], float32[:])'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
trangle_area
(
a
,
b
,
c
):
def
trangle_area
(
a
,
b
,
c
):
return
((
a
[
0
]
-
c
[
0
])
*
(
b
[
1
]
-
c
[
1
])
-
(
a
[
1
]
-
c
[
1
])
*
return
((
a
[
0
]
-
c
[
0
])
*
(
b
[
1
]
-
c
[
1
])
-
(
a
[
1
]
-
c
[
1
])
*
(
b
[
0
]
-
c
[
0
]))
/
2.0
(
b
[
0
]
-
c
[
0
]))
/
2.0
@
cuda
.
jit
(
'(float32[:], int32)'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
area
(
int_pts
,
num_of_inter
):
def
area
(
int_pts
,
num_of_inter
):
area_val
=
0.0
area_val
=
0.0
for
i
in
range
(
num_of_inter
-
2
):
for
i
in
range
(
num_of_inter
-
2
):
...
@@ -31,7 +32,7 @@ def area(int_pts, num_of_inter):
...
@@ -31,7 +32,7 @@ def area(int_pts, num_of_inter):
return
area_val
return
area_val
@
cuda
.
jit
(
'(float32[:], int32)'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
sort_vertex_in_convex_polygon
(
int_pts
,
num_of_inter
):
def
sort_vertex_in_convex_polygon
(
int_pts
,
num_of_inter
):
if
num_of_inter
>
0
:
if
num_of_inter
>
0
:
center
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
center
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
...
@@ -71,10 +72,7 @@ def sort_vertex_in_convex_polygon(int_pts, num_of_inter):
...
@@ -71,10 +72,7 @@ def sort_vertex_in_convex_polygon(int_pts, num_of_inter):
int_pts
[
j
*
2
+
1
]
=
ty
int_pts
[
j
*
2
+
1
]
=
ty
@
cuda
.
jit
(
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
'(float32[:], float32[:], int32, int32, float32[:])'
,
device
=
True
,
inline
=
True
)
def
line_segment_intersection
(
pts1
,
pts2
,
i
,
j
,
temp_pts
):
def
line_segment_intersection
(
pts1
,
pts2
,
i
,
j
,
temp_pts
):
A
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
A
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
B
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
B
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
...
@@ -117,10 +115,7 @@ def line_segment_intersection(pts1, pts2, i, j, temp_pts):
...
@@ -117,10 +115,7 @@ def line_segment_intersection(pts1, pts2, i, j, temp_pts):
return
False
return
False
@
cuda
.
jit
(
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
'(float32[:], float32[:], int32, int32, float32[:])'
,
device
=
True
,
inline
=
True
)
def
line_segment_intersection_v1
(
pts1
,
pts2
,
i
,
j
,
temp_pts
):
def
line_segment_intersection_v1
(
pts1
,
pts2
,
i
,
j
,
temp_pts
):
a
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
a
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
b
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
b
=
cuda
.
local
.
array
((
2
,
),
dtype
=
numba
.
float32
)
...
@@ -159,7 +154,7 @@ def line_segment_intersection_v1(pts1, pts2, i, j, temp_pts):
...
@@ -159,7 +154,7 @@ def line_segment_intersection_v1(pts1, pts2, i, j, temp_pts):
return
True
return
True
@
cuda
.
jit
(
'(float32, float32, float32[:])'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
point_in_quadrilateral
(
pt_x
,
pt_y
,
corners
):
def
point_in_quadrilateral
(
pt_x
,
pt_y
,
corners
):
ab0
=
corners
[
2
]
-
corners
[
0
]
ab0
=
corners
[
2
]
-
corners
[
0
]
ab1
=
corners
[
3
]
-
corners
[
1
]
ab1
=
corners
[
3
]
-
corners
[
1
]
...
@@ -178,7 +173,7 @@ def point_in_quadrilateral(pt_x, pt_y, corners):
...
@@ -178,7 +173,7 @@ def point_in_quadrilateral(pt_x, pt_y, corners):
return
abab
>=
abap
and
abap
>=
0
and
adad
>=
adap
and
adap
>=
0
return
abab
>=
abap
and
abap
>=
0
and
adad
>=
adap
and
adap
>=
0
@
cuda
.
jit
(
'(float32[:], float32[:], float32[:])'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
quadrilateral_intersection
(
pts1
,
pts2
,
int_pts
):
def
quadrilateral_intersection
(
pts1
,
pts2
,
int_pts
):
num_of_inter
=
0
num_of_inter
=
0
for
i
in
range
(
4
):
for
i
in
range
(
4
):
...
@@ -202,7 +197,7 @@ def quadrilateral_intersection(pts1, pts2, int_pts):
...
@@ -202,7 +197,7 @@ def quadrilateral_intersection(pts1, pts2, int_pts):
return
num_of_inter
return
num_of_inter
@
cuda
.
jit
(
'(float32[:], float32[:])'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
rbbox_to_corners
(
corners
,
rbbox
):
def
rbbox_to_corners
(
corners
,
rbbox
):
# generate clockwise corners and rotate it clockwise
# generate clockwise corners and rotate it clockwise
angle
=
rbbox
[
4
]
angle
=
rbbox
[
4
]
...
@@ -228,7 +223,7 @@ def rbbox_to_corners(corners, rbbox):
...
@@ -228,7 +223,7 @@ def rbbox_to_corners(corners, rbbox):
1
]
=
-
a_sin
*
corners_x
[
i
]
+
a_cos
*
corners_y
[
i
]
+
center_y
1
]
=
-
a_sin
*
corners_x
[
i
]
+
a_cos
*
corners_y
[
i
]
+
center_y
@
cuda
.
jit
(
'(float32[:], float32[:])'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
inter
(
rbbox1
,
rbbox2
):
def
inter
(
rbbox1
,
rbbox2
):
"""Compute intersection of two rotated boxes.
"""Compute intersection of two rotated boxes.
...
@@ -254,7 +249,7 @@ def inter(rbbox1, rbbox2):
...
@@ -254,7 +249,7 @@ def inter(rbbox1, rbbox2):
return
area
(
intersection_corners
,
num_intersection
)
return
area
(
intersection_corners
,
num_intersection
)
@
cuda
.
jit
(
'(float32[:], float32[:], int32)'
,
device
=
True
,
inline
=
True
)
@
cuda
.
jit
(
device
=
True
,
inline
=
True
)
def
devRotateIoUEval
(
rbox1
,
rbox2
,
criterion
=-
1
):
def
devRotateIoUEval
(
rbox1
,
rbox2
,
criterion
=-
1
):
"""Compute rotated iou on device.
"""Compute rotated iou on device.
...
@@ -291,7 +286,8 @@ def rotate_iou_kernel_eval(N,
...
@@ -291,7 +286,8 @@ def rotate_iou_kernel_eval(N,
dev_query_boxes
,
dev_query_boxes
,
dev_iou
,
dev_iou
,
criterion
=-
1
):
criterion
=-
1
):
"""Kernel of computing rotated iou.
"""Kernel of computing rotated IoU. This function is for bev boxes in
camera coordinate system ONLY (the rotation is clockwise).
Args:
Args:
N (int): The number of boxes.
N (int): The number of boxes.
...
@@ -343,10 +339,14 @@ def rotate_iou_gpu_eval(boxes, query_boxes, criterion=-1, device_id=0):
...
@@ -343,10 +339,14 @@ def rotate_iou_gpu_eval(boxes, query_boxes, criterion=-1, device_id=0):
in one example with numba.cuda code). convert from [this project](
in one example with numba.cuda code). convert from [this project](
https://github.com/hongzhenwang/RRPN-revise/tree/master/lib/rotation).
https://github.com/hongzhenwang/RRPN-revise/tree/master/lib/rotation).
This function is for bev boxes in camera coordinate system ONLY
(the rotation is clockwise).
Args:
Args:
boxes (torch.Tensor): rbboxes. format: centers, dims,
boxes (torch.Tensor): rbboxes. format: centers, dims,
angles(clockwise when positive) with the shape of [N, 5].
angles(clockwise when positive) with the shape of [N, 5].
query_boxes (float tensor: [K, 5]): rbboxes to compute iou with boxes.
query_boxes (torch.FloatTensor, shape=(K, 5)):
rbboxes to compute iou with boxes.
device_id (int, optional): Defaults to 0. Device to use.
device_id (int, optional): Defaults to 0. Device to use.
criterion (int, optional): Indicate different type of iou.
criterion (int, optional): Indicate different type of iou.
-1 indicate `area_inter / (area1 + area2 - area_inter)`,
-1 indicate `area_inter / (area1 + area2 - area_inter)`,
...
...
mmdet3d/core/evaluation/lyft_eval.py
View file @
32a4328b
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved.
from
os
import
path
as
osp
import
mmcv
import
mmcv
import
numpy
as
np
import
numpy
as
np
from
lyft_dataset_sdk.eval.detection.mAP_evaluation
import
(
Box3D
,
get_ap
,
from
lyft_dataset_sdk.eval.detection.mAP_evaluation
import
(
Box3D
,
get_ap
,
...
@@ -7,7 +9,6 @@ from lyft_dataset_sdk.eval.detection.mAP_evaluation import (Box3D, get_ap,
...
@@ -7,7 +9,6 @@ from lyft_dataset_sdk.eval.detection.mAP_evaluation import (Box3D, get_ap,
group_by_key
,
group_by_key
,
wrap_in_box
)
wrap_in_box
)
from
mmcv.utils
import
print_log
from
mmcv.utils
import
print_log
from
os
import
path
as
osp
from
terminaltables
import
AsciiTable
from
terminaltables
import
AsciiTable
...
@@ -18,7 +19,7 @@ def load_lyft_gts(lyft, data_root, eval_split, logger=None):
...
@@ -18,7 +19,7 @@ def load_lyft_gts(lyft, data_root, eval_split, logger=None):
lyft (:obj:`LyftDataset`): Lyft class in the sdk.
lyft (:obj:`LyftDataset`): Lyft class in the sdk.
data_root (str): Root of data for reading splits.
data_root (str): Root of data for reading splits.
eval_split (str): Name of the split for evaluation.
eval_split (str): Name of the split for evaluation.
logger (logging.Logger | str
| None
): Logger used for printing
logger (logging.Logger | str
, optional
): Logger used for printing
related information during evaluation. Default: None.
related information during evaluation. Default: None.
Returns:
Returns:
...
@@ -96,7 +97,7 @@ def lyft_eval(lyft, data_root, res_path, eval_set, output_dir, logger=None):
...
@@ -96,7 +97,7 @@ def lyft_eval(lyft, data_root, res_path, eval_set, output_dir, logger=None):
res_path (str): Path of result json file recording detections.
res_path (str): Path of result json file recording detections.
eval_set (str): Name of the split for evaluation.
eval_set (str): Name of the split for evaluation.
output_dir (str): Output directory for output json files.
output_dir (str): Output directory for output json files.
logger (logging.Logger | str
| None
): Logger used for printing
logger (logging.Logger | str
, optional
): Logger used for printing
related information during evaluation. Default: None.
related information during evaluation. Default: None.
Returns:
Returns:
...
@@ -202,9 +203,9 @@ def get_single_class_aps(gt, predictions, iou_thresholds):
...
@@ -202,9 +203,9 @@ def get_single_class_aps(gt, predictions, iou_thresholds):
Args:
Args:
gt (list[dict]): list of dictionaries in the format described above.
gt (list[dict]): list of dictionaries in the format described above.
predictions (list[dict]): list of dictionaries in the format
\
predictions (list[dict]): list of dictionaries in the format
described below.
described below.
iou_thresholds (list[float]): IOU thresholds used to calculate
\
iou_thresholds (list[float]): IOU thresholds used to calculate
TP / FN
TP / FN
Returns:
Returns:
...
...
mmdet3d/core/evaluation/seg_eval.py
View file @
32a4328b
...
@@ -77,7 +77,7 @@ def seg_eval(gt_labels, seg_preds, label2cat, ignore_index, logger=None):
...
@@ -77,7 +77,7 @@ def seg_eval(gt_labels, seg_preds, label2cat, ignore_index, logger=None):
seg_preds (list[torch.Tensor]): Predictions.
seg_preds (list[torch.Tensor]): Predictions.
label2cat (dict): Map from label to category name.
label2cat (dict): Map from label to category name.
ignore_index (int): Index that will be ignored in evaluation.
ignore_index (int): Index that will be ignored in evaluation.
logger (logging.Logger | str
| None
): The way to print the mAP
logger (logging.Logger | str
, optional
): The way to print the mAP
summary. See `mmdet.utils.print_log()` for details. Default: None.
summary. See `mmdet.utils.print_log()` for details. Default: None.
Returns:
Returns:
...
...
Prev
1
…
4
5
6
7
8
9
10
11
12
…
21
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