Unverified Commit 32a4328b authored by Wenwei Zhang's avatar Wenwei Zhang Committed by GitHub
Browse files

Bump version to V1.0.0rc0

Bump version to V1.0.0rc0
parents 86cc487c a8817998
...@@ -10,11 +10,12 @@ except ImportError: ...@@ -10,11 +10,12 @@ except ImportError:
'Please run "pip install waymo-open-dataset-tf-2-1-0==1.2.0" ' 'Please run "pip install waymo-open-dataset-tf-2-1-0==1.2.0" '
'to install the official devkit first.') 'to install the official devkit first.')
from glob import glob
from os.path import join
import mmcv import mmcv
import numpy as np import numpy as np
import tensorflow as tf import tensorflow as tf
from glob import glob
from os.path import join
from waymo_open_dataset import label_pb2 from waymo_open_dataset import label_pb2
from waymo_open_dataset.protos import metrics_pb2 from waymo_open_dataset.protos import metrics_pb2
...@@ -114,7 +115,7 @@ class KITTI2Waymo(object): ...@@ -114,7 +115,7 @@ class KITTI2Waymo(object):
instance_idx (int): Index of the instance to be converted. instance_idx (int): Index of the instance to be converted.
Returns: Returns:
:obj:`Object`: Predicted instance in waymo dataset \ :obj:`Object`: Predicted instance in waymo dataset
Object proto. Object proto.
""" """
cls = kitti_result['name'][instance_idx] cls = kitti_result['name'][instance_idx]
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import numpy as np
import torch
import warnings import warnings
from abc import abstractmethod from abc import abstractmethod
import numpy as np
import torch
from ..bbox.structures.utils import rotation_3d_in_axis
class BasePoints(object): class BasePoints(object):
"""Base class for Points. """Base class for Points.
Args: Args:
tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix. tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix.
points_dim (int): Number of the dimension of a point. points_dim (int, optional): Number of the dimension of a point.
Each row is (x, y, z). Default to 3. Each row is (x, y, z). Defaults to 3.
attribute_dims (dict): Dictionary to indicate the meaning of extra attribute_dims (dict, optional): Dictionary to indicate the
dimension. Default to None. meaning of extra dimension. Defaults to None.
Attributes: Attributes:
tensor (torch.Tensor): Float matrix of N x points_dim. tensor (torch.Tensor): Float matrix of N x points_dim.
points_dim (int): Integer indicating the dimension of a point. points_dim (int): Integer indicating the dimension of a point.
Each row is (x, y, z, ...). Each row is (x, y, z, ...).
attribute_dims (bool): Dictionary to indicate the meaning of extra attribute_dims (bool): Dictionary to indicate the meaning of extra
dimension. Default to None. dimension. Defaults to None.
rotation_axis (int): Default rotation axis for points rotation. rotation_axis (int): Default rotation axis for points rotation.
""" """
...@@ -45,7 +48,7 @@ class BasePoints(object): ...@@ -45,7 +48,7 @@ class BasePoints(object):
@property @property
def coord(self): def coord(self):
"""torch.Tensor: Coordinates of each point with size (N, 3).""" """torch.Tensor: Coordinates of each point in shape (N, 3)."""
return self.tensor[:, :3] return self.tensor[:, :3]
@coord.setter @coord.setter
...@@ -61,7 +64,8 @@ class BasePoints(object): ...@@ -61,7 +64,8 @@ class BasePoints(object):
@property @property
def height(self): def height(self):
"""torch.Tensor: A vector with height of each point.""" """torch.Tensor:
A vector with height of each point in shape (N, 1), or None."""
if self.attribute_dims is not None and \ if self.attribute_dims is not None and \
'height' in self.attribute_dims.keys(): 'height' in self.attribute_dims.keys():
return self.tensor[:, self.attribute_dims['height']] return self.tensor[:, self.attribute_dims['height']]
...@@ -91,7 +95,8 @@ class BasePoints(object): ...@@ -91,7 +95,8 @@ class BasePoints(object):
@property @property
def color(self): def color(self):
"""torch.Tensor: A vector with color of each point.""" """torch.Tensor:
A vector with color of each point in shape (N, 3), or None."""
if self.attribute_dims is not None and \ if self.attribute_dims is not None and \
'color' in self.attribute_dims.keys(): 'color' in self.attribute_dims.keys():
return self.tensor[:, self.attribute_dims['color']] return self.tensor[:, self.attribute_dims['color']]
...@@ -141,9 +146,9 @@ class BasePoints(object): ...@@ -141,9 +146,9 @@ class BasePoints(object):
"""Rotate points with the given rotation matrix or angle. """Rotate points with the given rotation matrix or angle.
Args: Args:
rotation (float, np.ndarray, torch.Tensor): Rotation matrix rotation (float | np.ndarray | torch.Tensor): Rotation matrix
or angle. or angle.
axis (int): Axis to rotate at. Defaults to None. axis (int, optional): Axis to rotate at. Defaults to None.
""" """
if not isinstance(rotation, torch.Tensor): if not isinstance(rotation, torch.Tensor):
rotation = self.tensor.new_tensor(rotation) rotation = self.tensor.new_tensor(rotation)
...@@ -154,34 +159,24 @@ class BasePoints(object): ...@@ -154,34 +159,24 @@ class BasePoints(object):
axis = self.rotation_axis axis = self.rotation_axis
if rotation.numel() == 1: if rotation.numel() == 1:
rot_sin = torch.sin(rotation) rotated_points, rot_mat_T = rotation_3d_in_axis(
rot_cos = torch.cos(rotation) self.tensor[:, :3][None], rotation, axis=axis, return_mat=True)
if axis == 1: self.tensor[:, :3] = rotated_points.squeeze(0)
rot_mat_T = rotation.new_tensor([[rot_cos, 0, -rot_sin], rot_mat_T = rot_mat_T.squeeze(0)
[0, 1, 0],
[rot_sin, 0, rot_cos]])
elif axis == 2 or axis == -1:
rot_mat_T = rotation.new_tensor([[rot_cos, -rot_sin, 0],
[rot_sin, rot_cos, 0],
[0, 0, 1]])
elif axis == 0:
rot_mat_T = rotation.new_tensor([[1, 0, 0],
[0, rot_cos, -rot_sin],
[0, rot_sin, rot_cos]])
else:
raise ValueError('axis should in range')
rot_mat_T = rot_mat_T.T
elif rotation.numel() == 9:
rot_mat_T = rotation
else: else:
raise NotImplementedError # rotation.numel() == 9
self.tensor[:, :3] = self.tensor[:, :3] @ rot_mat_T self.tensor[:, :3] = self.tensor[:, :3] @ rotation
rot_mat_T = rotation
return rot_mat_T return rot_mat_T
@abstractmethod @abstractmethod
def flip(self, bev_direction='horizontal'): def flip(self, bev_direction='horizontal'):
"""Flip the points in BEV along given BEV direction.""" """Flip the points along given BEV direction.
Args:
bev_direction (str): Flip direction (horizontal or vertical).
"""
pass pass
def translate(self, trans_vector): def translate(self, trans_vector):
...@@ -218,7 +213,7 @@ class BasePoints(object): ...@@ -218,7 +213,7 @@ class BasePoints(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 point is \ torch.Tensor: A binary vector indicating whether each point is
inside the reference range. inside the reference range.
""" """
in_range_flags = ((self.tensor[:, 0] > point_range[0]) in_range_flags = ((self.tensor[:, 0] > point_range[0])
...@@ -229,7 +224,11 @@ class BasePoints(object): ...@@ -229,7 +224,11 @@ class BasePoints(object):
& (self.tensor[:, 2] < point_range[5])) & (self.tensor[:, 2] < point_range[5]))
return in_range_flags return in_range_flags
@abstractmethod @property
def bev(self):
"""torch.Tensor: BEV of the points in shape (N, 2)."""
return self.tensor[:, [0, 1]]
def in_range_bev(self, point_range): def in_range_bev(self, point_range):
"""Check whether the points are in the given range. """Check whether the points are in the given range.
...@@ -238,10 +237,14 @@ class BasePoints(object): ...@@ -238,10 +237,14 @@ class BasePoints(object):
in order of (x_min, y_min, x_max, y_max). in order of (x_min, y_min, x_max, y_max).
Returns: Returns:
torch.Tensor: Indicating whether each point is inside \ torch.Tensor: Indicating whether each point is inside
the reference range. the reference range.
""" """
pass in_range_flags = ((self.bev[:, 0] > point_range[0])
& (self.bev[:, 1] > point_range[1])
& (self.bev[:, 1] < point_range[2])
& (self.bev[:, 1] < point_range[3]))
return in_range_flags
@abstractmethod @abstractmethod
def convert_to(self, dst, rt_mat=None): def convert_to(self, dst, rt_mat=None):
...@@ -249,14 +252,15 @@ class BasePoints(object): ...@@ -249,14 +252,15 @@ class BasePoints(object):
Args: Args:
dst (:obj:`CoordMode`): The target Box mode. dst (:obj:`CoordMode`): 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:`BasePoints`: The converted box of the same type \ :obj:`BasePoints`: The converted box of the same type
in the `dst` mode. in the `dst` mode.
""" """
pass pass
...@@ -288,7 +292,7 @@ class BasePoints(object): ...@@ -288,7 +292,7 @@ class BasePoints(object):
subject to Pytorch's indexing semantics. subject to Pytorch's indexing semantics.
Returns: Returns:
:obj:`BasePoints`: A new object of \ :obj:`BasePoints`: A new object of
:class:`BasePoints` after indexing. :class:`BasePoints` after indexing.
""" """
original_type = type(self) original_type = type(self)
...@@ -379,7 +383,7 @@ class BasePoints(object): ...@@ -379,7 +383,7 @@ class BasePoints(object):
device (str | :obj:`torch.device`): The name of the device. device (str | :obj:`torch.device`): The name of the device.
Returns: Returns:
:obj:`BasePoints`: A new boxes object on the \ :obj:`BasePoints`: A new boxes object on the
specific device. specific device.
""" """
original_type = type(self) original_type = type(self)
...@@ -392,7 +396,7 @@ class BasePoints(object): ...@@ -392,7 +396,7 @@ class BasePoints(object):
"""Clone the Points. """Clone the Points.
Returns: Returns:
:obj:`BasePoints`: Box object with the same properties \ :obj:`BasePoints`: Box object with the same properties
as self. as self.
""" """
original_type = type(self) original_type = type(self)
...@@ -417,14 +421,14 @@ class BasePoints(object): ...@@ -417,14 +421,14 @@ class BasePoints(object):
def new_point(self, data): def new_point(self, data):
"""Create a new point object with data. """Create a new point object with data.
The new point and its tensor has the similar properties \ The new point 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:`BasePoints`: A new point object with ``data``, \ :obj:`BasePoints`: A new point 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) \
......
...@@ -7,17 +7,17 @@ class CameraPoints(BasePoints): ...@@ -7,17 +7,17 @@ class CameraPoints(BasePoints):
Args: Args:
tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix. tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix.
points_dim (int): Number of the dimension of a point. points_dim (int, optional): Number of the dimension of a point.
Each row is (x, y, z). Default to 3. Each row is (x, y, z). Defaults to 3.
attribute_dims (dict): Dictionary to indicate the meaning of extra attribute_dims (dict, optional): Dictionary to indicate the
dimension. Default to None. meaning of extra dimension. Defaults to None.
Attributes: Attributes:
tensor (torch.Tensor): Float matrix of N x points_dim. tensor (torch.Tensor): Float matrix of N x points_dim.
points_dim (int): Integer indicating the dimension of a point. points_dim (int): Integer indicating the dimension of a point.
Each row is (x, y, z, ...). Each row is (x, y, z, ...).
attribute_dims (bool): Dictionary to indicate the meaning of extra attribute_dims (bool): Dictionary to indicate the meaning of extra
dimension. Default to None. dimension. Defaults to None.
rotation_axis (int): Default rotation axis for points rotation. rotation_axis (int): Default rotation axis for points rotation.
""" """
...@@ -27,42 +27,35 @@ class CameraPoints(BasePoints): ...@@ -27,42 +27,35 @@ class CameraPoints(BasePoints):
self.rotation_axis = 1 self.rotation_axis = 1
def flip(self, bev_direction='horizontal'): def flip(self, bev_direction='horizontal'):
"""Flip the boxes in BEV along given BEV direction.""" """Flip the points along given BEV direction.
Args:
bev_direction (str): Flip direction (horizontal or vertical).
"""
if bev_direction == 'horizontal': if bev_direction == 'horizontal':
self.tensor[:, 0] = -self.tensor[:, 0] self.tensor[:, 0] = -self.tensor[:, 0]
elif bev_direction == 'vertical': elif bev_direction == 'vertical':
self.tensor[:, 2] = -self.tensor[:, 2] self.tensor[:, 2] = -self.tensor[:, 2]
def in_range_bev(self, point_range): @property
"""Check whether the points are in the given range. def bev(self):
"""torch.Tensor: BEV of the points in shape (N, 2)."""
Args: return self.tensor[:, [0, 2]]
point_range (list | torch.Tensor): The range of point
in order of (x_min, y_min, x_max, y_max).
Returns:
torch.Tensor: Indicating whether each point is inside \
the reference range.
"""
in_range_flags = ((self.tensor[:, 0] > point_range[0])
& (self.tensor[:, 2] > point_range[1])
& (self.tensor[:, 0] < point_range[2])
& (self.tensor[:, 2] < point_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:`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:
:obj:`BasePoints`: The converted point of the same type \ :obj:`BasePoints`: The converted point of the same type
in the `dst` mode. in the `dst` mode.
""" """
from mmdet3d.core.bbox import Coord3DMode from mmdet3d.core.bbox import Coord3DMode
......
...@@ -7,17 +7,17 @@ class DepthPoints(BasePoints): ...@@ -7,17 +7,17 @@ class DepthPoints(BasePoints):
Args: Args:
tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix. tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix.
points_dim (int): Number of the dimension of a point. points_dim (int, optional): Number of the dimension of a point.
Each row is (x, y, z). Default to 3. Each row is (x, y, z). Defaults to 3.
attribute_dims (dict): Dictionary to indicate the meaning of extra attribute_dims (dict, optional): Dictionary to indicate the
dimension. Default to None. meaning of extra dimension. Defaults to None.
Attributes: Attributes:
tensor (torch.Tensor): Float matrix of N x points_dim. tensor (torch.Tensor): Float matrix of N x points_dim.
points_dim (int): Integer indicating the dimension of a point. points_dim (int): Integer indicating the dimension of a point.
Each row is (x, y, z, ...). Each row is (x, y, z, ...).
attribute_dims (bool): Dictionary to indicate the meaning of extra attribute_dims (bool): Dictionary to indicate the meaning of extra
dimension. Default to None. dimension. Defaults to None.
rotation_axis (int): Default rotation axis for points rotation. rotation_axis (int): Default rotation axis for points rotation.
""" """
...@@ -27,42 +27,30 @@ class DepthPoints(BasePoints): ...@@ -27,42 +27,30 @@ class DepthPoints(BasePoints):
self.rotation_axis = 2 self.rotation_axis = 2
def flip(self, bev_direction='horizontal'): def flip(self, bev_direction='horizontal'):
"""Flip the boxes in BEV along given BEV direction.""" """Flip the points along given BEV direction.
Args:
bev_direction (str): Flip direction (horizontal or vertical).
"""
if bev_direction == 'horizontal': if bev_direction == 'horizontal':
self.tensor[:, 0] = -self.tensor[:, 0] self.tensor[:, 0] = -self.tensor[:, 0]
elif bev_direction == 'vertical': elif bev_direction == 'vertical':
self.tensor[:, 1] = -self.tensor[:, 1] self.tensor[:, 1] = -self.tensor[:, 1]
def in_range_bev(self, point_range):
"""Check whether the points are in the given range.
Args:
point_range (list | torch.Tensor): The range of point
in order of (x_min, y_min, x_max, y_max).
Returns:
torch.Tensor: Indicating whether each point is inside \
the reference range.
"""
in_range_flags = ((self.tensor[:, 0] > point_range[0])
& (self.tensor[:, 1] > point_range[1])
& (self.tensor[:, 0] < point_range[2])
& (self.tensor[:, 1] < point_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:`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:
:obj:`BasePoints`: The converted point of the same type \ :obj:`BasePoints`: The converted point of the same type
in the `dst` mode. in the `dst` mode.
""" """
from mmdet3d.core.bbox import Coord3DMode from mmdet3d.core.bbox import Coord3DMode
......
...@@ -7,17 +7,17 @@ class LiDARPoints(BasePoints): ...@@ -7,17 +7,17 @@ class LiDARPoints(BasePoints):
Args: Args:
tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix. tensor (torch.Tensor | np.ndarray | list): a N x points_dim matrix.
points_dim (int): Number of the dimension of a point. points_dim (int, optional): Number of the dimension of a point.
Each row is (x, y, z). Default to 3. Each row is (x, y, z). Defaults to 3.
attribute_dims (dict): Dictionary to indicate the meaning of extra attribute_dims (dict, optional): Dictionary to indicate the
dimension. Default to None. meaning of extra dimension. Defaults to None.
Attributes: Attributes:
tensor (torch.Tensor): Float matrix of N x points_dim. tensor (torch.Tensor): Float matrix of N x points_dim.
points_dim (int): Integer indicating the dimension of a point. points_dim (int): Integer indicating the dimension of a point.
Each row is (x, y, z, ...). Each row is (x, y, z, ...).
attribute_dims (bool): Dictionary to indicate the meaning of extra attribute_dims (bool): Dictionary to indicate the meaning of extra
dimension. Default to None. dimension. Defaults to None.
rotation_axis (int): Default rotation axis for points rotation. rotation_axis (int): Default rotation axis for points rotation.
""" """
...@@ -27,42 +27,30 @@ class LiDARPoints(BasePoints): ...@@ -27,42 +27,30 @@ class LiDARPoints(BasePoints):
self.rotation_axis = 2 self.rotation_axis = 2
def flip(self, bev_direction='horizontal'): def flip(self, bev_direction='horizontal'):
"""Flip the boxes in BEV along given BEV direction.""" """Flip the points along given BEV direction.
Args:
bev_direction (str): Flip direction (horizontal or vertical).
"""
if bev_direction == 'horizontal': if bev_direction == 'horizontal':
self.tensor[:, 1] = -self.tensor[:, 1] self.tensor[:, 1] = -self.tensor[:, 1]
elif bev_direction == 'vertical': elif bev_direction == 'vertical':
self.tensor[:, 0] = -self.tensor[:, 0] self.tensor[:, 0] = -self.tensor[:, 0]
def in_range_bev(self, point_range):
"""Check whether the points are in the given range.
Args:
point_range (list | torch.Tensor): The range of point
in order of (x_min, y_min, x_max, y_max).
Returns:
torch.Tensor: Indicating whether each point is inside \
the reference range.
"""
in_range_flags = ((self.tensor[:, 0] > point_range[0])
& (self.tensor[:, 1] > point_range[1])
& (self.tensor[:, 0] < point_range[2])
& (self.tensor[:, 1] < point_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:`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:
:obj:`BasePoints`: The converted point of the same type \ :obj:`BasePoints`: The converted point of the same type
in the `dst` mode. in the `dst` mode.
""" """
from mmdet3d.core.bbox import Coord3DMode from mmdet3d.core.bbox import Coord3DMode
......
...@@ -15,16 +15,18 @@ def box3d_multiclass_nms(mlvl_bboxes, ...@@ -15,16 +15,18 @@ def box3d_multiclass_nms(mlvl_bboxes,
mlvl_dir_scores=None, mlvl_dir_scores=None,
mlvl_attr_scores=None, mlvl_attr_scores=None,
mlvl_bboxes2d=None): mlvl_bboxes2d=None):
"""Multi-class nms for 3D boxes. """Multi-class NMS for 3D boxes. The IoU used for NMS is defined as the 2D
IoU between BEV boxes.
Args: Args:
mlvl_bboxes (torch.Tensor): Multi-level boxes with shape (N, M). mlvl_bboxes (torch.Tensor): Multi-level boxes with shape (N, M).
M is the dimensions of boxes. M is the dimensions of boxes.
mlvl_bboxes_for_nms (torch.Tensor): Multi-level boxes with shape mlvl_bboxes_for_nms (torch.Tensor): Multi-level boxes with shape
(N, 5) ([x1, y1, x2, y2, ry]). N is the number of boxes. (N, 5) ([x1, y1, x2, y2, ry]). N is the number of boxes.
The coordinate system of the BEV boxes is counterclockwise.
mlvl_scores (torch.Tensor): Multi-level boxes with shape mlvl_scores (torch.Tensor): Multi-level boxes with shape
(N, C + 1). N is the number of boxes. C is the number of classes. (N, C + 1). N is the number of boxes. C is the number of classes.
score_thr (float): Score thredhold to filter boxes with low score_thr (float): Score threshold to filter boxes with low
confidence. confidence.
max_num (int): Maximum number of boxes will be kept. max_num (int): Maximum number of boxes will be kept.
cfg (dict): Configuration dict of NMS. cfg (dict): Configuration dict of NMS.
...@@ -36,8 +38,8 @@ def box3d_multiclass_nms(mlvl_bboxes, ...@@ -36,8 +38,8 @@ def box3d_multiclass_nms(mlvl_bboxes,
boxes. Defaults to None. boxes. Defaults to None.
Returns: Returns:
tuple[torch.Tensor]: Return results after nms, including 3D \ tuple[torch.Tensor]: Return results after nms, including 3D
bounding boxes, scores, labels, direction scores, attribute \ bounding boxes, scores, labels, direction scores, attribute
scores (optional) and 2D bounding boxes (optional). scores (optional) and 2D bounding boxes (optional).
""" """
# do multi class nms # do multi class nms
...@@ -128,13 +130,13 @@ def box3d_multiclass_nms(mlvl_bboxes, ...@@ -128,13 +130,13 @@ def box3d_multiclass_nms(mlvl_bboxes,
def aligned_3d_nms(boxes, scores, classes, thresh): def aligned_3d_nms(boxes, scores, classes, thresh):
"""3d nms for aligned boxes. """3D NMS for aligned boxes.
Args: Args:
boxes (torch.Tensor): Aligned box with shape [n, 6]. boxes (torch.Tensor): Aligned box with shape [n, 6].
scores (torch.Tensor): Scores of each box. scores (torch.Tensor): Scores of each box.
classes (torch.Tensor): Class of each box. classes (torch.Tensor): Class of each box.
thresh (float): Iou threshold for nms. thresh (float): IoU threshold for nms.
Returns: Returns:
torch.Tensor: Indices of selected boxes. torch.Tensor: Indices of selected boxes.
...@@ -188,8 +190,8 @@ def circle_nms(dets, thresh, post_max_size=83): ...@@ -188,8 +190,8 @@ def circle_nms(dets, thresh, post_max_size=83):
Args: Args:
dets (torch.Tensor): Detection results with the shape of [N, 3]. dets (torch.Tensor): Detection results with the shape of [N, 3].
thresh (float): Value of threshold. thresh (float): Value of threshold.
post_max_size (int): Max number of prediction to be kept. Defaults post_max_size (int, optional): Max number of prediction to be kept.
to 83 Defaults to 83.
Returns: Returns:
torch.Tensor: Indexes of the detections to be kept. torch.Tensor: Indexes of the detections to be kept.
...@@ -217,4 +219,8 @@ def circle_nms(dets, thresh, post_max_size=83): ...@@ -217,4 +219,8 @@ def circle_nms(dets, thresh, post_max_size=83):
# ovr = inter / areas[j] # ovr = inter / areas[j]
if dist <= thresh: if dist <= thresh:
suppressed[j] = 1 suppressed[j] = 1
return keep[:post_max_size]
if post_max_size < len(keep):
return keep[:post_max_size]
return keep
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
from .gaussian import draw_heatmap_gaussian, gaussian_2d, gaussian_radius from .array_converter import ArrayConverter, array_converter
from .gaussian import (draw_heatmap_gaussian, ellip_gaussian2D, gaussian_2d,
gaussian_radius, get_ellip_gaussian_2D)
__all__ = ['gaussian_2d', 'gaussian_radius', 'draw_heatmap_gaussian'] __all__ = [
'gaussian_2d', 'gaussian_radius', 'draw_heatmap_gaussian',
'ArrayConverter', 'array_converter', 'ellip_gaussian2D',
'get_ellip_gaussian_2D'
]
# Copyright (c) OpenMMLab. All rights reserved.
import functools
from inspect import getfullargspec
import numpy as np
import torch
def array_converter(to_torch=True,
apply_to=tuple(),
template_arg_name_=None,
recover=True):
"""Wrapper function for data-type agnostic processing.
First converts input arrays to PyTorch tensors or NumPy ndarrays
for middle calculation, then convert output to original data-type if
`recover=True`.
Args:
to_torch (Bool, optional): Whether convert to PyTorch tensors
for middle calculation. Defaults to True.
apply_to (tuple[str], optional): The arguments to which we apply
data-type conversion. Defaults to an empty tuple.
template_arg_name_ (str, optional): Argument serving as the template (
return arrays should have the same dtype and device
as the template). Defaults to None. If None, we will use the
first argument in `apply_to` as the template argument.
recover (Bool, optional): Whether or not recover the wrapped function
outputs to the `template_arg_name_` type. Defaults to True.
Raises:
ValueError: When template_arg_name_ is not among all args, or
when apply_to contains an arg which is not among all args,
a ValueError will be raised. When the template argument or
an argument to convert is a list or tuple, and cannot be
converted to a NumPy array, a ValueError will be raised.
TypeError: When the type of the template argument or
an argument to convert does not belong to the above range,
or the contents of such an list-or-tuple-type argument
do not share the same data type, a TypeError is raised.
Returns:
(function): wrapped function.
Example:
>>> import torch
>>> import numpy as np
>>>
>>> # Use torch addition for a + b,
>>> # and convert return values to the type of a
>>> @array_converter(apply_to=('a', 'b'))
>>> def simple_add(a, b):
>>> return a + b
>>>
>>> a = np.array([1.1])
>>> b = np.array([2.2])
>>> simple_add(a, b)
>>>
>>> # Use numpy addition for a + b,
>>> # and convert return values to the type of b
>>> @array_converter(to_torch=False, apply_to=('a', 'b'),
>>> template_arg_name_='b')
>>> def simple_add(a, b):
>>> return a + b
>>>
>>> simple_add()
>>>
>>> # Use torch funcs for floor(a) if flag=True else ceil(a),
>>> # and return the torch tensor
>>> @array_converter(apply_to=('a',), recover=False)
>>> def floor_or_ceil(a, flag=True):
>>> return torch.floor(a) if flag else torch.ceil(a)
>>>
>>> floor_or_ceil(a, flag=False)
"""
def array_converter_wrapper(func):
"""Outer wrapper for the function."""
@functools.wraps(func)
def new_func(*args, **kwargs):
"""Inner wrapper for the arguments."""
if len(apply_to) == 0:
return func(*args, **kwargs)
func_name = func.__name__
arg_spec = getfullargspec(func)
arg_names = arg_spec.args
arg_num = len(arg_names)
default_arg_values = arg_spec.defaults
if default_arg_values is None:
default_arg_values = []
no_default_arg_num = len(arg_names) - len(default_arg_values)
kwonly_arg_names = arg_spec.kwonlyargs
kwonly_default_arg_values = arg_spec.kwonlydefaults
if kwonly_default_arg_values is None:
kwonly_default_arg_values = {}
all_arg_names = arg_names + kwonly_arg_names
# in case there are args in the form of *args
if len(args) > arg_num:
named_args = args[:arg_num]
nameless_args = args[arg_num:]
else:
named_args = args
nameless_args = []
# template argument data type is used for all array-like arguments
if template_arg_name_ is None:
template_arg_name = apply_to[0]
else:
template_arg_name = template_arg_name_
if template_arg_name not in all_arg_names:
raise ValueError(f'{template_arg_name} is not among the '
f'argument list of function {func_name}')
# inspect apply_to
for arg_to_apply in apply_to:
if arg_to_apply not in all_arg_names:
raise ValueError(f'{arg_to_apply} is not '
f'an argument of {func_name}')
new_args = []
new_kwargs = {}
converter = ArrayConverter()
target_type = torch.Tensor if to_torch else np.ndarray
# non-keyword arguments
for i, arg_value in enumerate(named_args):
if arg_names[i] in apply_to:
new_args.append(
converter.convert(
input_array=arg_value, target_type=target_type))
else:
new_args.append(arg_value)
if arg_names[i] == template_arg_name:
template_arg_value = arg_value
kwonly_default_arg_values.update(kwargs)
kwargs = kwonly_default_arg_values
# keyword arguments and non-keyword arguments using default value
for i in range(len(named_args), len(all_arg_names)):
arg_name = all_arg_names[i]
if arg_name in kwargs:
if arg_name in apply_to:
new_kwargs[arg_name] = converter.convert(
input_array=kwargs[arg_name],
target_type=target_type)
else:
new_kwargs[arg_name] = kwargs[arg_name]
else:
default_value = default_arg_values[i - no_default_arg_num]
if arg_name in apply_to:
new_kwargs[arg_name] = converter.convert(
input_array=default_value, target_type=target_type)
else:
new_kwargs[arg_name] = default_value
if arg_name == template_arg_name:
template_arg_value = kwargs[arg_name]
# add nameless args provided by *args (if exists)
new_args += nameless_args
return_values = func(*new_args, **new_kwargs)
converter.set_template(template_arg_value)
def recursive_recover(input_data):
if isinstance(input_data, (tuple, list)):
new_data = []
for item in input_data:
new_data.append(recursive_recover(item))
return tuple(new_data) if isinstance(input_data,
tuple) else new_data
elif isinstance(input_data, dict):
new_data = {}
for k, v in input_data.items():
new_data[k] = recursive_recover(v)
return new_data
elif isinstance(input_data, (torch.Tensor, np.ndarray)):
return converter.recover(input_data)
else:
return input_data
if recover:
return recursive_recover(return_values)
else:
return return_values
return new_func
return array_converter_wrapper
class ArrayConverter:
SUPPORTED_NON_ARRAY_TYPES = (int, float, np.int8, np.int16, np.int32,
np.int64, np.uint8, np.uint16, np.uint32,
np.uint64, np.float16, np.float32, np.float64)
def __init__(self, template_array=None):
if template_array is not None:
self.set_template(template_array)
def set_template(self, array):
"""Set template array.
Args:
array (tuple | list | int | float | np.ndarray | torch.Tensor):
Template array.
Raises:
ValueError: If input is list or tuple and cannot be converted to
to a NumPy array, a ValueError is raised.
TypeError: If input type does not belong to the above range,
or the contents of a list or tuple do not share the
same data type, a TypeError is raised.
"""
self.array_type = type(array)
self.is_num = False
self.device = 'cpu'
if isinstance(array, np.ndarray):
self.dtype = array.dtype
elif isinstance(array, torch.Tensor):
self.dtype = array.dtype
self.device = array.device
elif isinstance(array, (list, tuple)):
try:
array = np.array(array)
if array.dtype not in self.SUPPORTED_NON_ARRAY_TYPES:
raise TypeError
self.dtype = array.dtype
except (ValueError, TypeError):
print(f'The following list cannot be converted to'
f' a numpy array of supported dtype:\n{array}')
raise
elif isinstance(array, self.SUPPORTED_NON_ARRAY_TYPES):
self.array_type = np.ndarray
self.is_num = True
self.dtype = np.dtype(type(array))
else:
raise TypeError(f'Template type {self.array_type}'
f' is not supported.')
def convert(self, input_array, target_type=None, target_array=None):
"""Convert input array to target data type.
Args:
input_array (tuple | list | np.ndarray |
torch.Tensor | int | float ):
Input array. Defaults to None.
target_type (<class 'np.ndarray'> | <class 'torch.Tensor'>,
optional):
Type to which input array is converted. Defaults to None.
target_array (np.ndarray | torch.Tensor, optional):
Template array to which input array is converted.
Defaults to None.
Raises:
ValueError: If input is list or tuple and cannot be converted to
to a NumPy array, a ValueError is raised.
TypeError: If input type does not belong to the above range,
or the contents of a list or tuple do not share the
same data type, a TypeError is raised.
"""
if isinstance(input_array, (list, tuple)):
try:
input_array = np.array(input_array)
if input_array.dtype not in self.SUPPORTED_NON_ARRAY_TYPES:
raise TypeError
except (ValueError, TypeError):
print(f'The input cannot be converted to'
f' a single-type numpy array:\n{input_array}')
raise
elif isinstance(input_array, self.SUPPORTED_NON_ARRAY_TYPES):
input_array = np.array(input_array)
array_type = type(input_array)
assert target_type is not None or target_array is not None, \
'must specify a target'
if target_type is not None:
assert target_type in (np.ndarray, torch.Tensor), \
'invalid target type'
if target_type == array_type:
return input_array
elif target_type == np.ndarray:
# default dtype is float32
converted_array = input_array.cpu().numpy().astype(np.float32)
else:
# default dtype is float32, device is 'cpu'
converted_array = torch.tensor(
input_array, dtype=torch.float32)
else:
assert isinstance(target_array, (np.ndarray, torch.Tensor)), \
'invalid target array type'
if isinstance(target_array, array_type):
return input_array
elif isinstance(target_array, np.ndarray):
converted_array = input_array.cpu().numpy().astype(
target_array.dtype)
else:
converted_array = target_array.new_tensor(input_array)
return converted_array
def recover(self, input_array):
assert isinstance(input_array, (np.ndarray, torch.Tensor)), \
'invalid input array type'
if isinstance(input_array, self.array_type):
return input_array
elif isinstance(input_array, torch.Tensor):
converted_array = input_array.cpu().numpy().astype(self.dtype)
else:
converted_array = torch.tensor(
input_array, dtype=self.dtype, device=self.device)
if self.is_num:
converted_array = converted_array.item()
return converted_array
...@@ -8,7 +8,7 @@ def gaussian_2d(shape, sigma=1): ...@@ -8,7 +8,7 @@ def gaussian_2d(shape, sigma=1):
Args: Args:
shape (list[int]): Shape of the map. shape (list[int]): Shape of the map.
sigma (float): Sigma to generate gaussian map. sigma (float, optional): Sigma to generate gaussian map.
Defaults to 1. Defaults to 1.
Returns: Returns:
...@@ -28,8 +28,8 @@ def draw_heatmap_gaussian(heatmap, center, radius, k=1): ...@@ -28,8 +28,8 @@ def draw_heatmap_gaussian(heatmap, center, radius, k=1):
Args: Args:
heatmap (torch.Tensor): Heatmap to be masked. heatmap (torch.Tensor): Heatmap to be masked.
center (torch.Tensor): Center coord of the heatmap. center (torch.Tensor): Center coord of the heatmap.
radius (int): Radius of gausian. radius (int): Radius of gaussian.
K (int): Multiple of masked_gaussian. Defaults to 1. K (int, optional): Multiple of masked_gaussian. Defaults to 1.
Returns: Returns:
torch.Tensor: Masked heatmap. torch.Tensor: Masked heatmap.
...@@ -59,7 +59,7 @@ def gaussian_radius(det_size, min_overlap=0.5): ...@@ -59,7 +59,7 @@ def gaussian_radius(det_size, min_overlap=0.5):
Args: Args:
det_size (tuple[torch.Tensor]): Size of the detection result. det_size (tuple[torch.Tensor]): Size of the detection result.
min_overlap (float): Gaussian_overlap. Defaults to 0.5. min_overlap (float, optional): Gaussian_overlap. Defaults to 0.5.
Returns: Returns:
torch.Tensor: Computed radius. torch.Tensor: Computed radius.
...@@ -84,3 +84,75 @@ def gaussian_radius(det_size, min_overlap=0.5): ...@@ -84,3 +84,75 @@ def gaussian_radius(det_size, min_overlap=0.5):
sq3 = torch.sqrt(b3**2 - 4 * a3 * c3) sq3 = torch.sqrt(b3**2 - 4 * a3 * c3)
r3 = (b3 + sq3) / 2 r3 = (b3 + sq3) / 2
return min(r1, r2, r3) return min(r1, r2, r3)
def get_ellip_gaussian_2D(heatmap, center, radius_x, radius_y, k=1):
"""Generate 2D ellipse gaussian heatmap.
Args:
heatmap (Tensor): Input heatmap, the gaussian kernel will cover on
it and maintain the max value.
center (list[int]): Coord of gaussian kernel's center.
radius_x (int): X-axis radius of gaussian kernel.
radius_y (int): Y-axis radius of gaussian kernel.
k (int, optional): Coefficient of gaussian kernel. Default: 1.
Returns:
out_heatmap (Tensor): Updated heatmap covered by gaussian kernel.
"""
diameter_x, diameter_y = 2 * radius_x + 1, 2 * radius_y + 1
gaussian_kernel = ellip_gaussian2D((radius_x, radius_y),
sigma_x=diameter_x / 6,
sigma_y=diameter_y / 6,
dtype=heatmap.dtype,
device=heatmap.device)
x, y = int(center[0]), int(center[1])
height, width = heatmap.shape[0:2]
left, right = min(x, radius_x), min(width - x, radius_x + 1)
top, bottom = min(y, radius_y), min(height - y, radius_y + 1)
masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right]
masked_gaussian = gaussian_kernel[radius_y - top:radius_y + bottom,
radius_x - left:radius_x + right]
out_heatmap = heatmap
torch.max(
masked_heatmap,
masked_gaussian * k,
out=out_heatmap[y - top:y + bottom, x - left:x + right])
return out_heatmap
def ellip_gaussian2D(radius,
sigma_x,
sigma_y,
dtype=torch.float32,
device='cpu'):
"""Generate 2D ellipse gaussian kernel.
Args:
radius (tuple(int)): Ellipse radius (radius_x, radius_y) of gaussian
kernel.
sigma_x (int): X-axis sigma of gaussian function.
sigma_y (int): Y-axis sigma of gaussian function.
dtype (torch.dtype, optional): Dtype of gaussian tensor.
Default: torch.float32.
device (str, optional): Device of gaussian tensor.
Default: 'cpu'.
Returns:
h (Tensor): Gaussian kernel with a
``(2 * radius_y + 1) * (2 * radius_x + 1)`` shape.
"""
x = torch.arange(
-radius[0], radius[0] + 1, dtype=dtype, device=device).view(1, -1)
y = torch.arange(
-radius[1], radius[1] + 1, dtype=dtype, device=device).view(-1, 1)
h = (-(x * x) / (2 * sigma_x * sigma_x) - (y * y) /
(2 * sigma_y * sigma_y)).exp()
h[h < torch.finfo(h.dtype).eps * h.max()] = 0
return h
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import copy import copy
import cv2 import cv2
import numpy as np import numpy as np
import torch import torch
...@@ -18,7 +19,7 @@ def project_pts_on_img(points, ...@@ -18,7 +19,7 @@ def project_pts_on_img(points,
raw_img (numpy.array): The numpy array of image. raw_img (numpy.array): The numpy array of image.
lidar2img_rt (numpy.array, shape=[4, 4]): The projection matrix lidar2img_rt (numpy.array, shape=[4, 4]): The projection matrix
according to the camera intrinsic parameters. according to the camera intrinsic parameters.
max_distance (float): the max distance of the points cloud. max_distance (float, optional): the max distance of the points cloud.
Default: 70. Default: 70.
thickness (int, optional): The thickness of 2D points. Default: -1. thickness (int, optional): The thickness of 2D points. Default: -1.
""" """
...@@ -69,7 +70,8 @@ def plot_rect3d_on_img(img, ...@@ -69,7 +70,8 @@ def plot_rect3d_on_img(img,
num_rects (int): Number of 3D rectangulars. num_rects (int): Number of 3D rectangulars.
rect_corners (numpy.array): Coordinates of the corners of 3D rect_corners (numpy.array): Coordinates of the corners of 3D
rectangulars. Should be in the shape of [num_rect, 8, 2]. rectangulars. Should be in the shape of [num_rect, 8, 2].
color (tuple[int]): The color to draw bboxes. Default: (0, 255, 0). color (tuple[int], optional): The color to draw bboxes.
Default: (0, 255, 0).
thickness (int, optional): The thickness of bboxes. Default: 1. thickness (int, optional): The thickness of bboxes. Default: 1.
""" """
line_indices = ((0, 1), (0, 3), (0, 4), (1, 2), (1, 5), (3, 2), (3, 7), line_indices = ((0, 1), (0, 3), (0, 4), (1, 2), (1, 5), (3, 2), (3, 7),
...@@ -99,7 +101,8 @@ def draw_lidar_bbox3d_on_img(bboxes3d, ...@@ -99,7 +101,8 @@ def draw_lidar_bbox3d_on_img(bboxes3d,
lidar2img_rt (numpy.array, shape=[4, 4]): The projection matrix lidar2img_rt (numpy.array, shape=[4, 4]): The projection matrix
according to the camera intrinsic parameters. according to the camera intrinsic parameters.
img_metas (dict): Useless here. img_metas (dict): Useless here.
color (tuple[int]): The color to draw bboxes. Default: (0, 255, 0). color (tuple[int], optional): The color to draw bboxes.
Default: (0, 255, 0).
thickness (int, optional): The thickness of bboxes. Default: 1. thickness (int, optional): The thickness of bboxes. Default: 1.
""" """
img = raw_img.copy() img = raw_img.copy()
...@@ -136,7 +139,8 @@ def draw_depth_bbox3d_on_img(bboxes3d, ...@@ -136,7 +139,8 @@ def draw_depth_bbox3d_on_img(bboxes3d,
raw_img (numpy.array): The numpy array of image. raw_img (numpy.array): The numpy array of image.
calibs (dict): Camera calibration information, Rt and K. calibs (dict): Camera calibration information, Rt and K.
img_metas (dict): Used in coordinates transformation. img_metas (dict): Used in coordinates transformation.
color (tuple[int]): The color to draw bboxes. Default: (0, 255, 0). color (tuple[int], optional): The color to draw bboxes.
Default: (0, 255, 0).
thickness (int, optional): The thickness of bboxes. Default: 1. thickness (int, optional): The thickness of bboxes. Default: 1.
""" """
from mmdet3d.core.bbox import points_cam2img from mmdet3d.core.bbox import points_cam2img
...@@ -176,7 +180,8 @@ def draw_camera_bbox3d_on_img(bboxes3d, ...@@ -176,7 +180,8 @@ def draw_camera_bbox3d_on_img(bboxes3d,
cam2img (dict): Camera intrinsic matrix, cam2img (dict): Camera intrinsic matrix,
denoted as `K` in depth bbox coordinate system. denoted as `K` in depth bbox coordinate system.
img_metas (dict): Useless here. img_metas (dict): Useless here.
color (tuple[int]): The color to draw bboxes. Default: (0, 255, 0). color (tuple[int], optional): The color to draw bboxes.
Default: (0, 255, 0).
thickness (int, optional): The thickness of bboxes. Default: 1. thickness (int, optional): The thickness of bboxes. Default: 1.
""" """
from mmdet3d.core.bbox import points_cam2img from mmdet3d.core.bbox import points_cam2img
...@@ -188,7 +193,10 @@ def draw_camera_bbox3d_on_img(bboxes3d, ...@@ -188,7 +193,10 @@ def draw_camera_bbox3d_on_img(bboxes3d,
points_3d = corners_3d.reshape(-1, 3) points_3d = corners_3d.reshape(-1, 3)
if not isinstance(cam2img, torch.Tensor): if not isinstance(cam2img, torch.Tensor):
cam2img = torch.from_numpy(np.array(cam2img)) cam2img = torch.from_numpy(np.array(cam2img))
cam2img = cam2img.reshape(3, 3).float().cpu()
assert (cam2img.shape == torch.Size([3, 3])
or cam2img.shape == torch.Size([4, 4]))
cam2img = cam2img.float().cpu()
# project to 2d to get image coords (uv) # project to 2d to get image coords (uv)
uv_origin = points_cam2img(points_3d, cam2img) uv_origin = points_cam2img(points_3d, cam2img)
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import copy import copy
import numpy as np import numpy as np
import torch import torch
...@@ -22,12 +23,12 @@ def _draw_points(points, ...@@ -22,12 +23,12 @@ def _draw_points(points,
points (numpy.array | torch.tensor, shape=[N, 3+C]): points (numpy.array | torch.tensor, shape=[N, 3+C]):
points to visualize. points to visualize.
vis (:obj:`open3d.visualization.Visualizer`): open3d visualizer. vis (:obj:`open3d.visualization.Visualizer`): open3d visualizer.
points_size (int): the size of points to show on visualizer. points_size (int, optional): the size of points to show on visualizer.
Default: 2. Default: 2.
point_color (tuple[float]): the color of points. point_color (tuple[float], optional): the color of points.
Default: (0.5, 0.5, 0.5). Default: (0.5, 0.5, 0.5).
mode (str): indicate type of the input points, avaliable mode mode (str, optional): indicate type of the input points,
['xyz', 'xyzrgb']. Default: 'xyz'. available mode ['xyz', 'xyzrgb']. Default: 'xyz'.
Returns: Returns:
tuple: points, color of each point. tuple: points, color of each point.
...@@ -69,19 +70,21 @@ def _draw_bboxes(bbox3d, ...@@ -69,19 +70,21 @@ def _draw_bboxes(bbox3d,
Args: Args:
bbox3d (numpy.array | torch.tensor, shape=[M, 7]): bbox3d (numpy.array | torch.tensor, shape=[M, 7]):
3d bbox (x, y, z, dx, dy, dz, yaw) to visualize. 3d bbox (x, y, z, x_size, y_size, z_size, yaw) to visualize.
vis (:obj:`open3d.visualization.Visualizer`): open3d visualizer. vis (:obj:`open3d.visualization.Visualizer`): open3d visualizer.
points_colors (numpy.array): color of each points. points_colors (numpy.array): color of each points.
pcd (:obj:`open3d.geometry.PointCloud`): point cloud. Default: None. pcd (:obj:`open3d.geometry.PointCloud`, optional): point cloud.
bbox_color (tuple[float]): the color of bbox. Default: (0, 1, 0). Default: None.
points_in_box_color (tuple[float]): bbox_color (tuple[float], optional): the color of bbox.
Default: (0, 1, 0).
points_in_box_color (tuple[float], optional):
the color of points inside bbox3d. Default: (1, 0, 0). the color of points inside bbox3d. Default: (1, 0, 0).
rot_axis (int): rotation axis of bbox. Default: 2. rot_axis (int, optional): rotation axis of bbox. Default: 2.
center_mode (bool): indicate the center of bbox is bottom center center_mode (bool, optional): indicate the center of bbox is
or gravity center. avaliable mode bottom center or gravity center. available mode
['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'. ['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'.
mode (str): indicate type of the input points, avaliable mode mode (str, optional): indicate type of the input points,
['xyz', 'xyzrgb']. Default: 'xyz'. available mode ['xyz', 'xyzrgb']. Default: 'xyz'.
""" """
if isinstance(bbox3d, torch.Tensor): if isinstance(bbox3d, torch.Tensor):
bbox3d = bbox3d.cpu().numpy() bbox3d = bbox3d.cpu().numpy()
...@@ -135,23 +138,27 @@ def show_pts_boxes(points, ...@@ -135,23 +138,27 @@ def show_pts_boxes(points,
Args: Args:
points (numpy.array | torch.tensor, shape=[N, 3+C]): points (numpy.array | torch.tensor, shape=[N, 3+C]):
points to visualize. points to visualize.
bbox3d (numpy.array | torch.tensor, shape=[M, 7]): bbox3d (numpy.array | torch.tensor, shape=[M, 7], optional):
3d bbox (x, y, z, dx, dy, dz, yaw) to visualize. Default: None. 3D bbox (x, y, z, x_size, y_size, z_size, yaw) to visualize.
show (bool): whether to show the visualization results. Default: True. Defaults to None.
save_path (str): path to save visualized results. Default: None. show (bool, optional): whether to show the visualization results.
points_size (int): the size of points to show on visualizer. Default: True.
save_path (str, optional): path to save visualized results.
Default: None.
points_size (int, optional): the size of points to show on visualizer.
Default: 2. Default: 2.
point_color (tuple[float]): the color of points. point_color (tuple[float], optional): the color of points.
Default: (0.5, 0.5, 0.5). Default: (0.5, 0.5, 0.5).
bbox_color (tuple[float]): the color of bbox. Default: (0, 1, 0). bbox_color (tuple[float], optional): the color of bbox.
points_in_box_color (tuple[float]): Default: (0, 1, 0).
points_in_box_color (tuple[float], optional):
the color of points which are in bbox3d. Default: (1, 0, 0). the color of points which are in bbox3d. Default: (1, 0, 0).
rot_axis (int): rotation axis of bbox. Default: 2. rot_axis (int, optional): rotation axis of bbox. Default: 2.
center_mode (bool): indicate the center of bbox is bottom center center_mode (bool, optional): indicate the center of bbox is bottom
or gravity center. avaliable mode center or gravity center. available mode
['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'. ['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'.
mode (str): indicate type of the input points, avaliable mode mode (str, optional): indicate type of the input points, available
['xyz', 'xyzrgb']. Default: 'xyz'. mode ['xyz', 'xyzrgb']. Default: 'xyz'.
""" """
# TODO: support score and class info # TODO: support score and class info
assert 0 <= rot_axis <= 2 assert 0 <= rot_axis <= 2
...@@ -196,21 +203,23 @@ def _draw_bboxes_ind(bbox3d, ...@@ -196,21 +203,23 @@ def _draw_bboxes_ind(bbox3d,
Args: Args:
bbox3d (numpy.array | torch.tensor, shape=[M, 7]): bbox3d (numpy.array | torch.tensor, shape=[M, 7]):
3d bbox (x, y, z, dx, dy, dz, yaw) to visualize. 3d bbox (x, y, z, x_size, y_size, z_size, yaw) to visualize.
vis (:obj:`open3d.visualization.Visualizer`): open3d visualizer. vis (:obj:`open3d.visualization.Visualizer`): open3d visualizer.
indices (numpy.array | torch.tensor, shape=[N, M]): indices (numpy.array | torch.tensor, shape=[N, M]):
indicate which bbox3d that each point lies in. indicate which bbox3d that each point lies in.
points_colors (numpy.array): color of each points. points_colors (numpy.array): color of each points.
pcd (:obj:`open3d.geometry.PointCloud`): point cloud. Default: None. pcd (:obj:`open3d.geometry.PointCloud`, optional): point cloud.
bbox_color (tuple[float]): the color of bbox. Default: (0, 1, 0). Default: None.
points_in_box_color (tuple[float]): bbox_color (tuple[float], optional): the color of bbox.
Default: (0, 1, 0).
points_in_box_color (tuple[float], optional):
the color of points which are in bbox3d. Default: (1, 0, 0). the color of points which are in bbox3d. Default: (1, 0, 0).
rot_axis (int): rotation axis of bbox. Default: 2. rot_axis (int, optional): rotation axis of bbox. Default: 2.
center_mode (bool): indicate the center of bbox is bottom center center_mode (bool, optional): indicate the center of bbox is
or gravity center. avaliable mode bottom center or gravity center. available mode
['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'. ['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'.
mode (str): indicate type of the input points, avaliable mode mode (str, optional): indicate type of the input points,
['xyz', 'xyzrgb']. Default: 'xyz'. available mode ['xyz', 'xyzrgb']. Default: 'xyz'.
""" """
if isinstance(bbox3d, torch.Tensor): if isinstance(bbox3d, torch.Tensor):
bbox3d = bbox3d.cpu().numpy() bbox3d = bbox3d.cpu().numpy()
...@@ -270,24 +279,28 @@ def show_pts_index_boxes(points, ...@@ -270,24 +279,28 @@ def show_pts_index_boxes(points,
points (numpy.array | torch.tensor, shape=[N, 3+C]): points (numpy.array | torch.tensor, shape=[N, 3+C]):
points to visualize. points to visualize.
bbox3d (numpy.array | torch.tensor, shape=[M, 7]): bbox3d (numpy.array | torch.tensor, shape=[M, 7]):
3d bbox (x, y, z, dx, dy, dz, yaw) to visualize. Default: None. 3D bbox (x, y, z, x_size, y_size, z_size, yaw) to visualize.
show (bool): whether to show the visualization results. Default: True. Defaults to None.
indices (numpy.array | torch.tensor, shape=[N, M]): show (bool, optional): whether to show the visualization results.
Default: True.
indices (numpy.array | torch.tensor, shape=[N, M], optional):
indicate which bbox3d that each point lies in. Default: None. indicate which bbox3d that each point lies in. Default: None.
save_path (str): path to save visualized results. Default: None. save_path (str, optional): path to save visualized results.
points_size (int): the size of points to show on visualizer. Default: None.
points_size (int, optional): the size of points to show on visualizer.
Default: 2. Default: 2.
point_color (tuple[float]): the color of points. point_color (tuple[float], optional): the color of points.
Default: (0.5, 0.5, 0.5). Default: (0.5, 0.5, 0.5).
bbox_color (tuple[float]): the color of bbox. Default: (0, 1, 0). bbox_color (tuple[float], optional): the color of bbox.
points_in_box_color (tuple[float]): Default: (0, 1, 0).
points_in_box_color (tuple[float], optional):
the color of points which are in bbox3d. Default: (1, 0, 0). the color of points which are in bbox3d. Default: (1, 0, 0).
rot_axis (int): rotation axis of bbox. Default: 2. rot_axis (int, optional): rotation axis of bbox. Default: 2.
center_mode (bool): indicate the center of bbox is bottom center center_mode (bool, optional): indicate the center of bbox is
or gravity center. avaliable mode bottom center or gravity center. available mode
['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'. ['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'.
mode (str): indicate type of the input points, avaliable mode mode (str, optional): indicate type of the input points,
['xyz', 'xyzrgb']. Default: 'xyz'. available mode ['xyz', 'xyzrgb']. Default: 'xyz'.
""" """
# TODO: support score and class info # TODO: support score and class info
assert 0 <= rot_axis <= 2 assert 0 <= rot_axis <= 2
...@@ -324,24 +337,27 @@ class Visualizer(object): ...@@ -324,24 +337,27 @@ class Visualizer(object):
points (numpy.array, shape=[N, 3+C]): Points to visualize. The Points points (numpy.array, shape=[N, 3+C]): Points to visualize. The Points
cloud is in mode of Coord3DMode.DEPTH (please refer to cloud is in mode of Coord3DMode.DEPTH (please refer to
core.structures.coord_3d_mode). core.structures.coord_3d_mode).
bbox3d (numpy.array, shape=[M, 7]): 3d bbox (x, y, z, dx, dy, dz, yaw) bbox3d (numpy.array, shape=[M, 7], optional): 3D bbox
to visualize. The 3d bbox is in mode of Box3DMode.DEPTH with (x, y, z, x_size, y_size, z_size, yaw) to visualize.
The 3D bbox is in mode of Box3DMode.DEPTH with
gravity_center (please refer to core.structures.box_3d_mode). gravity_center (please refer to core.structures.box_3d_mode).
Default: None. Default: None.
save_path (str): path to save visualized results. Default: None. save_path (str, optional): path to save visualized results.
points_size (int): the size of points to show on visualizer. Default: None.
points_size (int, optional): the size of points to show on visualizer.
Default: 2. Default: 2.
point_color (tuple[float]): the color of points. point_color (tuple[float], optional): the color of points.
Default: (0.5, 0.5, 0.5). Default: (0.5, 0.5, 0.5).
bbox_color (tuple[float]): the color of bbox. Default: (0, 1, 0). bbox_color (tuple[float], optional): the color of bbox.
points_in_box_color (tuple[float]): Default: (0, 1, 0).
points_in_box_color (tuple[float], optional):
the color of points which are in bbox3d. Default: (1, 0, 0). the color of points which are in bbox3d. Default: (1, 0, 0).
rot_axis (int): rotation axis of bbox. Default: 2. rot_axis (int, optional): rotation axis of bbox. Default: 2.
center_mode (bool): indicate the center of bbox is bottom center center_mode (bool, optional): indicate the center of bbox is
or gravity center. avaliable mode bottom center or gravity center. available mode
['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'. ['lidar_bottom', 'camera_bottom']. Default: 'lidar_bottom'.
mode (str): indicate type of the input points, avaliable mode mode (str, optional): indicate type of the input points,
['xyz', 'xyzrgb']. Default: 'xyz'. available mode ['xyz', 'xyzrgb']. Default: 'xyz'.
""" """
def __init__(self, def __init__(self,
...@@ -390,12 +406,13 @@ class Visualizer(object): ...@@ -390,12 +406,13 @@ class Visualizer(object):
Args: Args:
bbox3d (numpy.array, shape=[M, 7]): bbox3d (numpy.array, shape=[M, 7]):
3D bbox (x, y, z, dx, dy, dz, yaw) to be visualized. 3D bbox (x, y, z, x_size, y_size, z_size, yaw)
The 3d bbox is in mode of Box3DMode.DEPTH with to be visualized. The 3d bbox is in mode of
gravity_center (please refer to core.structures.box_3d_mode). Box3DMode.DEPTH with gravity_center (please refer to
bbox_color (tuple[float]): the color of bbox. Defaule: None. core.structures.box_3d_mode).
bbox_color (tuple[float]): the color of bbox. Default: None.
points_in_box_color (tuple[float]): the color of points which points_in_box_color (tuple[float]): the color of points which
are in bbox3d. Defaule: None. are in bbox3d. Default: None.
""" """
if bbox_color is None: if bbox_color is None:
bbox_color = self.bbox_color bbox_color = self.bbox_color
...@@ -431,7 +448,7 @@ class Visualizer(object): ...@@ -431,7 +448,7 @@ class Visualizer(object):
"""Visualize the points cloud. """Visualize the points cloud.
Args: Args:
save_path (str): path to save image. Default: None. save_path (str, optional): path to save image. Default: None.
""" """
self.o3d_visualizer.run() self.o3d_visualizer.run()
......
# 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
import trimesh import trimesh
from os import path as osp
from .image_vis import (draw_camera_bbox3d_on_img, draw_depth_bbox3d_on_img, from .image_vis import (draw_camera_bbox3d_on_img, draw_depth_bbox3d_on_img,
draw_lidar_bbox3d_on_img) draw_lidar_bbox3d_on_img)
...@@ -35,7 +36,7 @@ def _write_oriented_bbox(scene_bbox, out_filename): ...@@ -35,7 +36,7 @@ def _write_oriented_bbox(scene_bbox, out_filename):
Args: Args:
scene_bbox(list[ndarray] or ndarray): xyz pos of center and scene_bbox(list[ndarray] or ndarray): xyz pos of center and
3 lengths (dx,dy,dz) and heading angle around Z axis. 3 lengths (x_size, y_size, z_size) and heading angle around Z axis.
Y forward, X right, Z upward. heading angle of positive X is 0, Y forward, X right, Z upward. heading angle of positive X is 0,
heading angle of positive Y is 90 degrees. heading angle of positive Y is 90 degrees.
out_filename(str): Filename. out_filename(str): Filename.
...@@ -131,16 +132,14 @@ def show_result(points, ...@@ -131,16 +132,14 @@ def show_result(points,
if gt_bboxes is not None: if gt_bboxes is not None:
# bottom center to gravity center # bottom center to gravity center
gt_bboxes[..., 2] += gt_bboxes[..., 5] / 2 gt_bboxes[..., 2] += gt_bboxes[..., 5] / 2
# the positive direction for yaw in meshlab is clockwise
gt_bboxes[:, 6] *= -1
_write_oriented_bbox(gt_bboxes, _write_oriented_bbox(gt_bboxes,
osp.join(result_path, f'{filename}_gt.obj')) osp.join(result_path, f'{filename}_gt.obj'))
if pred_bboxes is not None: if pred_bboxes is not None:
# bottom center to gravity center # bottom center to gravity center
pred_bboxes[..., 2] += pred_bboxes[..., 5] / 2 pred_bboxes[..., 2] += pred_bboxes[..., 5] / 2
# the positive direction for yaw in meshlab is clockwise
pred_bboxes[:, 6] *= -1
_write_oriented_bbox(pred_bboxes, _write_oriented_bbox(pred_bboxes,
osp.join(result_path, f'{filename}_pred.obj')) osp.join(result_path, f'{filename}_pred.obj'))
...@@ -152,7 +151,7 @@ def show_seg_result(points, ...@@ -152,7 +151,7 @@ def show_seg_result(points,
filename, filename,
palette, palette,
ignore_index=None, ignore_index=None,
show=True, show=False,
snapshot=False): snapshot=False):
"""Convert results into format that is directly readable for meshlab. """Convert results into format that is directly readable for meshlab.
...@@ -163,10 +162,10 @@ def show_seg_result(points, ...@@ -163,10 +162,10 @@ def show_seg_result(points,
out_dir (str): Path of output directory out_dir (str): Path of output directory
filename (str): Filename of the current frame. filename (str): Filename of the current frame.
palette (np.ndarray): Mapping between class labels and colors. palette (np.ndarray): Mapping between class labels and colors.
ignore_index (int, optional): The label index to be ignored, e.g. \ ignore_index (int, optional): The label index to be ignored, e.g.
unannotated points. Defaults to None. unannotated points. Defaults to None.
show (bool, optional): Visualize the results online. Defaults to False. show (bool, optional): Visualize the results online. Defaults to False.
snapshot (bool, optional): Whether to save the online results. \ snapshot (bool, optional): Whether to save the online results.
Defaults to False. Defaults to False.
""" """
# we need 3D coordinates to visualize segmentation mask # we need 3D coordinates to visualize segmentation mask
...@@ -226,7 +225,7 @@ def show_multi_modality_result(img, ...@@ -226,7 +225,7 @@ def show_multi_modality_result(img,
filename, filename,
box_mode='lidar', box_mode='lidar',
img_metas=None, img_metas=None,
show=True, show=False,
gt_bbox_color=(61, 102, 255), gt_bbox_color=(61, 102, 255),
pred_bbox_color=(241, 101, 72)): pred_bbox_color=(241, 101, 72)):
"""Convert multi-modality detection results into 2D results. """Convert multi-modality detection results into 2D results.
...@@ -241,14 +240,16 @@ def show_multi_modality_result(img, ...@@ -241,14 +240,16 @@ def show_multi_modality_result(img,
according to the camera intrinsic parameters. according to the camera intrinsic parameters.
out_dir (str): Path of output directory. out_dir (str): Path of output directory.
filename (str): Filename of the current frame. filename (str): Filename of the current frame.
box_mode (str): Coordinate system the boxes are in. Should be one of box_mode (str, optional): Coordinate system the boxes are in.
'depth', 'lidar' and 'camera'. Defaults to 'lidar'. Should be one of 'depth', 'lidar' and 'camera'.
img_metas (dict): Used in projecting depth bbox. Defaults to 'lidar'.
show (bool): Visualize the results online. Defaults to False. img_metas (dict, optional): Used in projecting depth bbox.
gt_bbox_color (str or tuple(int)): Color of bbox lines. Defaults to None.
The tuple of color should be in BGR order. Default: (255, 102, 61) show (bool, optional): Visualize the results online. Defaults to False.
pred_bbox_color (str or tuple(int)): Color of bbox lines. gt_bbox_color (str or tuple(int), optional): Color of bbox lines.
The tuple of color should be in BGR order. Default: (72, 101, 241) The tuple of color should be in BGR order. Default: (255, 102, 61).
pred_bbox_color (str or tuple(int), optional): Color of bbox lines.
The tuple of color should be in BGR order. Default: (72, 101, 241).
""" """
if box_mode == 'depth': if box_mode == 'depth':
draw_bbox = draw_depth_bbox3d_on_img draw_bbox = draw_depth_bbox3d_on_img
......
...@@ -82,18 +82,18 @@ def points_to_voxel(points, ...@@ -82,18 +82,18 @@ def points_to_voxel(points,
"""convert kitti points(N, >=3) to voxels. """convert kitti points(N, >=3) to voxels.
Args: Args:
points (np.ndarray): [N, ndim]. points[:, :3] contain xyz points and \ points (np.ndarray): [N, ndim]. points[:, :3] contain xyz points and
points[:, 3:] contain other information such as reflectivity. points[:, 3:] contain other information such as reflectivity.
voxel_size (list, tuple, np.ndarray): [3] xyz, indicate voxel size voxel_size (list, tuple, np.ndarray): [3] xyz, indicate voxel size
coors_range (list[float | tuple[float] | ndarray]): Voxel range. \ coors_range (list[float | tuple[float] | ndarray]): Voxel range.
format: xyzxyz, minmax format: xyzxyz, minmax
max_points (int): Indicate maximum points contained in a voxel. max_points (int): Indicate maximum points contained in a voxel.
reverse_index (bool): Whether return reversed coordinates. \ reverse_index (bool): Whether return reversed coordinates.
if points has xyz format and reverse_index is True, output \ if points has xyz format and reverse_index is True, output
coordinates will be zyx format, but points in features always \ coordinates will be zyx format, but points in features always
xyz format. xyz format.
max_voxels (int): Maximum number of voxels this function creates. \ max_voxels (int): Maximum number of voxels this function creates.
For second, 20000 is a good choice. Points should be shuffled for \ For second, 20000 is a good choice. Points should be shuffled for
randomness before this function because max_voxels drops points. randomness before this function because max_voxels drops points.
Returns: Returns:
...@@ -147,20 +147,20 @@ def _points_to_voxel_reverse_kernel(points, ...@@ -147,20 +147,20 @@ def _points_to_voxel_reverse_kernel(points,
"""convert kitti points(N, >=3) to voxels. """convert kitti points(N, >=3) to voxels.
Args: Args:
points (np.ndarray): [N, ndim]. points[:, :3] contain xyz points and \ points (np.ndarray): [N, ndim]. points[:, :3] contain xyz points and
points[:, 3:] contain other information such as reflectivity. points[:, 3:] contain other information such as reflectivity.
voxel_size (list, tuple, np.ndarray): [3] xyz, indicate voxel size \ voxel_size (list, tuple, np.ndarray): [3] xyz, indicate voxel size
coors_range (list[float | tuple[float] | ndarray]): Range of voxels. \ coors_range (list[float | tuple[float] | ndarray]): Range of voxels.
format: xyzxyz, minmax format: xyzxyz, minmax
num_points_per_voxel (int): Number of points per voxel. num_points_per_voxel (int): Number of points per voxel.
coor_to_voxel_idx (np.ndarray): A voxel grid of shape (D, H, W), \ coor_to_voxel_idx (np.ndarray): A voxel grid of shape (D, H, W),
which has the same shape as the complete voxel map. It indicates \ which has the same shape as the complete voxel map. It indicates
the index of each corresponding voxel. the index of each corresponding voxel.
voxels (np.ndarray): Created empty voxels. voxels (np.ndarray): Created empty voxels.
coors (np.ndarray): Created coordinates of each voxel. coors (np.ndarray): Created coordinates of each voxel.
max_points (int): Indicate maximum points contained in a voxel. max_points (int): Indicate maximum points contained in a voxel.
max_voxels (int): Maximum number of voxels this function create. \ max_voxels (int): Maximum number of voxels this function create.
for second, 20000 is a good choice. Points should be shuffled for \ for second, 20000 is a good choice. Points should be shuffled for
randomness before this function because max_voxels drops points. randomness before this function because max_voxels drops points.
Returns: Returns:
...@@ -221,20 +221,20 @@ def _points_to_voxel_kernel(points, ...@@ -221,20 +221,20 @@ def _points_to_voxel_kernel(points,
"""convert kitti points(N, >=3) to voxels. """convert kitti points(N, >=3) to voxels.
Args: Args:
points (np.ndarray): [N, ndim]. points[:, :3] contain xyz points and \ points (np.ndarray): [N, ndim]. points[:, :3] contain xyz points and
points[:, 3:] contain other information such as reflectivity. points[:, 3:] contain other information such as reflectivity.
voxel_size (list, tuple, np.ndarray): [3] xyz, indicate voxel size. voxel_size (list, tuple, np.ndarray): [3] xyz, indicate voxel size.
coors_range (list[float | tuple[float] | ndarray]): Range of voxels. \ coors_range (list[float | tuple[float] | ndarray]): Range of voxels.
format: xyzxyz, minmax format: xyzxyz, minmax
num_points_per_voxel (int): Number of points per voxel. num_points_per_voxel (int): Number of points per voxel.
coor_to_voxel_idx (np.ndarray): A voxel grid of shape (D, H, W), \ coor_to_voxel_idx (np.ndarray): A voxel grid of shape (D, H, W),
which has the same shape as the complete voxel map. It indicates \ which has the same shape as the complete voxel map. It indicates
the index of each corresponding voxel. the index of each corresponding voxel.
voxels (np.ndarray): Created empty voxels. voxels (np.ndarray): Created empty voxels.
coors (np.ndarray): Created coordinates of each voxel. coors (np.ndarray): Created coordinates of each voxel.
max_points (int): Indicate maximum points contained in a voxel. max_points (int): Indicate maximum points contained in a voxel.
max_voxels (int): Maximum number of voxels this function create. \ max_voxels (int): Maximum number of voxels this function create.
for second, 20000 is a good choice. Points should be shuffled for \ for second, 20000 is a good choice. Points should be shuffled for
randomness before this function because max_voxels drops points. randomness before this function because max_voxels drops points.
Returns: Returns:
......
...@@ -9,14 +9,15 @@ from .lyft_dataset import LyftDataset ...@@ -9,14 +9,15 @@ from .lyft_dataset import LyftDataset
from .nuscenes_dataset import NuScenesDataset from .nuscenes_dataset import NuScenesDataset
from .nuscenes_mono_dataset import NuScenesMonoDataset from .nuscenes_mono_dataset import NuScenesMonoDataset
# yapf: disable # yapf: disable
from .pipelines import (BackgroundPointsFilter, GlobalAlignment, from .pipelines import (AffineResize, BackgroundPointsFilter, GlobalAlignment,
GlobalRotScaleTrans, IndoorPatchPointSample, GlobalRotScaleTrans, IndoorPatchPointSample,
IndoorPointSample, LoadAnnotations3D, IndoorPointSample, LoadAnnotations3D,
LoadPointsFromFile, LoadPointsFromMultiSweeps, LoadPointsFromDict, LoadPointsFromFile,
NormalizePointsColor, ObjectNameFilter, ObjectNoise, LoadPointsFromMultiSweeps, NormalizePointsColor,
ObjectRangeFilter, ObjectSample, PointSample, ObjectNameFilter, ObjectNoise, ObjectRangeFilter,
PointShuffle, PointsRangeFilter, RandomDropPointsColor, ObjectSample, PointSample, PointShuffle,
RandomFlip3D, RandomJitterPoints, PointsRangeFilter, RandomDropPointsColor, RandomFlip3D,
RandomJitterPoints, RandomShiftScale,
VoxelBasedPointSampler) VoxelBasedPointSampler)
# yapf: enable # yapf: enable
from .s3dis_dataset import S3DISDataset, S3DISSegDataset from .s3dis_dataset import S3DISDataset, S3DISSegDataset
...@@ -38,5 +39,6 @@ __all__ = [ ...@@ -38,5 +39,6 @@ __all__ = [
'Custom3DDataset', 'Custom3DSegDataset', 'LoadPointsFromMultiSweeps', 'Custom3DDataset', 'Custom3DSegDataset', 'LoadPointsFromMultiSweeps',
'WaymoDataset', 'BackgroundPointsFilter', 'VoxelBasedPointSampler', 'WaymoDataset', 'BackgroundPointsFilter', 'VoxelBasedPointSampler',
'get_loading_pipeline', 'RandomDropPointsColor', 'RandomJitterPoints', 'get_loading_pipeline', 'RandomDropPointsColor', 'RandomJitterPoints',
'ObjectNameFilter' 'ObjectNameFilter', 'AffineResize', 'RandomShiftScale',
'LoadPointsFromDict'
] ]
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import platform import platform
from mmcv.utils import Registry, build_from_cfg from mmcv.utils import Registry, build_from_cfg
from mmdet.datasets import DATASETS from mmdet.datasets import DATASETS
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import mmcv
import numpy as np
import tempfile import tempfile
import warnings import warnings
from os import path as osp from os import path as osp
import mmcv
import numpy as np
from torch.utils.data import Dataset from torch.utils.data import Dataset
from mmdet.datasets import DATASETS from mmdet.datasets import DATASETS
...@@ -88,7 +89,7 @@ class Custom3DDataset(Dataset): ...@@ -88,7 +89,7 @@ class Custom3DDataset(Dataset):
index (int): Index of the sample data to get. index (int): Index of the sample data to get.
Returns: Returns:
dict: Data information that will be passed to the data \ dict: Data information that will be passed to the data
preprocessing pipelines. It includes the following keys: preprocessing pipelines. It includes the following keys:
- sample_idx (str): Sample index. - sample_idx (str): Sample index.
...@@ -177,7 +178,7 @@ class Custom3DDataset(Dataset): ...@@ -177,7 +178,7 @@ class Custom3DDataset(Dataset):
"""Get class names of current dataset. """Get class names of current dataset.
Args: Args:
classes (Sequence[str] | str | None): If classes is None, use classes (Sequence[str] | str): If classes is None, use
default CLASSES defined by builtin dataset. If classes is a default CLASSES defined by builtin dataset. If classes is a
string, take it as a file name. The file contains the name of string, take it as a file name. The file contains the name of
classes where each line contains one class name. If classes is classes where each line contains one class name. If classes is
...@@ -207,13 +208,13 @@ class Custom3DDataset(Dataset): ...@@ -207,13 +208,13 @@ class Custom3DDataset(Dataset):
Args: Args:
outputs (list[dict]): Testing results of the dataset. outputs (list[dict]): Testing results of the dataset.
pklfile_prefix (str | None): The prefix of pkl files. It includes pklfile_prefix (str): The prefix of pkl files. It includes
the file path and the prefix of filename, e.g., "a/b/prefix". the file path and the prefix of filename, e.g., "a/b/prefix".
If not specified, a temp file will be created. Default: None. If not specified, a temp file will be created. Default: None.
Returns: Returns:
tuple: (outputs, tmp_dir), outputs is the detection results, \ tuple: (outputs, tmp_dir), outputs is the detection results,
tmp_dir is the temporal directory created for saving json \ tmp_dir is the temporal directory created for saving json
files when ``jsonfile_prefix`` is not specified. files when ``jsonfile_prefix`` is not specified.
""" """
if pklfile_prefix is None: if pklfile_prefix is None:
...@@ -237,11 +238,14 @@ class Custom3DDataset(Dataset): ...@@ -237,11 +238,14 @@ class Custom3DDataset(Dataset):
Args: Args:
results (list[dict]): List of results. results (list[dict]): List of results.
metric (str | list[str]): Metrics to be evaluated. metric (str | list[str], optional): Metrics to be evaluated.
iou_thr (list[float]): AP IoU thresholds. Defaults to None.
show (bool): Whether to visualize. iou_thr (list[float]): AP IoU thresholds. Defaults to (0.25, 0.5).
logger (logging.Logger | str, optional): Logger used for printing
related information during evaluation. Defaults to None.
show (bool, optional): Whether to visualize.
Default: False. Default: False.
out_dir (str): Path to save the visualization results. out_dir (str, optional): Path to save the visualization results.
Default: None. Default: None.
pipeline (list[dict], optional): raw data loading for showing. pipeline (list[dict], optional): raw data loading for showing.
Default: None. Default: None.
...@@ -281,7 +285,7 @@ class Custom3DDataset(Dataset): ...@@ -281,7 +285,7 @@ class Custom3DDataset(Dataset):
"""Get data loading pipeline in self.show/evaluate function. """Get data loading pipeline in self.show/evaluate function.
Args: Args:
pipeline (list[dict] | None): Input pipeline. If None is given, \ pipeline (list[dict]): Input pipeline. If None is given,
get from self.pipeline. get from self.pipeline.
""" """
if pipeline is None: if pipeline is None:
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import mmcv
import numpy as np
import tempfile import tempfile
import warnings import warnings
from os import path as osp from os import path as osp
import mmcv
import numpy as np
from torch.utils.data import Dataset from torch.utils.data import Dataset
from mmdet.datasets import DATASETS from mmdet.datasets import DATASETS
...@@ -32,7 +33,7 @@ class Custom3DSegDataset(Dataset): ...@@ -32,7 +33,7 @@ class Custom3DSegDataset(Dataset):
as input. Defaults to None. as input. Defaults to None.
test_mode (bool, optional): Whether the dataset is in test mode. test_mode (bool, optional): Whether the dataset is in test mode.
Defaults to False. Defaults to False.
ignore_index (int, optional): The label index to be ignored, e.g. \ ignore_index (int, optional): The label index to be ignored, e.g.
unannotated points. If None is given, set to len(self.CLASSES) to unannotated points. If None is given, set to len(self.CLASSES) to
be consistent with PointSegClassMapping function in pipeline. be consistent with PointSegClassMapping function in pipeline.
Defaults to None. Defaults to None.
...@@ -102,7 +103,7 @@ class Custom3DSegDataset(Dataset): ...@@ -102,7 +103,7 @@ class Custom3DSegDataset(Dataset):
index (int): Index of the sample data to get. index (int): Index of the sample data to get.
Returns: Returns:
dict: Data information that will be passed to the data \ dict: Data information that will be passed to the data
preprocessing pipelines. It includes the following keys: preprocessing pipelines. It includes the following keys:
- sample_idx (str): Sample index. - sample_idx (str): Sample index.
...@@ -179,13 +180,13 @@ class Custom3DSegDataset(Dataset): ...@@ -179,13 +180,13 @@ class Custom3DSegDataset(Dataset):
This function is taken from MMSegmentation. This function is taken from MMSegmentation.
Args: Args:
classes (Sequence[str] | str | None): If classes is None, use classes (Sequence[str] | str): If classes is None, use
default CLASSES defined by builtin dataset. If classes is a default CLASSES defined by builtin dataset. If classes is a
string, take it as a file name. The file contains the name of string, take it as a file name. The file contains the name of
classes where each line contains one class name. If classes is classes where each line contains one class name. If classes is
a tuple or list, override the CLASSES defined by the dataset. a tuple or list, override the CLASSES defined by the dataset.
Defaults to None. Defaults to None.
palette (Sequence[Sequence[int]]] | np.ndarray | None): palette (Sequence[Sequence[int]]] | np.ndarray):
The palette of segmentation map. If None is given, random The palette of segmentation map. If None is given, random
palette will be generated. Defaults to None. palette will be generated. Defaults to None.
""" """
...@@ -276,13 +277,13 @@ class Custom3DSegDataset(Dataset): ...@@ -276,13 +277,13 @@ class Custom3DSegDataset(Dataset):
Args: Args:
outputs (list[dict]): Testing results of the dataset. outputs (list[dict]): Testing results of the dataset.
pklfile_prefix (str | None): The prefix of pkl files. It includes pklfile_prefix (str): The prefix of pkl files. It includes
the file path and the prefix of filename, e.g., "a/b/prefix". the file path and the prefix of filename, e.g., "a/b/prefix".
If not specified, a temp file will be created. Default: None. If not specified, a temp file will be created. Default: None.
Returns: Returns:
tuple: (outputs, tmp_dir), outputs is the detection results, \ tuple: (outputs, tmp_dir), outputs is the detection results,
tmp_dir is the temporal directory created for saving json \ tmp_dir is the temporal directory created for saving json
files when ``jsonfile_prefix`` is not specified. files when ``jsonfile_prefix`` is not specified.
""" """
if pklfile_prefix is None: if pklfile_prefix is None:
...@@ -306,7 +307,7 @@ class Custom3DSegDataset(Dataset): ...@@ -306,7 +307,7 @@ class Custom3DSegDataset(Dataset):
Args: Args:
results (list[dict]): List of results. results (list[dict]): List of results.
metric (str | list[str]): Metrics to be evaluated. metric (str | list[str]): Metrics to be evaluated.
logger (logging.Logger | None | str): Logger used for printing logger (logging.Logger | str, optional): Logger used for printing
related information during evaluation. Defaults to None. related information during evaluation. Defaults to None.
show (bool, optional): Whether to visualize. show (bool, optional): Whether to visualize.
Defaults to False. Defaults to False.
...@@ -364,7 +365,7 @@ class Custom3DSegDataset(Dataset): ...@@ -364,7 +365,7 @@ class Custom3DSegDataset(Dataset):
"""Get data loading pipeline in self.show/evaluate function. """Get data loading pipeline in self.show/evaluate function.
Args: Args:
pipeline (list[dict] | None): Input pipeline. If None is given, \ pipeline (list[dict]): Input pipeline. If None is given,
get from self.pipeline. get from self.pipeline.
""" """
if pipeline is None: if pipeline is None:
......
...@@ -206,7 +206,8 @@ class Kitti2DDataset(CustomDataset): ...@@ -206,7 +206,8 @@ class Kitti2DDataset(CustomDataset):
Args: Args:
outputs (list[np.ndarray]): List of arrays storing the inferenced outputs (list[np.ndarray]): List of arrays storing the inferenced
bounding boxes and scores. bounding boxes and scores.
out (str | None): The prefix of output file. Default: None. out (str, optional): The prefix of output file.
Default: None.
Returns: Returns:
list[dict]: A list of dictionaries with the kitti 2D format. list[dict]: A list of dictionaries with the kitti 2D format.
...@@ -222,7 +223,7 @@ class Kitti2DDataset(CustomDataset): ...@@ -222,7 +223,7 @@ class Kitti2DDataset(CustomDataset):
Args: Args:
result_files (str): Path of result files. result_files (str): Path of result files.
eval_types (str): Types of evaluation. Default: None. eval_types (str, optional): Types of evaluation. Default: None.
KITTI dataset only support 'bbox' evaluation type. KITTI dataset only support 'bbox' evaluation type.
Returns: Returns:
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import copy import copy
import mmcv
import numpy as np
import os import os
import tempfile import tempfile
from os import path as osp
import mmcv
import numpy as np
import torch import torch
from mmcv.utils import print_log from mmcv.utils import print_log
from os import path as osp
from mmdet.datasets import DATASETS from mmdet.datasets import DATASETS
from ..core import show_multi_modality_result, show_result from ..core import show_multi_modality_result, show_result
...@@ -47,8 +48,9 @@ class KittiDataset(Custom3DDataset): ...@@ -47,8 +48,9 @@ class KittiDataset(Custom3DDataset):
Defaults to True. Defaults to True.
test_mode (bool, optional): Whether the dataset is in test mode. test_mode (bool, optional): Whether the dataset is in test mode.
Defaults to False. Defaults to False.
pcd_limit_range (list): The range of point cloud used to filter pcd_limit_range (list, optional): The range of point cloud used to
invalid predicted boxes. Default: [0, -40, -3, 70.4, 40, 0.0]. filter invalid predicted boxes.
Default: [0, -40, -3, 70.4, 40, 0.0].
""" """
CLASSES = ('car', 'pedestrian', 'cyclist') CLASSES = ('car', 'pedestrian', 'cyclist')
...@@ -100,14 +102,14 @@ class KittiDataset(Custom3DDataset): ...@@ -100,14 +102,14 @@ class KittiDataset(Custom3DDataset):
index (int): Index of the sample data to get. index (int): Index of the sample data to get.
Returns: Returns:
dict: Data information that will be passed to the data \ dict: Data information that will be passed to the data
preprocessing pipelines. It includes the following keys: preprocessing pipelines. It includes the following keys:
- sample_idx (str): Sample index. - sample_idx (str): Sample index.
- pts_filename (str): Filename of point clouds. - pts_filename (str): Filename of point clouds.
- img_prefix (str | None): Prefix of image files. - img_prefix (str): Prefix of image files.
- img_info (dict): Image info. - img_info (dict): Image info.
- lidar2img (list[np.ndarray], optional): Transformations \ - lidar2img (list[np.ndarray], optional): Transformations
from lidar to different cameras. from lidar to different cameras.
- ann_info (dict): Annotation info. - ann_info (dict): Annotation info.
""" """
...@@ -145,19 +147,38 @@ class KittiDataset(Custom3DDataset): ...@@ -145,19 +147,38 @@ class KittiDataset(Custom3DDataset):
Returns: Returns:
dict: annotation information consists of the following keys: dict: annotation information consists of the following keys:
- gt_bboxes_3d (:obj:`LiDARInstance3DBoxes`): \ - gt_bboxes_3d (:obj:`LiDARInstance3DBoxes`):
3D ground truth bboxes. 3D ground truth bboxes.
- gt_labels_3d (np.ndarray): Labels of ground truths. - gt_labels_3d (np.ndarray): Labels of ground truths.
- gt_bboxes (np.ndarray): 2D ground truth bboxes. - gt_bboxes (np.ndarray): 2D ground truth bboxes.
- gt_labels (np.ndarray): Labels of ground truths. - gt_labels (np.ndarray): Labels of ground truths.
- gt_names (list[str]): Class names of ground truths. - gt_names (list[str]): Class names of ground truths.
- difficulty (int): kitti difficulty. - difficulty (int): Difficulty defined by KITTI.
0, 1, 2 represent xxxxx respectively.
""" """
# Use index to get the annos, thus the evalhook could also use this api # Use index to get the annos, thus the evalhook could also use this api
info = self.data_infos[index] info = self.data_infos[index]
rect = info['calib']['R0_rect'].astype(np.float32) rect = info['calib']['R0_rect'].astype(np.float32)
Trv2c = info['calib']['Tr_velo_to_cam'].astype(np.float32) Trv2c = info['calib']['Tr_velo_to_cam'].astype(np.float32)
if 'plane' in info:
# convert ground plane to velodyne coordinates
reverse = np.linalg.inv(rect @ Trv2c)
(plane_norm_cam,
plane_off_cam) = (info['plane'][:3],
-info['plane'][:3] * info['plane'][3])
plane_norm_lidar = \
(reverse[:3, :3] @ plane_norm_cam[:, None])[:, 0]
plane_off_lidar = (
reverse[:3, :3] @ plane_off_cam[:, None][:, 0] +
reverse[:3, 3])
plane_lidar = np.zeros_like(plane_norm_lidar, shape=(4, ))
plane_lidar[:3] = plane_norm_lidar
plane_lidar[3] = -plane_norm_lidar.T @ plane_off_lidar
else:
plane_lidar = None
difficulty = info['annos']['difficulty'] difficulty = info['annos']['difficulty']
annos = info['annos'] annos = info['annos']
# we need other objects to avoid collision when sample # we need other objects to avoid collision when sample
...@@ -193,6 +214,7 @@ class KittiDataset(Custom3DDataset): ...@@ -193,6 +214,7 @@ class KittiDataset(Custom3DDataset):
bboxes=gt_bboxes, bboxes=gt_bboxes,
labels=gt_labels, labels=gt_labels,
gt_names=gt_names, gt_names=gt_names,
plane=plane_lidar,
difficulty=difficulty) difficulty=difficulty)
return anns_results return anns_results
...@@ -251,17 +273,17 @@ class KittiDataset(Custom3DDataset): ...@@ -251,17 +273,17 @@ class KittiDataset(Custom3DDataset):
Args: Args:
outputs (list[dict]): Testing results of the dataset. outputs (list[dict]): Testing results of the dataset.
pklfile_prefix (str | None): The prefix of pkl files. It includes pklfile_prefix (str): The prefix of pkl files. It includes
the file path and the prefix of filename, e.g., "a/b/prefix". the file path and the prefix of filename, e.g., "a/b/prefix".
If not specified, a temp file will be created. Default: None. If not specified, a temp file will be created. Default: None.
submission_prefix (str | None): The prefix of submitted files. It submission_prefix (str): The prefix of submitted files. It
includes the file path and the prefix of filename, e.g., includes the file path and the prefix of filename, e.g.,
"a/b/prefix". If not specified, a temp file will be created. "a/b/prefix". If not specified, a temp file will be created.
Default: None. Default: None.
Returns: Returns:
tuple: (result_files, tmp_dir), result_files is a dict containing \ tuple: (result_files, tmp_dir), result_files is a dict containing
the json filepaths, tmp_dir is the temporal directory created \ the json filepaths, tmp_dir is the temporal directory created
for saving json files when jsonfile_prefix is not specified. for saving json files when jsonfile_prefix is not specified.
""" """
if pklfile_prefix is None: if pklfile_prefix is None:
...@@ -311,17 +333,19 @@ class KittiDataset(Custom3DDataset): ...@@ -311,17 +333,19 @@ class KittiDataset(Custom3DDataset):
Args: Args:
results (list[dict]): Testing results of the dataset. results (list[dict]): Testing results of the dataset.
metric (str | list[str]): Metrics to be evaluated. metric (str | list[str], optional): Metrics to be evaluated.
logger (logging.Logger | str | None): Logger used for printing Default: None.
logger (logging.Logger | str, optional): Logger used for printing
related information during evaluation. Default: None. related information during evaluation. Default: None.
pklfile_prefix (str | None): The prefix of pkl files. It includes pklfile_prefix (str, optional): The prefix of pkl files, including
the file path and the prefix of filename, e.g., "a/b/prefix". the file path and the prefix of filename, e.g., "a/b/prefix".
If not specified, a temp file will be created. Default: None. If not specified, a temp file will be created. Default: None.
submission_prefix (str | None): The prefix of submission data. submission_prefix (str, optional): The prefix of submission data.
If not specified, the submission data will not be generated. If not specified, the submission data will not be generated.
show (bool): Whether to visualize. Default: None.
show (bool, optional): Whether to visualize.
Default: False. Default: False.
out_dir (str): Path to save the visualization results. out_dir (str, optional): Path to save the visualization results.
Default: None. Default: None.
pipeline (list[dict], optional): raw data loading for showing. pipeline (list[dict], optional): raw data loading for showing.
Default: None. Default: None.
...@@ -361,8 +385,8 @@ class KittiDataset(Custom3DDataset): ...@@ -361,8 +385,8 @@ class KittiDataset(Custom3DDataset):
if tmp_dir is not None: if tmp_dir is not None:
tmp_dir.cleanup() tmp_dir.cleanup()
if show: if show or out_dir:
self.show(results, out_dir, pipeline=pipeline) self.show(results, out_dir, show=show, pipeline=pipeline)
return ap_dict return ap_dict
def bbox2result_kitti(self, def bbox2result_kitti(self,
...@@ -374,11 +398,11 @@ class KittiDataset(Custom3DDataset): ...@@ -374,11 +398,11 @@ class KittiDataset(Custom3DDataset):
submission. submission.
Args: Args:
net_outputs (list[np.ndarray]): List of array storing the \ net_outputs (list[np.ndarray]): List of array storing the
inferenced bounding boxes and scores. inferenced bounding boxes and scores.
class_names (list[String]): A list of class names. class_names (list[String]): A list of class names.
pklfile_prefix (str | None): The prefix of pkl file. pklfile_prefix (str): The prefix of pkl file.
submission_prefix (str | None): The prefix of submission file. submission_prefix (str): The prefix of submission file.
Returns: Returns:
list[dict]: A list of dictionaries with the kitti format. list[dict]: A list of dictionaries with the kitti format.
...@@ -489,11 +513,11 @@ class KittiDataset(Custom3DDataset): ...@@ -489,11 +513,11 @@ class KittiDataset(Custom3DDataset):
submission. submission.
Args: Args:
net_outputs (list[np.ndarray]): List of array storing the \ net_outputs (list[np.ndarray]): List of array storing the
inferenced bounding boxes and scores. inferenced bounding boxes and scores.
class_names (list[String]): A list of class names. class_names (list[String]): A list of class names.
pklfile_prefix (str | None): The prefix of pkl file. pklfile_prefix (str): The prefix of pkl file.
submission_prefix (str | None): The prefix of submission file. submission_prefix (str): The prefix of submission file.
Returns: Returns:
list[dict]: A list of dictionaries have the kitti format list[dict]: A list of dictionaries have the kitti format
...@@ -607,9 +631,9 @@ class KittiDataset(Custom3DDataset): ...@@ -607,9 +631,9 @@ class KittiDataset(Custom3DDataset):
dict: Valid predicted boxes. dict: Valid predicted boxes.
- bbox (np.ndarray): 2D bounding boxes. - bbox (np.ndarray): 2D bounding boxes.
- box3d_camera (np.ndarray): 3D bounding boxes in \ - box3d_camera (np.ndarray): 3D bounding boxes in
camera coordinate. camera coordinate.
- box3d_lidar (np.ndarray): 3D bounding boxes in \ - box3d_lidar (np.ndarray): 3D bounding boxes in
LiDAR coordinate. LiDAR coordinate.
- scores (np.ndarray): Scores of boxes. - scores (np.ndarray): Scores of boxes.
- label_preds (np.ndarray): Class label predictions. - label_preds (np.ndarray): Class label predictions.
...@@ -620,8 +644,6 @@ class KittiDataset(Custom3DDataset): ...@@ -620,8 +644,6 @@ class KittiDataset(Custom3DDataset):
scores = box_dict['scores_3d'] scores = box_dict['scores_3d']
labels = box_dict['labels_3d'] labels = box_dict['labels_3d']
sample_idx = info['image']['image_idx'] sample_idx = info['image']['image_idx']
# TODO: remove the hack of yaw
box_preds.tensor[:, -1] = box_preds.tensor[:, -1] - np.pi
box_preds.limit_yaw(offset=0.5, period=np.pi * 2) box_preds.limit_yaw(offset=0.5, period=np.pi * 2)
if len(box_preds) == 0: if len(box_preds) == 0:
...@@ -701,7 +723,8 @@ class KittiDataset(Custom3DDataset): ...@@ -701,7 +723,8 @@ class KittiDataset(Custom3DDataset):
Args: Args:
results (list[dict]): List of bounding boxes results. results (list[dict]): List of bounding boxes results.
out_dir (str): Output directory of visualization result. out_dir (str): Output directory of visualization result.
show (bool): Visualize the results online. show (bool): Whether to visualize the results online.
Default: False.
pipeline (list[dict], optional): raw data loading for showing. pipeline (list[dict], optional): raw data loading for showing.
Default: None. Default: None.
""" """
......
# Copyright (c) OpenMMLab. All rights reserved. # Copyright (c) OpenMMLab. All rights reserved.
import copy import copy
import tempfile
from os import path as osp
import mmcv import mmcv
import numpy as np import numpy as np
import tempfile
import torch import torch
from mmcv.utils import print_log from mmcv.utils import print_log
from os import path as osp
from mmdet.datasets import DATASETS from mmdet.datasets import DATASETS
from ..core.bbox import Box3DMode, CameraInstance3DBoxes, points_cam2img from ..core.bbox import Box3DMode, CameraInstance3DBoxes, points_cam2img
...@@ -57,8 +58,8 @@ class KittiMonoDataset(NuScenesMonoDataset): ...@@ -57,8 +58,8 @@ class KittiMonoDataset(NuScenesMonoDataset):
with_mask (bool): Whether to parse mask annotations. with_mask (bool): Whether to parse mask annotations.
Returns: Returns:
dict: A dict containing the following keys: bboxes, bboxes_ignore,\ dict: A dict containing the following keys: bboxes, bboxes_ignore,
labels, masks, seg_map. "masks" are raw annotations and not \ labels, masks, seg_map. "masks" are raw annotations and not
decoded into binary masks. decoded into binary masks.
""" """
gt_bboxes = [] gt_bboxes = []
...@@ -147,17 +148,17 @@ class KittiMonoDataset(NuScenesMonoDataset): ...@@ -147,17 +148,17 @@ class KittiMonoDataset(NuScenesMonoDataset):
Args: Args:
outputs (list[dict]): Testing results of the dataset. outputs (list[dict]): Testing results of the dataset.
pklfile_prefix (str | None): The prefix of pkl files. It includes pklfile_prefix (str): The prefix of pkl files. It includes
the file path and the prefix of filename, e.g., "a/b/prefix". the file path and the prefix of filename, e.g., "a/b/prefix".
If not specified, a temp file will be created. Default: None. If not specified, a temp file will be created. Default: None.
submission_prefix (str | None): The prefix of submitted files. It submission_prefix (str): The prefix of submitted files. It
includes the file path and the prefix of filename, e.g., includes the file path and the prefix of filename, e.g.,
"a/b/prefix". If not specified, a temp file will be created. "a/b/prefix". If not specified, a temp file will be created.
Default: None. Default: None.
Returns: Returns:
tuple: (result_files, tmp_dir), result_files is a dict containing \ tuple: (result_files, tmp_dir), result_files is a dict containing
the json filepaths, tmp_dir is the temporal directory created \ the json filepaths, tmp_dir is the temporal directory created
for saving json files when jsonfile_prefix is not specified. for saving json files when jsonfile_prefix is not specified.
""" """
if pklfile_prefix is None: if pklfile_prefix is None:
...@@ -202,22 +203,26 @@ class KittiMonoDataset(NuScenesMonoDataset): ...@@ -202,22 +203,26 @@ class KittiMonoDataset(NuScenesMonoDataset):
pklfile_prefix=None, pklfile_prefix=None,
submission_prefix=None, submission_prefix=None,
show=False, show=False,
out_dir=None): out_dir=None,
pipeline=None):
"""Evaluation in KITTI protocol. """Evaluation in KITTI protocol.
Args: Args:
results (list[dict]): Testing results of the dataset. results (list[dict]): Testing results of the dataset.
metric (str | list[str]): Metrics to be evaluated. metric (str | list[str], optional): Metrics to be evaluated.
logger (logging.Logger | str | None): Logger used for printing Defaults to None.
logger (logging.Logger | str, optional): Logger used for printing
related information during evaluation. Default: None. related information during evaluation. Default: None.
pklfile_prefix (str | None): The prefix of pkl files. It includes pklfile_prefix (str, optional): The prefix of pkl files, including
the file path and the prefix of filename, e.g., "a/b/prefix". the file path and the prefix of filename, e.g., "a/b/prefix".
If not specified, a temp file will be created. Default: None. If not specified, a temp file will be created. Default: None.
submission_prefix (str | None): The prefix of submission datas. submission_prefix (str, optional): The prefix of submission data.
If not specified, the submission data will not be generated. If not specified, the submission data will not be generated.
show (bool): Whether to visualize. show (bool, optional): Whether to visualize.
Default: False. Default: False.
out_dir (str): Path to save the visualization results. out_dir (str, optional): Path to save the visualization results.
Default: None.
pipeline (list[dict], optional): raw data loading for showing.
Default: None. Default: None.
Returns: Returns:
...@@ -255,8 +260,8 @@ class KittiMonoDataset(NuScenesMonoDataset): ...@@ -255,8 +260,8 @@ class KittiMonoDataset(NuScenesMonoDataset):
if tmp_dir is not None: if tmp_dir is not None:
tmp_dir.cleanup() tmp_dir.cleanup()
if show: if show or out_dir:
self.show(results, out_dir) self.show(results, out_dir, show=show, pipeline=pipeline)
return ap_dict return ap_dict
def bbox2result_kitti(self, def bbox2result_kitti(self,
...@@ -268,11 +273,11 @@ class KittiMonoDataset(NuScenesMonoDataset): ...@@ -268,11 +273,11 @@ class KittiMonoDataset(NuScenesMonoDataset):
submission. submission.
Args: Args:
net_outputs (list[np.ndarray]): List of array storing the \ net_outputs (list[np.ndarray]): List of array storing the
inferenced bounding boxes and scores. inferenced bounding boxes and scores.
class_names (list[String]): A list of class names. class_names (list[String]): A list of class names.
pklfile_prefix (str | None): The prefix of pkl file. pklfile_prefix (str): The prefix of pkl file.
submission_prefix (str | None): The prefix of submission file. submission_prefix (str): The prefix of submission file.
Returns: Returns:
list[dict]: A list of dictionaries with the kitti format. list[dict]: A list of dictionaries with the kitti format.
...@@ -383,11 +388,11 @@ class KittiMonoDataset(NuScenesMonoDataset): ...@@ -383,11 +388,11 @@ class KittiMonoDataset(NuScenesMonoDataset):
submission. submission.
Args: Args:
net_outputs (list[np.ndarray]): List of array storing the \ net_outputs (list[np.ndarray]): List of array storing the
inferenced bounding boxes and scores. inferenced bounding boxes and scores.
class_names (list[String]): A list of class names. class_names (list[String]): A list of class names.
pklfile_prefix (str | None): The prefix of pkl file. pklfile_prefix (str): The prefix of pkl file.
submission_prefix (str | None): The prefix of submission file. submission_prefix (str): The prefix of submission file.
Returns: Returns:
list[dict]: A list of dictionaries have the kitti format list[dict]: A list of dictionaries have the kitti format
...@@ -498,7 +503,7 @@ class KittiMonoDataset(NuScenesMonoDataset): ...@@ -498,7 +503,7 @@ class KittiMonoDataset(NuScenesMonoDataset):
Returns: Returns:
dict: Valid predicted boxes. dict: Valid predicted boxes.
- bbox (np.ndarray): 2D bounding boxes. - bbox (np.ndarray): 2D bounding boxes.
- box3d_camera (np.ndarray): 3D bounding boxes in \ - box3d_camera (np.ndarray): 3D bounding boxes in
camera coordinate. camera coordinate.
- scores (np.ndarray): Scores of boxes. - scores (np.ndarray): Scores of boxes.
- label_preds (np.ndarray): Class label predictions. - label_preds (np.ndarray): Class label predictions.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment