""" This code is based on https://github.com/erikwijmans/Pointnet2_PyTorch/blob/master/pointnet2/data/data_utils.py """ from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals import numpy as np def angle_axis(angle, axis): """ Returns a 4x4 rotation matrix that performs a rotation around axis by angle Parameters ---------- angle : float Angle to rotate by axis: np.ndarray Axis to rotate about Returns ------- Tensor 3x3 rotation matrix """ u = axis / np.linalg.norm(axis) cosval, sinval = np.cos(angle), np.sin(angle) # yapf: disable cross_prod_mat = np.array([[0.0, -u[2], u[1]], [u[2], 0.0, -u[0]], [-u[1], u[0], 0.0]]) R = np.array( cosval * np.eye(3) + sinval * cross_prod_mat + (1.0 - cosval) * np.outer(u, u)).astype("float32") return R class PointcloudScale(object): def __init__(self, lo=0.8, hi=1.25): self.lo, self.hi = lo, hi def __call__(self, points): scaler = np.random.uniform(self.lo, self.hi) points[:, 0:3] *= scaler return points class PointcloudRotate(object): def __init__(self, axis=np.array([0.0, 1.0, 0.0])): self.axis = axis def __call__(self, points): rotation_angle = np.random.uniform() * 2 * np.pi rotation_matrix = angle_axis(rotation_angle, self.axis) normals = points.shape[1] > 3 if not normals: return np.matmul(points, rotation_matrix.T) else: pc_xyz = points[:, 0:3] pc_normals = points[:, 3:] points[:, 0:3] = np.matmul(pc_xyz, rotation_matrix.T) points[:, 3:] = np.matmul(pc_normals, rotation_matrix.T) return points class PointcloudTranslate(object): def __init__(self, translate_range=0.1): self.translate_range = translate_range def __call__(self, points): translation = np.random.uniform(-self.translate_range, self.translate_range) points[:, 0:3] += translation return points class PointcloudJitter(object): def __init__(self, std=0.01, clip=0.05): self.std, self.clip = std, clip def __call__(self, points): jittered_data = np.random.normal(loc=0,scale=self.std,size=(points.shape[0],3)) jittered_data = np.clip(jittered_data, -self.clip, self.clip) points[:, 0:3] += jittered_data return points class PointcloudRotatePerturbation(object): def __init__(self, angle_sigma=0.06, angle_clip=0.18): self.angle_sigma, self.angle_clip = angle_sigma, angle_clip def _get_angles(self): angles = np.clip( self.angle_sigma * np.random.randn(3), -self.angle_clip, self.angle_clip ) return angles def __call__(self, points): angles = self._get_angles() Rx = angle_axis(angles[0], np.array([1.0, 0.0, 0.0])) Ry = angle_axis(angles[1], np.array([0.0, 1.0, 0.0])) Rz = angle_axis(angles[2], np.array([0.0, 0.0, 1.0])) rotation_matrix = np.matmul(np.matmul(Rz, Ry), Rx) normals = points.shape[1] > 3 if not normals: return np.matmul(points, rotation_matrix.T) else: pc_xyz = points[:, 0:3] pc_normals = points[:, 3:] points[:, 0:3] = np.matmul(pc_xyz, rotation_matrix.T) points[:, 3:] = np.matmul(pc_normals, rotation_matrix.T) return points class PointcloudRandomInputDropout(object): def __init__(self, max_dropout_ratio=0.875): assert max_dropout_ratio >= 0 and max_dropout_ratio < 1 self.max_dropout_ratio = max_dropout_ratio def __call__(self, points): dropout_ratio = np.random.random() * self.max_dropout_ratio # 0~0.875 drop_idx = np.where(np.random.random((points.shape[0])) <= dropout_ratio)[0] if len(drop_idx) > 0: points[drop_idx] = points[0] # set to the first point return points