torch-pointcloud 0.0.2__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/.gitignore +1 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/PKG-INFO +4 -4
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/README.md +3 -3
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/pyproject.toml +1 -1
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/_utils.py +48 -6
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/inferer.py +21 -15
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/knn_window.py +46 -28
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/potential_sphere.py +32 -19
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/sliding_window.py +132 -61
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/tta.py +26 -22
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/voxel_partition.py +44 -22
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/serialized_attention.py +59 -20
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/lightning/metrics.py +80 -53
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/center.py +3 -3
- torch_pointcloud-0.0.4/src/torch_pointcloud/metrics/__init__.py +20 -0
- torch_pointcloud-0.0.4/src/torch_pointcloud/metrics/classification.py +139 -0
- torch_pointcloud-0.0.4/src/torch_pointcloud/metrics/detection.py +380 -0
- torch_pointcloud-0.0.4/src/torch_pointcloud/metrics/instance_segmentation.py +359 -0
- torch_pointcloud-0.0.4/src/torch_pointcloud/metrics/nuscenes.py +404 -0
- torch_pointcloud-0.0.4/src/torch_pointcloud/metrics/segmentation.py +310 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/_base.py +10 -4
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/concerto.py +8 -4
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/detr3d.py +3 -3
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/dgcnn.py +1 -8
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/kpconv.py +13 -14
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/octformer.py +13 -12
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/point_bert.py +10 -4
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/point_m2ae.py +2 -9
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/point_mae.py +2 -7
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/point_mamba.py +2 -2
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/point_transformer.py +9 -9
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/point_transformer_v2.py +11 -12
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/point_transformer_v3.py +44 -21
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointcnn.py +12 -22
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointconv.py +9 -17
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointmlp.py +12 -22
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointnet2.py +8 -8
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointnext.py +23 -34
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pvcnn.py +16 -12
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pvcnn2.py +17 -11
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/randlanet.py +14 -20
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/sonata.py +8 -4
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/spformer_unet.py +41 -14
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/spunet.py +40 -16
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/spvcnn.py +45 -24
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/utonia.py +4 -4
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/voxel_mamba.py +2 -2
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/transforms/transforms.py +2 -2
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/__init__.py +1 -1
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/box3d.py +22 -6
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/data.py +1 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/geometry.py +2 -2
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/heatmap.py +8 -8
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/serialization.py +3 -3
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/types.py +26 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/voxelization.py +5 -5
- torch_pointcloud-0.0.2/src/torch_pointcloud/utils/metrics.py +0 -1191
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/LICENSE +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/THIRD_PARTY_NOTICES.md +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/config.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/concat.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/kitti.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/mix.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/modelnet.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/nuscenes.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/parislille3d.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/pointcloud.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/repeat.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/s3dis.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/scannet.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/scanobjectnn.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/semantic3d.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/semantickitti.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/shapenetpart.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/sunrgbd.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/toronto3d.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/datasets/utils.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/part_refinement.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/simple.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/_modules.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/act.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/affine.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/anchors.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/bev_backbone.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/conv2d_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/conv3d_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/dropouts.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/fps.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/geometric_affine.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/grid_pool.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/linear_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/norms.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/octree_attention.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/octree_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/pdnorm.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/point_patch_embed.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/pointconv.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/pointconv_sa.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/pointnet2_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/pointnext_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/pools.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/pvcnn_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/rope.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/serialized_pool.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/spconv_blocks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/tnet.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/transformer.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/vfe.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/view.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/layers/xconv.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/lightning/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/lightning/callbacks.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/lightning/datamodule.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/lightning/module.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/_utils.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/anchor.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/chamfer.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/detr3d.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/lovasz.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/pointrcnn.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/sum.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/transfusion.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/losses/votenet.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/_registry.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/lion.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointgpt.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointnet.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointpillars.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/pointrcnn.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/second.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/sphereformer.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/votenet.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/models/voxelnext.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/py.typed +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/transforms/__init__.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/transforms/functional.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/cluster.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/conversion.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/diffusion.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/ensemble.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/hilbert.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/imports.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/io.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/misc.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/neighbors.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/octree.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/ops.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/optim.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/random.py +0 -0
- {torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/utils/state_dict.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: torch-pointcloud
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: PyTorch Point Cloud models, scripts, pretrained weights and more
|
|
5
5
|
Project-URL: Homepage, https://github.com/arthurdjn/pytorch-pointcloud
|
|
6
6
|
Project-URL: Repository, https://github.com/arthurdjn/pytorch-pointcloud
|
|
@@ -51,7 +51,7 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
[](https://github.com/arthurdjn/pytorch-pointcloud/actions/workflows/test.yml)
|
|
52
52
|
[](https://pypi.org/project/torch-pointcloud/)
|
|
53
53
|
[](https://github.com/arthurdjn/pytorch-pointcloud/blob/main/LICENSE)
|
|
54
|
-
[](https://doi.org/10.5281/zenodo.22159632)
|
|
55
55
|
[](https://pytorch-pointcloud.org/)
|
|
56
56
|
|
|
57
57
|
</div>
|
|
@@ -158,7 +158,7 @@ Every checkpoint ships the transform that turns a raw point cloud into what the
|
|
|
158
158
|
# Requires torch-scatter, torch-cluster, spconv
|
|
159
159
|
model, info = tp.create_model("ptv3-base.scannet20.pointcept", task="segmentation", pretrained=True, return_info=True)
|
|
160
160
|
info["transform"] # the preprocessing pipeline of that checkpoint
|
|
161
|
-
info["weights"]["metrics"] # {"mIoU":
|
|
161
|
+
info["weights"]["metrics"] # {"mIoU": 77.40}
|
|
162
162
|
|
|
163
163
|
tp.list_models("pointnext*") # every registered PointNeXt config
|
|
164
164
|
tp.list_models(task="detection", pretrained=True) # all detection checkpoints
|
|
@@ -186,7 +186,7 @@ If you find this project useful, please consider citing:
|
|
|
186
186
|
title = {PyTorch PointCloud},
|
|
187
187
|
year = {2026},
|
|
188
188
|
url = {https://github.com/arthurdjn/pytorch-pointcloud},
|
|
189
|
-
doi = {10.5281/zenodo.
|
|
189
|
+
doi = {10.5281/zenodo.22159632},
|
|
190
190
|
license = {Apache-2.0}
|
|
191
191
|
}
|
|
192
192
|
```
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://github.com/arthurdjn/pytorch-pointcloud/actions/workflows/test.yml)
|
|
9
9
|
[](https://pypi.org/project/torch-pointcloud/)
|
|
10
10
|
[](https://github.com/arthurdjn/pytorch-pointcloud/blob/main/LICENSE)
|
|
11
|
-
[](https://doi.org/10.5281/zenodo.22159632)
|
|
12
12
|
[](https://pytorch-pointcloud.org/)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
@@ -115,7 +115,7 @@ Every checkpoint ships the transform that turns a raw point cloud into what the
|
|
|
115
115
|
# Requires torch-scatter, torch-cluster, spconv
|
|
116
116
|
model, info = tp.create_model("ptv3-base.scannet20.pointcept", task="segmentation", pretrained=True, return_info=True)
|
|
117
117
|
info["transform"] # the preprocessing pipeline of that checkpoint
|
|
118
|
-
info["weights"]["metrics"] # {"mIoU":
|
|
118
|
+
info["weights"]["metrics"] # {"mIoU": 77.40}
|
|
119
119
|
|
|
120
120
|
tp.list_models("pointnext*") # every registered PointNeXt config
|
|
121
121
|
tp.list_models(task="detection", pretrained=True) # all detection checkpoints
|
|
@@ -143,7 +143,7 @@ If you find this project useful, please consider citing:
|
|
|
143
143
|
title = {PyTorch PointCloud},
|
|
144
144
|
year = {2026},
|
|
145
145
|
url = {https://github.com/arthurdjn/pytorch-pointcloud},
|
|
146
|
-
doi = {10.5281/zenodo.
|
|
146
|
+
doi = {10.5281/zenodo.22159632},
|
|
147
147
|
license = {Apache-2.0}
|
|
148
148
|
}
|
|
149
149
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""Shared inferer helpers: dict indexing, chunk splitting, and Gaussian distance weighting."""
|
|
1
|
+
"""Shared inferer helpers: dict indexing, per-fragment transforms, chunk splitting, and Gaussian distance weighting."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, Dict, List, Optional, Union
|
|
3
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
4
4
|
|
|
5
5
|
import torch
|
|
6
6
|
from torch import Tensor
|
|
@@ -21,6 +21,47 @@ def index_select_dict(data: Dict[str, Any], idx: Tensor, n_points: int) -> Dict[
|
|
|
21
21
|
return out
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def apply_transform(
|
|
25
|
+
sample: Dict[str, Any],
|
|
26
|
+
transform: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]],
|
|
27
|
+
*,
|
|
28
|
+
pos_key: str,
|
|
29
|
+
inverse_key: Optional[str],
|
|
30
|
+
) -> Tuple[Dict[str, Any], Optional[Tensor]]:
|
|
31
|
+
r"""Run the per-fragment `transform` over `sample` and return it with its source-to-predictor row map.
|
|
32
|
+
|
|
33
|
+
Any value already at `inverse_key` is dropped before `transform` runs, so a scene-level `inverse` never
|
|
34
|
+
becomes the prior the fragment's own map composes through. The map the transform writes under `inverse_key`
|
|
35
|
+
is popped from the returned sample, so it never reaches the predictor.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
sample: Data dict of one fragment (block, sub-cloud, sphere).
|
|
39
|
+
transform: Callable applied to `sample`, or `None` to leave it unchanged.
|
|
40
|
+
pos_key: Dict key for the position tensor, whose row count defines the fragment size.
|
|
41
|
+
inverse_key: Dict key under which a row-altering `transform` records its long index map of shape
|
|
42
|
+
$(N_\text{source},)$ with values in $[0, N_\text{predictor})$. `None` when rows are preserved.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
The transformed sample and its inverse map, `None` when the transform wrote none. Predictions of shape
|
|
46
|
+
$(N_\text{predictor}, C)$ index with the map back to the $N_\text{source}$ rows of `sample`.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ValueError: If `transform` changes the row count without writing a map under `inverse_key`.
|
|
50
|
+
"""
|
|
51
|
+
n_source = int(sample[pos_key].size(0))
|
|
52
|
+
sample = {key: value for key, value in sample.items() if key != inverse_key}
|
|
53
|
+
if transform is not None:
|
|
54
|
+
sample = transform(sample)
|
|
55
|
+
inverse_map = sample.pop(inverse_key, None) if inverse_key is not None else None
|
|
56
|
+
n_predictor = int(sample[pos_key].size(0))
|
|
57
|
+
if inverse_map is None and n_predictor != n_source:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"`transform` changed the fragment's row count ({n_source} -> {n_predictor}) without recording an "
|
|
60
|
+
f"index map. Make it write one (e.g. `dst_inverse_key`) and pass that key as `inverse_key`."
|
|
61
|
+
)
|
|
62
|
+
return sample, inverse_map
|
|
63
|
+
|
|
64
|
+
|
|
24
65
|
def check_batch_alignment(pos: Tensor, batch: Tensor, pos_key: str, batch_key: str) -> None:
|
|
25
66
|
"""Raise when the per-point batch index does not line up with the positions row for row.
|
|
26
67
|
|
|
@@ -35,7 +76,7 @@ def check_batch_alignment(pos: Tensor, batch: Tensor, pos_key: str, batch_key: s
|
|
|
35
76
|
)
|
|
36
77
|
|
|
37
78
|
|
|
38
|
-
def split_chunks(n: int, max_size: Optional[int], rng: torch.Generator) -> List[Tensor]:
|
|
79
|
+
def split_chunks(n: int, max_size: Optional[int], rng: Optional[torch.Generator], device: torch.device) -> List[Tensor]:
|
|
39
80
|
r"""Partition `range(n)` into index chunks of at most `max_size` points each.
|
|
40
81
|
|
|
41
82
|
When `max_size` is `None` or `n` is within `max_size`, returns a single in-order
|
|
@@ -46,13 +87,14 @@ def split_chunks(n: int, max_size: Optional[int], rng: torch.Generator) -> List[
|
|
|
46
87
|
Args:
|
|
47
88
|
n: Number of indices to partition.
|
|
48
89
|
max_size: Maximum points per chunk, or `None` for a single unsplit chunk.
|
|
49
|
-
rng: Generator for the permutation. Only consumed when a split
|
|
90
|
+
rng: Generator for the permutation, or `None` for the global generator. Only consumed when a split
|
|
91
|
+
is needed.
|
|
92
|
+
device: Device of the returned indices, which a given `rng` must live on.
|
|
50
93
|
|
|
51
94
|
Returns:
|
|
52
|
-
List of 1-D `long` index tensors on `
|
|
95
|
+
List of 1-D `long` index tensors on `device`. The chunks partition
|
|
53
96
|
`range(n)` exactly: every index appears in one and only one chunk.
|
|
54
97
|
"""
|
|
55
|
-
device = rng.device
|
|
56
98
|
if max_size is None:
|
|
57
99
|
return [torch.arange(n, device=device)]
|
|
58
100
|
if n > max_size:
|
|
@@ -27,21 +27,27 @@ class Inferer(metaclass=ABCMeta):
|
|
|
27
27
|
|
|
28
28
|
`data` is a packed-batch dict (at minimum containing position and batch indices).
|
|
29
29
|
`predictor` is any callable taking such a dict and returning per-point logits of shape
|
|
30
|
-
$(N, C_\text{out})$. Inferers
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
| `
|
|
43
|
-
|
|
|
44
|
-
| `
|
|
30
|
+
$(N, C_\text{out})$. Inferers keep nothing of the scene, so one instance can be
|
|
31
|
+
reused across scenes and wrapped by another inferer.
|
|
32
|
+
|
|
33
|
+
Inferers that draw random numbers take a `seed`. `None` follows the global generator,
|
|
34
|
+
like the transforms do, so one `torch.manual_seed` (or `seed_everything`) seeds the
|
|
35
|
+
whole evaluation. An int gives the inferer its own stream, offset by the number of
|
|
36
|
+
calls the instance has made: successive calls (e.g. `TTAInferer` votes) draw different
|
|
37
|
+
numbers, and a fresh instance replays the same sequence.
|
|
38
|
+
|
|
39
|
+
The `softmax` parameter controls whether partial predictions are converted to softmax
|
|
40
|
+
probabilities before aggregation, and `aggregate` how they are combined:
|
|
41
|
+
|
|
42
|
+
| Inferer | `softmax` | Aggregated quantity |
|
|
43
|
+
| ------------------------ | --------- | ----------------------------------------------------------- |
|
|
44
|
+
| `SimpleInferer` | `False` | predictor output as-is |
|
|
45
|
+
| `SlidingWindowInferer` | `True` | softmax probabilities per block (`"max"` / `"vote"` always) |
|
|
46
|
+
| `KNNWindowInferer` | `False` | raw logits (`"mean"`); always probabilities (`"ema"`) |
|
|
47
|
+
| `VoxelPartitionInferer` | `False` | raw logits per pass |
|
|
48
|
+
| `PotentialSphereInferer` | none | always an EMA of softmax probabilities |
|
|
49
|
+
| `TTAInferer` | `False` | base output as-is |
|
|
50
|
+
| `PartRefinementInferer` | none | one-hot refined labels of the base output's argmax |
|
|
45
51
|
|
|
46
52
|
When the input scene is empty ($N = 0$), inferers that never call the predictor
|
|
47
53
|
return a $(0, 0)$ tensor (the channel count cannot be inferred without a predictor
|
{torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/knn_window.py
RENAMED
|
@@ -19,11 +19,11 @@ from tqdm import tqdm
|
|
|
19
19
|
|
|
20
20
|
from torch_pointcloud.utils.data import DataKeys
|
|
21
21
|
|
|
22
|
-
from ._utils import check_batch_alignment, gaussian_weights, index_select_dict
|
|
22
|
+
from ._utils import apply_transform, check_batch_alignment, gaussian_weights, index_select_dict
|
|
23
23
|
from .inferer import Inferer
|
|
24
24
|
|
|
25
25
|
WindowMode = Literal["constant", "gaussian"]
|
|
26
|
-
AggregateMode = Literal["
|
|
26
|
+
AggregateMode = Literal["mean", "ema"]
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def _knn_centers(pos_src: Tensor, centers: Tensor, k: int) -> Tensor:
|
|
@@ -62,12 +62,13 @@ def knn_window_inference(
|
|
|
62
62
|
overlap: float = 0.5,
|
|
63
63
|
mode: WindowMode = "constant",
|
|
64
64
|
sigma_scale: float = 0.125,
|
|
65
|
-
aggregate: AggregateMode = "
|
|
65
|
+
aggregate: AggregateMode = "mean",
|
|
66
66
|
ema_smoothing: float = 0.95,
|
|
67
67
|
softmax: bool = False,
|
|
68
68
|
transform: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None,
|
|
69
69
|
pos_key: str = DataKeys.POS,
|
|
70
70
|
batch_key: str = DataKeys.BATCH,
|
|
71
|
+
inverse_key: Optional[str] = None,
|
|
71
72
|
progress: bool = False,
|
|
72
73
|
seed: Optional[int] = None,
|
|
73
74
|
) -> Tensor:
|
|
@@ -81,7 +82,7 @@ def knn_window_inference(
|
|
|
81
82
|
|
|
82
83
|
`aggregate` controls how overlapping window predictions are combined:
|
|
83
84
|
|
|
84
|
-
- `"
|
|
85
|
+
- `"mean"`: accumulates distance-weighted logits and divides by total
|
|
85
86
|
weight at the end, producing a weighted-average logit tensor.
|
|
86
87
|
- `"ema"`: per-update softmax EMA
|
|
87
88
|
($\text{new} = \alpha \cdot \text{old} + (1 - \alpha) \cdot \text{softmax}(\text{logits})$),
|
|
@@ -109,10 +110,10 @@ def knn_window_inference(
|
|
|
109
110
|
mode: Distance weighting for each window. `"constant"` gives equal weight
|
|
110
111
|
to every point; `"gaussian"` weights by $\exp(-d^2 / 2\sigma^2)$ with
|
|
111
112
|
$\sigma = \text{sigma\_scale} \cdot \max_i d_i$. `"gaussian"` requires
|
|
112
|
-
`aggregate="
|
|
113
|
+
`aggregate="mean"`; EMA updates ignore distance weights.
|
|
113
114
|
sigma_scale: Gaussian sigma scale factor (only used when `mode="gaussian"`).
|
|
114
115
|
aggregate: How predictions from overlapping windows are combined.
|
|
115
|
-
`"
|
|
116
|
+
`"mean"`: weighted-average logits (divide by total weight at end).
|
|
116
117
|
`"ema"`: softmax EMA
|
|
117
118
|
($\text{new} = \alpha \cdot \text{old} + (1 - \alpha) \cdot \text{softmax}(\text{logits})$);
|
|
118
119
|
use `sw_batch_size=1` with EMA to match the reference evaluation protocol.
|
|
@@ -123,14 +124,23 @@ def knn_window_inference(
|
|
|
123
124
|
softmax probabilities.
|
|
124
125
|
transform: Optional callable applied to each window's data dict before the
|
|
125
126
|
predictor (typical example: `T.Shift(keys=DataKeys.POS, method="centroid")`).
|
|
127
|
+
If it changes the row count (pad, voxelize, ...) it must record a
|
|
128
|
+
source-to-predictor index map under `inverse_key` so the inferer can gather
|
|
129
|
+
predictions back to the window's points.
|
|
126
130
|
pos_key: Dict key for the position tensor.
|
|
127
131
|
batch_key: Dict key for the per-point batch index.
|
|
132
|
+
inverse_key: Dict key under which a row-altering `transform` records a
|
|
133
|
+
source-to-predictor long index map of shape $(k,)$ with values in
|
|
134
|
+
$[0, N_\text{predictor})$. Any scene-level value at this key is dropped before
|
|
135
|
+
`transform` runs, and the map is popped before the predictor is called. Leave
|
|
136
|
+
`None` when the transform preserves row count.
|
|
128
137
|
progress: If `True`, show a `tqdm` progress bar per batch element.
|
|
129
|
-
seed: RNG seed for the per-point initial possibility scores.
|
|
138
|
+
seed: RNG seed for the per-point initial possibility scores. `None` draws from the global generator, so
|
|
139
|
+
`torch.manual_seed` seeds the inferer together with the transforms.
|
|
130
140
|
|
|
131
141
|
Returns:
|
|
132
142
|
Per-point output tensor of shape $(N, C_\text{out})$. Aggregation produces
|
|
133
|
-
logits when `aggregate="
|
|
143
|
+
logits when `aggregate="mean"` (probabilities with `softmax=True`)
|
|
134
144
|
and probabilities when `aggregate="ema"`. An empty scene ($N = 0$) returns
|
|
135
145
|
a $(0, 0)$ tensor: the predictor is never called, so the channel count
|
|
136
146
|
cannot be inferred.
|
|
@@ -143,8 +153,8 @@ def knn_window_inference(
|
|
|
143
153
|
raise ValueError(f"`overlap` must be in (0, 1), got {overlap}.")
|
|
144
154
|
if mode not in ("constant", "gaussian"):
|
|
145
155
|
raise ValueError(f"`mode` must be 'constant' or 'gaussian', got {mode!r}.")
|
|
146
|
-
if aggregate not in ("
|
|
147
|
-
raise ValueError(f"`aggregate` must be '
|
|
156
|
+
if aggregate not in ("mean", "ema"):
|
|
157
|
+
raise ValueError(f"`aggregate` must be 'mean' or 'ema', got {aggregate!r}.")
|
|
148
158
|
if sw_batch_size < 1:
|
|
149
159
|
raise ValueError(f"`sw_batch_size` must be >= 1, got {sw_batch_size}.")
|
|
150
160
|
if not 0.0 <= ema_smoothing < 1.0:
|
|
@@ -152,7 +162,7 @@ def knn_window_inference(
|
|
|
152
162
|
if aggregate == "ema" and mode == "gaussian":
|
|
153
163
|
raise ValueError(
|
|
154
164
|
"`mode='gaussian'` is incompatible with `aggregate='ema'`: EMA updates blend by `ema_smoothing`, not "
|
|
155
|
-
"by per-point distance weights. Use `aggregate='
|
|
165
|
+
"by per-point distance weights. Use `aggregate='mean'` or `mode='constant'`."
|
|
156
166
|
)
|
|
157
167
|
if aggregate == "ema" and sw_batch_size > 1:
|
|
158
168
|
warnings.warn(
|
|
@@ -170,9 +180,7 @@ def knn_window_inference(
|
|
|
170
180
|
n_total = pos.size(0)
|
|
171
181
|
output: Optional[Tensor] = None
|
|
172
182
|
|
|
173
|
-
rng = torch.Generator(device=device)
|
|
174
|
-
if seed is not None:
|
|
175
|
-
rng.manual_seed(int(seed))
|
|
183
|
+
rng = None if seed is None else torch.Generator(device=device).manual_seed(int(seed))
|
|
176
184
|
|
|
177
185
|
for b in torch.unique(batch).tolist():
|
|
178
186
|
idx_b = torch.where(batch == b)[0]
|
|
@@ -201,37 +209,39 @@ def knn_window_inference(
|
|
|
201
209
|
local_idxs = _knn_centers(pos_b, center_pos, k)
|
|
202
210
|
flat_idxs = local_idxs.reshape(-1)
|
|
203
211
|
per_window_dicts: List[Dict[str, Any]] = []
|
|
212
|
+
inverse_maps: List[Optional[Tensor]] = []
|
|
204
213
|
for w_i in range(sw):
|
|
205
214
|
w_idx = local_idxs[w_i]
|
|
206
215
|
wd = index_select_dict(data_b, w_idx, n_b)
|
|
207
216
|
wd[batch_key] = torch.full((k,), w_i, device=device, dtype=torch.long)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if int(wd[pos_key].size(0)) != k:
|
|
211
|
-
raise ValueError(
|
|
212
|
-
f"`transform` must preserve each window's row count; got {k} -> "
|
|
213
|
-
f"{int(wd[pos_key].size(0))} rows."
|
|
214
|
-
)
|
|
215
|
-
|
|
217
|
+
wd, inverse_map = apply_transform(wd, transform, pos_key=pos_key, inverse_key=inverse_key)
|
|
218
|
+
wd[batch_key] = torch.full((int(wd[pos_key].size(0)),), w_i, device=device, dtype=torch.long)
|
|
216
219
|
per_window_dicts.append(wd)
|
|
220
|
+
inverse_maps.append(inverse_map)
|
|
217
221
|
|
|
222
|
+
sizes = [int(wd[pos_key].size(0)) for wd in per_window_dicts]
|
|
218
223
|
window_data: Dict[str, Any] = {}
|
|
219
224
|
for key in per_window_dicts[0]:
|
|
220
225
|
values = [wd[key] for wd in per_window_dicts]
|
|
221
|
-
if torch.is_tensor(values[0]) and values[0].dim() > 0 and values[0].size(0) ==
|
|
226
|
+
if torch.is_tensor(values[0]) and values[0].dim() > 0 and values[0].size(0) == sizes[0]:
|
|
222
227
|
window_data[key] = torch.cat(values, dim=0)
|
|
223
228
|
else:
|
|
224
229
|
window_data[key] = values[0]
|
|
225
230
|
|
|
226
231
|
window_logits = predictor(window_data).to(device)
|
|
227
232
|
num_classes = int(window_logits.size(-1))
|
|
228
|
-
window_logits =
|
|
233
|
+
window_logits = torch.stack(
|
|
234
|
+
[
|
|
235
|
+
part if inverse_map is None else part[inverse_map]
|
|
236
|
+
for part, inverse_map in zip(window_logits.split(sizes), inverse_maps)
|
|
237
|
+
]
|
|
238
|
+
) # (sw, k, C)
|
|
229
239
|
|
|
230
240
|
if output is None:
|
|
231
241
|
output = torch.zeros(n_total, num_classes, device=device, dtype=torch.float32)
|
|
232
242
|
if scores_b is None:
|
|
233
243
|
scores_b = torch.zeros(n_b, num_classes, device=device, dtype=torch.float32)
|
|
234
|
-
if aggregate == "
|
|
244
|
+
if aggregate == "mean":
|
|
235
245
|
weights_b = torch.zeros(n_b, device=device, dtype=torch.float32)
|
|
236
246
|
|
|
237
247
|
distances = torch.linalg.norm(pos_b[local_idxs] - center_pos.unsqueeze(1), dim=-1)
|
|
@@ -289,7 +299,9 @@ class KNNWindowInferer(Inferer):
|
|
|
289
299
|
the least-covered points until all points are covered. Reuse the same instance
|
|
290
300
|
across scenes; compose with `TTAInferer` for multi-augmentation averaging.
|
|
291
301
|
|
|
292
|
-
All parameters are forwarded verbatim to `knn_window_inference
|
|
302
|
+
All parameters are forwarded verbatim to `knn_window_inference`, except `seed`, which is offset by
|
|
303
|
+
the number of calls the instance has made: repeated calls (e.g. `TTAInferer` votes) draw different random
|
|
304
|
+
numbers, and a fresh instance replays the same sequence.
|
|
293
305
|
|
|
294
306
|
Example:
|
|
295
307
|
```python
|
|
@@ -308,12 +320,13 @@ class KNNWindowInferer(Inferer):
|
|
|
308
320
|
overlap: float = 0.5,
|
|
309
321
|
mode: WindowMode = "constant",
|
|
310
322
|
sigma_scale: float = 0.125,
|
|
311
|
-
aggregate: AggregateMode = "
|
|
323
|
+
aggregate: AggregateMode = "mean",
|
|
312
324
|
ema_smoothing: float = 0.95,
|
|
313
325
|
softmax: bool = False,
|
|
314
326
|
transform: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None,
|
|
315
327
|
pos_key: str = DataKeys.POS,
|
|
316
328
|
batch_key: str = DataKeys.BATCH,
|
|
329
|
+
inverse_key: Optional[str] = None,
|
|
317
330
|
progress: bool = False,
|
|
318
331
|
seed: Optional[int] = None,
|
|
319
332
|
) -> None:
|
|
@@ -328,14 +341,18 @@ class KNNWindowInferer(Inferer):
|
|
|
328
341
|
self.transform = transform
|
|
329
342
|
self.pos_key = pos_key
|
|
330
343
|
self.batch_key = batch_key
|
|
344
|
+
self.inverse_key = inverse_key
|
|
331
345
|
self.progress = progress
|
|
332
346
|
self.seed = seed
|
|
347
|
+
self._num_calls = 0
|
|
333
348
|
|
|
334
349
|
def forward(
|
|
335
350
|
self,
|
|
336
351
|
data: Dict[str, Any],
|
|
337
352
|
predictor: Callable[[Dict[str, Any]], Tensor],
|
|
338
353
|
) -> Tensor:
|
|
354
|
+
seed = None if self.seed is None else self.seed + self._num_calls
|
|
355
|
+
self._num_calls += 1
|
|
339
356
|
return knn_window_inference(
|
|
340
357
|
data,
|
|
341
358
|
predictor=predictor,
|
|
@@ -350,6 +367,7 @@ class KNNWindowInferer(Inferer):
|
|
|
350
367
|
transform=self.transform,
|
|
351
368
|
pos_key=self.pos_key,
|
|
352
369
|
batch_key=self.batch_key,
|
|
370
|
+
inverse_key=self.inverse_key,
|
|
353
371
|
progress=self.progress,
|
|
354
|
-
seed=
|
|
372
|
+
seed=seed,
|
|
355
373
|
)
|
{torch_pointcloud-0.0.2 → torch_pointcloud-0.0.4}/src/torch_pointcloud/inferers/potential_sphere.py
RENAMED
|
@@ -15,7 +15,7 @@ from tqdm import tqdm
|
|
|
15
15
|
from torch_pointcloud.utils.data import DataKeys, collate
|
|
16
16
|
from torch_pointcloud.utils.ops import voxel_grid_fnv
|
|
17
17
|
|
|
18
|
-
from ._utils import check_batch_alignment, index_select_dict
|
|
18
|
+
from ._utils import apply_transform, check_batch_alignment, index_select_dict
|
|
19
19
|
from .inferer import Inferer
|
|
20
20
|
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ def _next_sphere(
|
|
|
23
23
|
pos_b: Tensor,
|
|
24
24
|
coarse_pos: Tensor,
|
|
25
25
|
potentials: Tensor,
|
|
26
|
-
rng: torch.Generator,
|
|
26
|
+
rng: Optional[torch.Generator],
|
|
27
27
|
radius: float,
|
|
28
28
|
jitter: float,
|
|
29
29
|
) -> Tuple[Tensor, Tensor]:
|
|
@@ -55,6 +55,7 @@ def potential_sphere_inference(
|
|
|
55
55
|
transform: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None,
|
|
56
56
|
pos_key: str = DataKeys.POS,
|
|
57
57
|
batch_key: str = DataKeys.BATCH,
|
|
58
|
+
inverse_key: Optional[str] = None,
|
|
58
59
|
progress: bool = False,
|
|
59
60
|
seed: Optional[int] = None,
|
|
60
61
|
) -> Tensor:
|
|
@@ -91,12 +92,19 @@ def potential_sphere_inference(
|
|
|
91
92
|
sw_batch_size: Number of spheres packed into one predictor call. Centers are still drawn one at a
|
|
92
93
|
time with the potentials updated in between, as the reference sampler does.
|
|
93
94
|
transform: Optional per-sphere callable applied to the centered sphere dict before the predictor.
|
|
94
|
-
The transform must
|
|
95
|
-
|
|
95
|
+
The transform must keep positions centered on the sphere (the `inner_ratio` mask is evaluated on
|
|
96
|
+
the transformed positions, as the reference does). If it changes the row count (pad, voxelize, ...)
|
|
97
|
+
it must record a source-to-predictor index map under `inverse_key` so the inferer can gather
|
|
98
|
+
predictions and the mask back to the sphere's points.
|
|
96
99
|
pos_key: Dict key for the position tensor.
|
|
97
100
|
batch_key: Dict key for the per-point batch index.
|
|
101
|
+
inverse_key: Dict key under which a row-altering `transform` records a source-to-predictor long index
|
|
102
|
+
map of shape $(N_\text{sphere},)$ with values in $[0, N_\text{predictor})$. Any scene-level value at
|
|
103
|
+
this key is dropped before `transform` runs, and the map is popped before the predictor is called.
|
|
104
|
+
Leave `None` when the transform preserves row count.
|
|
98
105
|
progress: If `True`, show a `tqdm` progress bar per batch element.
|
|
99
|
-
seed:
|
|
106
|
+
seed: RNG seed for the initial potentials and the center jitter. `None` draws from the global generator, so
|
|
107
|
+
`torch.manual_seed` seeds the inferer together with the transforms.
|
|
100
108
|
|
|
101
109
|
Returns:
|
|
102
110
|
Per-point score tensor of shape $(N, C)$: the EMA of softmax probabilities over the spheres covering
|
|
@@ -132,9 +140,7 @@ def potential_sphere_inference(
|
|
|
132
140
|
n = pos.size(0)
|
|
133
141
|
inner_sq = (inner_ratio * radius) ** 2
|
|
134
142
|
|
|
135
|
-
rng = torch.Generator(device=device)
|
|
136
|
-
if seed is not None:
|
|
137
|
-
rng.manual_seed(int(seed))
|
|
143
|
+
rng = None if seed is None else torch.Generator(device=device).manual_seed(int(seed))
|
|
138
144
|
|
|
139
145
|
output: Optional[Tensor] = None
|
|
140
146
|
for b in torch.unique(batch).tolist():
|
|
@@ -152,6 +158,7 @@ def potential_sphere_inference(
|
|
|
152
158
|
while float(potentials.min().item()) < num_votes:
|
|
153
159
|
spheres: List[Dict[str, Any]] = []
|
|
154
160
|
sphere_idx: List[Tensor] = []
|
|
161
|
+
inverse_maps: List[Optional[Tensor]] = []
|
|
155
162
|
for _ in range(sw_batch_size):
|
|
156
163
|
if float(potentials.min().item()) >= num_votes:
|
|
157
164
|
break
|
|
@@ -162,16 +169,11 @@ def potential_sphere_inference(
|
|
|
162
169
|
|
|
163
170
|
sphere = index_select_dict(data_b, idx, n_b)
|
|
164
171
|
sphere[pos_key] = sphere[pos_key] - center
|
|
165
|
-
|
|
166
|
-
sphere = transform(sphere)
|
|
167
|
-
if int(sphere[pos_key].size(0)) != int(idx.numel()):
|
|
168
|
-
raise ValueError(
|
|
169
|
-
f"`transform` must preserve each sphere's row count; got {int(idx.numel())} -> "
|
|
170
|
-
f"{int(sphere[pos_key].size(0))} rows."
|
|
171
|
-
)
|
|
172
|
+
sphere, inverse_map = apply_transform(sphere, transform, pos_key=pos_key, inverse_key=inverse_key)
|
|
172
173
|
|
|
173
174
|
spheres.append(sphere)
|
|
174
175
|
sphere_idx.append(idx)
|
|
176
|
+
inverse_maps.append(inverse_map)
|
|
175
177
|
|
|
176
178
|
if not spheres:
|
|
177
179
|
continue
|
|
@@ -182,11 +184,14 @@ def potential_sphere_inference(
|
|
|
182
184
|
scores_b = torch.zeros(n_b, int(probs.size(-1)), device=device, dtype=probs.dtype)
|
|
183
185
|
|
|
184
186
|
offset = 0
|
|
185
|
-
for sphere, idx in zip(spheres, sphere_idx):
|
|
186
|
-
count = int(
|
|
187
|
+
for sphere, idx, inverse_map in zip(spheres, sphere_idx, inverse_maps):
|
|
188
|
+
count = int(sphere[pos_key].size(0))
|
|
187
189
|
sphere_probs = probs[offset : offset + count]
|
|
188
190
|
offset += count
|
|
189
191
|
inner = sphere[pos_key].square().sum(dim=-1) < inner_sq
|
|
192
|
+
if inverse_map is not None:
|
|
193
|
+
sphere_probs = sphere_probs[inverse_map]
|
|
194
|
+
inner = inner[inverse_map]
|
|
190
195
|
rows = idx[inner]
|
|
191
196
|
scores_b[rows] = ema_smoothing * scores_b[rows] + (1.0 - ema_smoothing) * (
|
|
192
197
|
sphere_probs[inner].to(scores_b.dtype)
|
|
@@ -225,7 +230,9 @@ class PotentialSphereInferer(Inferer):
|
|
|
225
230
|
each sphere's softmax predictions into the running per-point scores by an exponential moving average,
|
|
226
231
|
until every region has been covered about `num_votes` times.
|
|
227
232
|
|
|
228
|
-
All parameters are forwarded verbatim to `potential_sphere_inference
|
|
233
|
+
All parameters are forwarded verbatim to `potential_sphere_inference`, except `seed`, which is offset by
|
|
234
|
+
the number of calls the instance has made: repeated calls (e.g. `TTAInferer` votes) draw different random
|
|
235
|
+
numbers, and a fresh instance replays the same sequence.
|
|
229
236
|
|
|
230
237
|
Example:
|
|
231
238
|
```{.python notest}
|
|
@@ -248,6 +255,7 @@ class PotentialSphereInferer(Inferer):
|
|
|
248
255
|
transform: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None,
|
|
249
256
|
pos_key: str = DataKeys.POS,
|
|
250
257
|
batch_key: str = DataKeys.BATCH,
|
|
258
|
+
inverse_key: Optional[str] = None,
|
|
251
259
|
progress: bool = False,
|
|
252
260
|
seed: Optional[int] = None,
|
|
253
261
|
) -> None:
|
|
@@ -261,14 +269,18 @@ class PotentialSphereInferer(Inferer):
|
|
|
261
269
|
self.transform = transform
|
|
262
270
|
self.pos_key = pos_key
|
|
263
271
|
self.batch_key = batch_key
|
|
272
|
+
self.inverse_key = inverse_key
|
|
264
273
|
self.progress = progress
|
|
265
274
|
self.seed = seed
|
|
275
|
+
self._num_calls = 0
|
|
266
276
|
|
|
267
277
|
def forward(
|
|
268
278
|
self,
|
|
269
279
|
data: Dict[str, Any],
|
|
270
280
|
predictor: Callable[[Dict[str, Any]], Tensor],
|
|
271
281
|
) -> Tensor:
|
|
282
|
+
seed = None if self.seed is None else self.seed + self._num_calls
|
|
283
|
+
self._num_calls += 1
|
|
272
284
|
return potential_sphere_inference(
|
|
273
285
|
data,
|
|
274
286
|
predictor=predictor,
|
|
@@ -282,6 +294,7 @@ class PotentialSphereInferer(Inferer):
|
|
|
282
294
|
transform=self.transform,
|
|
283
295
|
pos_key=self.pos_key,
|
|
284
296
|
batch_key=self.batch_key,
|
|
297
|
+
inverse_key=self.inverse_key,
|
|
285
298
|
progress=self.progress,
|
|
286
|
-
seed=
|
|
299
|
+
seed=seed,
|
|
287
300
|
)
|