import math
import random
from ..core.transforms_interface import PointCloudsTransform, to_tuple
from . import functional as F
__all__ = [
"Scale3d",
"RotateAroundAxis3d",
"Crop3d",
"RandomMove3d",
"Move3d",
"Center3d",
"RandomDropout3d",
"Flip3d",
]
[docs]class Scale3d(PointCloudsTransform):
"""Scale the input point cloud.
Args:
scale_limit (float, float, float): maximum scaling of input point cloud.
Default: (0.1, 0.1, 0.1).
bias (list(float, float, float)): base scaling that is always applied.
List of 3 values to determine the basic scaling. Default: (1, 1, 1).
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(
self, scale_limit=(0.1, 0.1, 0.1), bias=(1, 1, 1), always_apply=False, p=0.5
):
super().__init__(always_apply, p)
self.scale_limit = []
for limit, bias_for_axis in zip(scale_limit, bias):
self.scale_limit.append(to_tuple(limit, bias=bias_for_axis))
def get_params(self):
scale = []
for limit in self.scale_limit:
scale.append(random.uniform(limit[0], limit[1]))
return {"scale": scale}
def apply(self, points, scale=(1, 1, 1), **params):
return F.scale(points, scale)
def apply_to_normals(self, normals, **params):
return normals
def apply_to_features(self, features, **params):
return features
def apply_to_labels(self, labels, **params):
return labels
def get_transform_init_args(self):
return {"scale_limit": self.scale_limit}
[docs]class RotateAroundAxis3d(PointCloudsTransform):
"""Rotate point cloud around axis on random angle.
Args:
rotation_limit (float): maximum rotation of the input point cloud. Default: (pi / 2).
axis (list(float, float, float)): axis around which the point cloud is rotated. Default: (0, 0, 1).
center_point (float, float, float): point around which to rotate. Default: mean points.
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(
self,
rotation_limit=math.pi / 2,
axis=(0, 0, 1),
center_point=None,
always_apply=False,
p=0.5,
):
super().__init__(always_apply, p)
self.rotation_limit = to_tuple(rotation_limit, bias=0)
self.axis = axis
self.center_point = center_point
def get_params(self):
angle = random.uniform(self.rotation_limit[0], self.rotation_limit[1])
return {"angle": angle, "axis": self.axis, "center_point": self.center_point}
def apply(self, points, axis, angle, **params):
return F.rotate_around_axis(points, axis, angle, center_point=self.center_point)
def apply_to_normals(self, normals, axis, angle, **params):
return F.rotate_around_axis(normals, axis, angle, center_point=(0, 0, 0))
def apply_to_features(self, features, **params):
return features
def apply_to_labels(self, labels, **params):
return labels
def get_transform_init_args(self):
return {
"rotation_limit": to_tuple(self.rotation_limit, bias=0),
"axis": self.axis,
}
[docs]class Crop3d(PointCloudsTransform):
"""Crop region from image.
Args:
x_min (float): Minimum x coordinate.
y_min (float): Minimum y coordinate.
z_min (float): Minimum z coordinate.
x_max (float): Maximum x coordinate.
y_max (float): Maximum y coordinate.
z_max (float): Maximum z coordinate.
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(
self,
x_min=-math.inf,
y_min=-math.inf,
z_min=-math.inf,
x_max=math.inf,
y_max=math.inf,
z_max=math.inf,
always_apply=False,
p=1.0,
):
super().__init__(always_apply, p)
self.x_min = x_min
self.y_min = y_min
self.z_min = z_min
self.x_max = x_max
self.y_max = y_max
self.z_max = z_max
@property
def targets_as_params(self):
return ["points"]
def get_params_dependent_on_targets(self, params):
return {
"indexes": F.crop(
params["points"],
x_min=self.x_min,
y_min=self.y_min,
z_min=self.z_min,
x_max=self.x_max,
y_max=self.y_max,
z_max=self.z_max,
)
}
def apply(self, points, indexes, **params):
return points[indexes]
def apply_to_normals(self, normals, indexes, **params):
return normals[indexes]
def apply_to_labels(self, labels, indexes, **params):
return labels[indexes]
def apply_to_features(self, features, indexes, **params):
return features[indexes]
def get_transform_init_args_names(self):
return ("x_min", "y_min", "z_min", "x_max", "y_max", "z_max")
[docs]class Center3d(PointCloudsTransform):
"""Move average of point cloud and move it to coordinate (0,0,0).
Args:
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(self, offset=(0, 0, 0), always_apply=False, p=0.5):
super().__init__(always_apply, p)
self.offset = offset
def apply(self, points, **params):
return F.move(F.center(points), self.offset)
def apply_to_normals(self, normals, **params):
return normals
def apply_to_features(self, features, **params):
return features
def apply_to_labels(self, labels, **params):
return labels
def get_transform_init_args(self):
return {
"offset": self.offset,
}
[docs]class Move3d(PointCloudsTransform):
"""Move point cloud on offset.
Args:
offset (float): coorinate where to move origin of coordinate frame. Default: 0.
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(self, offset=(0, 0, 0), always_apply=False, p=1.0):
super().__init__(always_apply, p)
self.offset = offset
def get_params(self):
return {"offset": self.offset}
def apply(self, points, offset, **params):
return F.move(points, offset)
def apply_to_normals(self, normals, **params):
return normals
def apply_to_features(self, features, **params):
return features
def apply_to_labels(self, labels, **params):
return labels
def get_transform_init_args(self):
return {
"offset": self.offset,
}
[docs]class RandomMove3d(Move3d):
"""Move point cloud on random offset.
Args:
x_min (float): Minimum x coordinate. Default: -1.
y_min (float): Minimum y coordinate. Default: -1.
z_min (float): Minimum z coordinate. Default: -1.
x_max (float): Maximum x coordinate. Default: 1.
y_max (float): Maximum y coordinate. Default: 1.
z_max (float): Maximum z coordinate. Default: 1.
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(
self,
x_min=-1.0,
y_min=-1.0,
z_min=-1.0,
x_max=1.0,
y_max=1.0,
z_max=1.0,
offset=(0, 0, 0),
always_apply=False,
p=0.5,
):
super().__init__(offset, always_apply, p)
self.x_min = x_min
self.y_min = y_min
self.z_min = z_min
self.x_max = x_max
self.y_max = y_max
self.z_max = z_max
def get_params(self):
offset = [
random.uniform(self.x_min, self.x_max),
random.uniform(self.y_min, self.y_max),
random.uniform(self.z_min, self.z_max),
]
return {"offset": offset}
def get_transform_init_args_names(self):
return {
"offset": self.offset,
"x_min": self.x_min,
"y_min": self.y_min,
"z_min": self.z_min,
"x_max": self.x_max,
"y_max": self.y_max,
"z_max": self.z_max,
}
[docs]class RandomDropout3d(PointCloudsTransform):
"""Randomly drop points from point cloud.
Args:
dropout_ratio (float): Percent of points to drop. Default: 0.2.
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(self, dropout_ratio=0.2, always_apply=False, p=0.5):
super().__init__(always_apply, p)
self.dropout_ratio = dropout_ratio
@property
def targets_as_params(self):
return ["points"]
def get_params_dependent_on_targets(self, params):
points_len = len(params["points"])
indexes = random.sample(
range(points_len), k=int(points_len * (1 - self.dropout_ratio))
)
sorted_indexes = sorted(indexes)
return {"indexes": sorted_indexes}
def apply(self, points, indexes, **params):
return points[indexes]
def apply_to_normals(self, normals, indexes, **params):
return normals[indexes]
def apply_to_labels(self, labels, indexes, **params):
return labels[indexes]
def apply_to_features(self, features, indexes, **params):
return features[indexes]
def get_transform_init_args(self):
return {"dropout_ratio": self.dropout_ratio}
[docs]class Flip3d(PointCloudsTransform):
"""Flip point cloud around axis
Implemented as rotation on 180 deg around axis.
Args:
axis (list(float, float, float)): Axis to flip the point cloud around. Default: 0.2.
p (float): probability of applying the transform. Default: 0.5.
Targets:
points
normals
features
labels
"""
def __init__(self, axis=(1, 0, 0), always_apply=False, p=0.5):
super().__init__(always_apply, p)
self.axis = axis
def apply(self, points, **params):
points = F.flip_coordinates(points, self.axis)
return points
def apply_to_normals(self, normals, **params):
normals[:, self.axis] = -normals[:, self.axis]
return normals
def apply_to_features(self, features, **params):
return features
def apply_to_labels(self, labels, **params):
return labels
def get_transform_init_args(self):
return {"axis": self.axis}