def augmentation_transform(self, points, normals=None, verbose=False): """Implementation of an augmentation transform for point clouds.""" ########## # Rotation ########## # Initialize rotation matrix R = np.eye(points.shape[1]) if points.shape[1] == 3: if self.config.augment_rotation == 'vertical': # Create random rotations theta = np.random.rand() * 2 * np.pi c, s = np.cos(theta), np.sin(theta) R = np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]], dtype=np.float32) elif self.config.augment_rotation == 'all': # Choose two random angles for the first vector in polar coordinates theta = np.random.rand() * 2 * np.pi phi = (np.random.rand() - 0.5) * np.pi # Create the first vector in carthesian coordinates u = np.array([np.cos(theta) * np.cos(phi), np.sin(theta) * np.cos(phi), np.sin(phi)]) # Choose a random rotation angle alpha = np.random.rand() * 2 * np.pi # Create the rotation matrix with this vector and angle R = create_3D_rotations(np.reshape(u, (1, -1)), np.reshape(alpha, (1, -1)))[0] R = R.astype(np.float32) ####### # Scale ####### # Choose random scales for each example min_s = self.config.augment_scale_min max_s = self.config.augment_scale_max if self.config.augment_scale_anisotropic: scale = np.random.rand(points.shape[1]) * (max_s - min_s) + min_s else: scale = np.random.rand() * (max_s - min_s) - min_s # Add random symmetries to the scale factor symmetries = np.array(self.config.augment_symmetries).astype(np.int32) symmetries *= np.random.randint(2, size=points.shape[1]) scale = (scale * (1 - symmetries * 2)).astype(np.float32) ####### # Noise ####### noise = (np.random.randn(points.shape[0], points.shape[1]) * self.config.augment_noise).astype(np.float32) ################## # Apply transforms ################## # Do not use np.dot because it is multi-threaded #augmented_points = np.dot(points, R) * scale + noise augmented_points = np.sum(np.expand_dims(points, 2) * R, axis=1) * scale + noise if normals is None: return augmented_points, scale, R else: # Anisotropic scale of the normals thanks to cross product formula normal_scale = scale[[1, 2, 0]] * scale[[2, 0, 1]] augmented_normals = np.dot(normals, R) * normal_scale # Renormalise augmented_normals *= 1 / (np.linalg.norm(augmented_normals, axis=1, keepdims=True) + 1e-6) if verbose: test_p = [np.vstack([points, augmented_points])] test_n = [np.vstack([normals, augmented_normals])] test_l = [np.hstack([points[:, 2]*0, augmented_points[:, 2]*0+1])] show_ModelNet_examples(test_p, test_n, test_l) return augmented_points, augmented_normals, scale, R
def batch_grid_subsampling(points, batches_len, features=None, labels=None, ins_labels=None, sampleDl=0.1, max_p=0, verbose=0, random_grid_orient=True): """ CPP wrapper for a grid subsampling (method = barycenter for points and features) :param points: (N, 3) matrix of input points :param features: optional (N, d) matrix of features (floating number) :param labels: optional (N,) matrix of integer labels :param ins_labels: optional (N,) matrix of integer instance labels :param sampleDl: parameter defining the size of grid voxels :param verbose: 1 to display :return: subsampled points, with features and/or labels depending of the input """ R = None B = len(batches_len) if random_grid_orient: ######################################################## # Create a random rotation matrix for each batch element ######################################################## # Choose two random angles for the first vector in polar coordinates theta = np.random.rand(B) * 2 * np.pi phi = (np.random.rand(B) - 0.5) * np.pi # Create the first vector in carthesian coordinates u = np.vstack([np.cos(theta) * np.cos(phi), np.sin(theta) * np.cos(phi), np.sin(phi)]) # Choose a random rotation angle alpha = np.random.rand(B) * 2 * np.pi # Create the rotation matrix with this vector and angle R = create_3D_rotations(u.T, alpha).astype(np.float32) ################# # Apply rotations ################# i0 = 0 points = points.copy() for bi, length in enumerate(batches_len): # Apply the rotation points[i0:i0 + length, :] = np.sum(np.expand_dims(points[i0:i0 + length, :], 2) * R[bi], axis=1) i0 += length ####################### # Sunsample and realign ####################### if (features is None) and (labels is None): s_points, s_len = cpp_subsampling.subsample_batch(points, batches_len, sampleDl=sampleDl, max_p=max_p, verbose=verbose) if random_grid_orient: i0 = 0 for bi, length in enumerate(s_len): s_points[i0:i0 + length, :] = np.sum(np.expand_dims(s_points[i0:i0 + length, :], 2) * R[bi].T, axis=1) i0 += length return s_points, s_len elif (labels is None): s_points, s_len, s_features = cpp_subsampling.subsample_batch(points, batches_len, features=features, sampleDl=sampleDl, max_p=max_p, verbose=verbose) if random_grid_orient: i0 = 0 for bi, length in enumerate(s_len): # Apply the rotation s_points[i0:i0 + length, :] = np.sum(np.expand_dims(s_points[i0:i0 + length, :], 2) * R[bi].T, axis=1) i0 += length return s_points, s_len, s_features elif (features is None): s_points, s_len, s_labels, s_ins_labels = cpp_subsampling.subsample_batch(points, batches_len, classes=labels, ins_classes=ins_labels, sampleDl=sampleDl, max_p=max_p, verbose=verbose) if random_grid_orient: i0 = 0 for bi, length in enumerate(s_len): # Apply the rotation s_points[i0:i0 + length, :] = np.sum(np.expand_dims(s_points[i0:i0 + length, :], 2) * R[bi].T, axis=1) i0 += length return s_points, s_len, s_labels, s_ins_labels else: s_points, s_len, s_features, s_labels, s_ins_labels = cpp_subsampling.subsample_batch(points, batches_len, features=features, classes=labels, ins_classes=ins_labels, sampleDl=sampleDl, max_p=max_p, verbose=verbose) if random_grid_orient: i0 = 0 for bi, length in enumerate(s_len): # Apply the rotation s_points[i0:i0 + length, :] = np.sum(np.expand_dims(s_points[i0:i0 + length, :], 2) * R[bi].T, axis=1) i0 += length return s_points, s_len, s_features, s_labels, s_ins_labels
def augmentation_transform(self, points, normals=None, verbose=False): """Implementation of an augmentation transform for point clouds.""" ########## # Rotation ########## # Initialize rotation matrix R = np.eye(points.shape[1]) if points.shape[1] == 3: if self.config.augment_rotation == 'vertical': # Create random rotations theta = np.random.rand() * 2 * np.pi c, s = np.cos(theta), np.sin(theta) R = np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]], dtype=np.float32)#只沿着z轴转 elif self.config.augment_rotation == 'all': # Choose two random angles for the first vector in polar coordinates theta = np.random.rand() * 2 * np.pi phi = (np.random.rand() - 0.5) * np.pi # Create the first vector in cartesian coordinates u = np.array([np.cos(theta) * np.cos(phi), np.sin(theta) * np.cos(phi), np.sin(phi)]) # Choose a random rotation angle alpha = np.random.rand() * 2 * np.pi # Create the rotation matrix with this vector and angle R = create_3D_rotations(np.reshape(u, (1, -1)), np.reshape(alpha, (1, -1)))[0] R = R.astype(np.float32) ####### # Scale ####### # Choose random scales for each example min_s = self.config.augment_scale_min max_s = self.config.augment_scale_max if self.config.augment_scale_anisotropic: scale = np.random.rand(points.shape[1]) * (max_s - min_s) + min_s else: scale = np.random.rand() * (max_s - min_s) - min_s # Add random symmetries to the scale factor symmetries = np.array(self.config.augment_symmetries).astype(np.int32)#转化为[ 0 0 0 ] symmetries *= np.random.randint(2, size=points.shape[1])#生成 points.shape[1] 个2以内的整数,不包括2 scale = (scale * (1 - symmetries * 2)).astype(np.float32)#尺度缩放带上对称处理 ####### # Noise ####### noise = (np.random.randn(points.shape[0], points.shape[1]) * self.config.augment_noise).astype(np.float32) ################## # Apply transforms ################## # Do not use np.dot because it is multi-threaded #augmented_points = np.dot(points, R) * scale + noise # point.shape is [N,3,1] after expand_dim R.shape is [3,3] scale.shape is [1,3] '''