Voxel downsampling uses a voxel grid to uniformly downsample the input point cloud. Procedure: - Points are discretized into voxels. - Each occupied voxel generates exactly one point by averaging all points inside. Args: cell_width:
(
self,
cell_width: float,
sigma: float = 0.5,
drop_features: bool = True,
bidx: int = 0,
)
| 466 | return PointCloud(**out_dict) |
| 467 | |
| 468 | def voxel_downsampling( |
| 469 | self, |
| 470 | cell_width: float, |
| 471 | sigma: float = 0.5, |
| 472 | drop_features: bool = True, |
| 473 | bidx: int = 0, |
| 474 | ) -> 'PointCloud': |
| 475 | """ |
| 476 | Voxel downsampling uses a voxel grid to uniformly downsample the input point cloud. |
| 477 | |
| 478 | Procedure: |
| 479 | - Points are discretized into voxels. |
| 480 | - Each occupied voxel generates exactly one point by averaging all points inside. |
| 481 | |
| 482 | Args: |
| 483 | cell_width: |
| 484 | the width of each grid cell. |
| 485 | If <0, return self (do nothing) |
| 486 | sigma: |
| 487 | the sigma used in computing the gaussian weight |
| 488 | |
| 489 | Returns: |
| 490 | """ |
| 491 | if cell_width < 0: |
| 492 | return self |
| 493 | |
| 494 | print(f'voxel downsampling started, original num points = {self.xyz_w.size(1)}') |
| 495 | |
| 496 | assert self.xyz_w.size(0) == 1 |
| 497 | |
| 498 | sigma = sigma * cell_width |
| 499 | |
| 500 | xyz_w = self.extract_valid_attr( |
| 501 | arr=self.xyz_w, |
| 502 | bidx=bidx, |
| 503 | ).unsqueeze(0) # (b=1, n, 3) |
| 504 | |
| 505 | # construct grid |
| 506 | grid_to = xyz_w.max(dim=-2, keepdim=True)[0] + 1.e-3 # (b, 1, 3) |
| 507 | grid_from = xyz_w.min(dim=-2, keepdim=True)[0] - 1.e-3 # (b, 1, 3) |
| 508 | grid_width = grid_to - grid_from # (b, 1, 3) |
| 509 | grid_size = torch.ceil(grid_width / cell_width).long() # (b, 1, 3) |
| 510 | cell_width = grid_width / grid_size.float() # (b, 1, 3) |
| 511 | |
| 512 | # discretize to cell idx |
| 513 | subidxs = torch.floor((xyz_w - grid_from) / cell_width).long() # (b, n, 3) |
| 514 | inds = subidxs[..., 2] + \ |
| 515 | subidxs[..., 1] * grid_size[..., 2] + \ |
| 516 | subidxs[..., 0] * (grid_size[..., 1] * grid_size[..., 2]) # (b, n) |
| 517 | |
| 518 | # remap ind to unique index (remove unused grid_cells) |
| 519 | all_point_clouds = [] |
| 520 | for b in range(self.xyz_w.size(0)): |
| 521 | # xyz_w = self.xyz_w[b, start_idx:] |
| 522 | _, idxs, counts = torch.unique(inds[b], return_inverse=True, return_counts=True) |
| 523 | # idxs: (n,) |
| 524 | # counts: (num_occupied_cells,) |
| 525 | num_occupied_cells = counts.size(0) |
no test coverage detected