openpoints 0.1.0__py3-none-any.whl

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.
Files changed (184) hide show
  1. openpoints/__init__.py +3 -0
  2. openpoints/cpp/__init__.py +6 -0
  3. openpoints/cpp/chamfer_dist/__init__.py +110 -0
  4. openpoints/cpp/chamfer_dist/setup.py +19 -0
  5. openpoints/cpp/chamfer_dist/test.py +34 -0
  6. openpoints/cpp/emd/__init__.py +3 -0
  7. openpoints/cpp/emd/emd.py +88 -0
  8. openpoints/cpp/emd/setup.py +27 -0
  9. openpoints/cpp/emd/test_emd_loss.py +45 -0
  10. openpoints/cpp/pointnet2_batch/__init__.py +24 -0
  11. openpoints/cpp/pointnet2_batch/setup.py +23 -0
  12. openpoints/cpp/pointops/__init__.py +0 -0
  13. openpoints/cpp/pointops/functions/__init__.py +0 -0
  14. openpoints/cpp/pointops/functions/pointops.py +314 -0
  15. openpoints/cpp/pointops/setup.py +37 -0
  16. openpoints/cpp/pointops/src/__init__.py +0 -0
  17. openpoints/dataset/__init__.py +10 -0
  18. openpoints/dataset/atom3d/__init__.py +1 -0
  19. openpoints/dataset/atom3d/psr.py +38 -0
  20. openpoints/dataset/build.py +98 -0
  21. openpoints/dataset/data_util.py +192 -0
  22. openpoints/dataset/datalist.py +67 -0
  23. openpoints/dataset/dataset_base.py +96 -0
  24. openpoints/dataset/graph_dataset/__init__.py +3 -0
  25. openpoints/dataset/graph_dataset/graph_dataset.py +93 -0
  26. openpoints/dataset/graph_dataset/stack_with_pad.py +91 -0
  27. openpoints/dataset/graph_dataset/structural_dataset.py +73 -0
  28. openpoints/dataset/graph_dataset/svd_encodings_dataset.py +110 -0
  29. openpoints/dataset/grid_sample.py +21 -0
  30. openpoints/dataset/matterport3d/__init__.py +1 -0
  31. openpoints/dataset/matterport3d/category_mapping.tsv +1660 -0
  32. openpoints/dataset/matterport3d/matterport3d.py +210 -0
  33. openpoints/dataset/matterport3d/matterport3d_dataprocessing.py +105 -0
  34. openpoints/dataset/modelnet/__init__.py +3 -0
  35. openpoints/dataset/modelnet/modelnet40_normal_resampled_loader.py +124 -0
  36. openpoints/dataset/modelnet/modelnet40_ply_2048_loader.py +160 -0
  37. openpoints/dataset/molhiv/__init__.py +1 -0
  38. openpoints/dataset/molhiv/data.py +59 -0
  39. openpoints/dataset/molpcba/__init__.py +1 -0
  40. openpoints/dataset/molpcba/data.py +59 -0
  41. openpoints/dataset/parsers/__init__.py +1 -0
  42. openpoints/dataset/parsers/class_map.py +19 -0
  43. openpoints/dataset/parsers/constants.py +1 -0
  44. openpoints/dataset/parsers/parser.py +17 -0
  45. openpoints/dataset/parsers/parser_factory.py +29 -0
  46. openpoints/dataset/parsers/parser_image_folder.py +69 -0
  47. openpoints/dataset/parsers/parser_image_in_tar.py +222 -0
  48. openpoints/dataset/parsers/parser_image_tar.py +72 -0
  49. openpoints/dataset/parsers/parser_tfds.py +297 -0
  50. openpoints/dataset/pcqm4m/__init__.py +1 -0
  51. openpoints/dataset/pcqm4m/data.py +62 -0
  52. openpoints/dataset/pcqm4mv2/__init__.py +1 -0
  53. openpoints/dataset/pcqm4mv2/data.py +87 -0
  54. openpoints/dataset/s3dis/__init__.py +2 -0
  55. openpoints/dataset/s3dis/s3dis.py +156 -0
  56. openpoints/dataset/s3dis/s3dis_block.py +96 -0
  57. openpoints/dataset/s3dis/s3dis_sphere.py +349 -0
  58. openpoints/dataset/scannetv2/__init__.py +1 -0
  59. openpoints/dataset/scannetv2/scannet.py +176 -0
  60. openpoints/dataset/scanobjectnn/__init__.py +3 -0
  61. openpoints/dataset/scanobjectnn/scanobjectnn.py +110 -0
  62. openpoints/dataset/semantic_kitti/__init__.py +1 -0
  63. openpoints/dataset/semantic_kitti/helper_tool.py +286 -0
  64. openpoints/dataset/semantic_kitti/label_mapping.yaml +211 -0
  65. openpoints/dataset/semantic_kitti/semantickitti.py +229 -0
  66. openpoints/dataset/semantic_kitti/utils/meta/anno_paths.txt +272 -0
  67. openpoints/dataset/semantic_kitti/utils/meta/class_names.txt +13 -0
  68. openpoints/dataset/semantic_kitti/utils/semantic-kitti.yaml +211 -0
  69. openpoints/dataset/shapenet/__init__.py +1 -0
  70. openpoints/dataset/shapenet/shapenet55.py +76 -0
  71. openpoints/dataset/shapenet/shapenetpart.py +121 -0
  72. openpoints/dataset/shapenetpart/__init__.py +1 -0
  73. openpoints/dataset/shapenetpart/shapenet55.py +75 -0
  74. openpoints/dataset/shapenetpart/shapenetpart.py +388 -0
  75. openpoints/dataset/vis2d.py +17 -0
  76. openpoints/dataset/vis3d.py +153 -0
  77. openpoints/loss/__init__.py +3 -0
  78. openpoints/loss/build.py +281 -0
  79. openpoints/loss/cross_entropy.py +38 -0
  80. openpoints/loss/distill_loss.py +76 -0
  81. openpoints/models/__init__.py +10 -0
  82. openpoints/models/backbone/Stratified_transformer.py +558 -0
  83. openpoints/models/backbone/__init__.py +11 -0
  84. openpoints/models/backbone/baafnet.py +527 -0
  85. openpoints/models/backbone/ball_dgcnn.py +123 -0
  86. openpoints/models/backbone/curvenet.py +793 -0
  87. openpoints/models/backbone/debug_invvit.py +114 -0
  88. openpoints/models/backbone/deepgcn.py +143 -0
  89. openpoints/models/backbone/dgcnn.py +119 -0
  90. openpoints/models/backbone/graphvit3d.py +134 -0
  91. openpoints/models/backbone/grouppointnet.py +100 -0
  92. openpoints/models/backbone/pct.py +163 -0
  93. openpoints/models/backbone/pointmlp.py +417 -0
  94. openpoints/models/backbone/pointnet.py +199 -0
  95. openpoints/models/backbone/pointnetv2.py +511 -0
  96. openpoints/models/backbone/pointnext.py +663 -0
  97. openpoints/models/backbone/pointnextPyG.py +555 -0
  98. openpoints/models/backbone/pointtransformer.py +293 -0
  99. openpoints/models/backbone/pointvector.py +853 -0
  100. openpoints/models/backbone/pointvit.py +392 -0
  101. openpoints/models/backbone/pointvit_inv.py +942 -0
  102. openpoints/models/backbone/pointvit_inv_old.py +784 -0
  103. openpoints/models/backbone/randlenet.py +318 -0
  104. openpoints/models/backbone/resnet.py +342 -0
  105. openpoints/models/backbone/simpleview.py +153 -0
  106. openpoints/models/backbone/simpleview_util.py +292 -0
  107. openpoints/models/build.py +13 -0
  108. openpoints/models/classification/__init__.py +5 -0
  109. openpoints/models/classification/cls_base.py +136 -0
  110. openpoints/models/classification/point_bert.py +154 -0
  111. openpoints/models/layers/__init__.py +14 -0
  112. openpoints/models/layers/activation.py +57 -0
  113. openpoints/models/layers/attention.py +103 -0
  114. openpoints/models/layers/conv.py +167 -0
  115. openpoints/models/layers/drop.py +164 -0
  116. openpoints/models/layers/graph_conv.py +122 -0
  117. openpoints/models/layers/group.py +415 -0
  118. openpoints/models/layers/group_embed.py +286 -0
  119. openpoints/models/layers/helpers.py +43 -0
  120. openpoints/models/layers/kmeans.py +119 -0
  121. openpoints/models/layers/knn.py +110 -0
  122. openpoints/models/layers/local_aggregation.py +286 -0
  123. openpoints/models/layers/mlp.py +129 -0
  124. openpoints/models/layers/norm.py +106 -0
  125. openpoints/models/layers/padding.py +56 -0
  126. openpoints/models/layers/patch_embed.py +37 -0
  127. openpoints/models/layers/registry.py +168 -0
  128. openpoints/models/layers/subsample.py +185 -0
  129. openpoints/models/layers/upsampling.py +106 -0
  130. openpoints/models/layers/weight_init.py +89 -0
  131. openpoints/models/reconstruction/__init__.py +8 -0
  132. openpoints/models/reconstruction/base_recontruct.py +216 -0
  133. openpoints/models/reconstruction/maskedpoint.py +116 -0
  134. openpoints/models/reconstruction/maskedpointgroup.py +168 -0
  135. openpoints/models/reconstruction/maskedpointvit.py +253 -0
  136. openpoints/models/reconstruction/nodeshuffle.py +11 -0
  137. openpoints/models/registry.py +149 -0
  138. openpoints/models/segmentation/__init__.py +6 -0
  139. openpoints/models/segmentation/base_seg.py +278 -0
  140. openpoints/models/segmentation/vit_seg.py +126 -0
  141. openpoints/optim/__init__.py +15 -0
  142. openpoints/optim/adabelief.py +201 -0
  143. openpoints/optim/adafactor.py +167 -0
  144. openpoints/optim/adahessian.py +156 -0
  145. openpoints/optim/adamp.py +105 -0
  146. openpoints/optim/adamw.py +122 -0
  147. openpoints/optim/lamb.py +192 -0
  148. openpoints/optim/lars.py +135 -0
  149. openpoints/optim/lookahead.py +61 -0
  150. openpoints/optim/madgrad.py +184 -0
  151. openpoints/optim/nadam.py +92 -0
  152. openpoints/optim/nvnovograd.py +120 -0
  153. openpoints/optim/optim_factory.py +306 -0
  154. openpoints/optim/radam.py +89 -0
  155. openpoints/optim/rmsprop_tf.py +139 -0
  156. openpoints/optim/sgdp.py +70 -0
  157. openpoints/scheduler/__init__.py +8 -0
  158. openpoints/scheduler/cosine_lr.py +124 -0
  159. openpoints/scheduler/multistep_lr.py +65 -0
  160. openpoints/scheduler/plateau_lr.py +113 -0
  161. openpoints/scheduler/poly_lr.py +116 -0
  162. openpoints/scheduler/scheduler.py +110 -0
  163. openpoints/scheduler/scheduler_factory.py +118 -0
  164. openpoints/scheduler/step_lr.py +65 -0
  165. openpoints/scheduler/tanh_lr.py +117 -0
  166. openpoints/transforms/__init__.py +7 -0
  167. openpoints/transforms/point_transform_cpu.py +332 -0
  168. openpoints/transforms/point_transformer_gpu.py +764 -0
  169. openpoints/transforms/transforms_factory.py +60 -0
  170. openpoints/utils/__init__.py +8 -0
  171. openpoints/utils/ckpt_util.py +438 -0
  172. openpoints/utils/config.py +113 -0
  173. openpoints/utils/dist_utils.py +54 -0
  174. openpoints/utils/logger.py +170 -0
  175. openpoints/utils/metrics.py +311 -0
  176. openpoints/utils/random.py +16 -0
  177. openpoints/utils/registry.py +294 -0
  178. openpoints/utils/str2bool.py +11 -0
  179. openpoints/utils/wandb.py +88 -0
  180. openpoints-0.1.0.dist-info/METADATA +150 -0
  181. openpoints-0.1.0.dist-info/RECORD +184 -0
  182. openpoints-0.1.0.dist-info/WHEEL +5 -0
  183. openpoints-0.1.0.dist-info/licenses/LICENSE +21 -0
  184. openpoints-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,122 @@
1
+ import torch
2
+ from torch import nn
3
+ from .group import grouping_operation
4
+ from .knn import DilatedKNN
5
+ from openpoints.models.layers.conv import create_convblock2d
6
+
7
+
8
+ def gather_features(features, indices, sparse_grad=True):
9
+ """Gather the features specified by indices
10
+
11
+ Args:
12
+ features: tensor of shape [B, C, N, 1]
13
+ indices: long tensor of shape [B, N, K]
14
+ sparse_grad: whether to use a sparse tensor for the gradient
15
+
16
+ Returns:
17
+ gathered_features [B, C, N, K]
18
+ """
19
+ indices = indices.unsqueeze(1).long()
20
+ features, indices = torch.broadcast_tensors(features, indices)
21
+ return features.gather(dim=-2, index=indices, sparse_grad=sparse_grad)
22
+
23
+
24
+ class MRConv(nn.Module):
25
+ """
26
+ Max-Relative Graph Convolution (Paper: https://arxiv.org/abs/1904.03751) for dense data type
27
+ """
28
+ def __init__(self, in_channels, out_channels, **kwargs):
29
+ super(MRConv, self).__init__()
30
+ self.nn = create_convblock2d(in_channels*2, out_channels, **kwargs)
31
+
32
+ def forward(self, x, edge_index):
33
+ x_j = grouping_operation(x.squeeze(-1), edge_index)
34
+ x_j, _ = torch.max(x_j - x.unsequence(-1), -1, keepdim=True)
35
+ return self.nn(torch.cat([x, x_j], dim=1))
36
+
37
+
38
+ class EdgeConv(nn.Module):
39
+ """
40
+ Edge convolution layer (with activation, batch normalization) for dense data type
41
+ """
42
+ def __init__(self, in_channels, out_channels, **kwargs):
43
+ super(EdgeConv, self).__init__()
44
+ self.nn = create_convblock2d(in_channels * 2, out_channels, **kwargs)
45
+
46
+ def forward(self, x, edge_index):
47
+ # x_j = gather_features(x,edge_index)
48
+ x_j = grouping_operation(x.squeeze(-1), edge_index.int())
49
+ max_value, _ = torch.max(self.nn(torch.cat([x.expand(-1, -1, -1, edge_index.shape[-1]), x_j - x],
50
+ dim=1)), -1, keepdim=True)
51
+ return max_value
52
+
53
+
54
+ _GCN_LAYER_DEFAULT = dict(
55
+ mrconv=MRConv,
56
+ edgeconv=EdgeConv,
57
+ edge=EdgeConv
58
+ )
59
+
60
+
61
+ class GraphConv(nn.Module):
62
+ """
63
+ Static graph convolution layer
64
+ """
65
+ def __init__(self, in_channels, out_channels, conv=EdgeConv, **kwargs):
66
+ super(GraphConv, self).__init__()
67
+ if isinstance(conv, str):
68
+ conv = _GCN_LAYER_DEFAULT[conv]
69
+ self.gconv = conv(in_channels, out_channels, **kwargs)
70
+
71
+ def forward(self, x, edge_index):
72
+ return self.gconv(x, edge_index)
73
+
74
+
75
+ class DynConv(GraphConv):
76
+ """
77
+ Dynamic graph convolution layer
78
+ """
79
+ def __init__(self, in_channels, out_channels, conv=EdgeConv,
80
+ k=9, dilation=1, stochastic=False, epsilon=0.0,
81
+ **kwargs):
82
+ super(DynConv, self).__init__(in_channels, out_channels, conv, **kwargs)
83
+ self.k = k
84
+ self.d = dilation
85
+ self.dilated_knn_graph = DilatedKNN(k, dilation, stochastic, epsilon)
86
+
87
+ def forward(self, x):
88
+ edge_index = self.dilated_knn_graph(x.squeeze(-1).transpose(1, 2))
89
+ return super(DynConv, self).forward(x, edge_index)
90
+
91
+
92
+ class ResDynBlock(nn.Module):
93
+ """
94
+ Residual Dynamic graph convolution block
95
+ """
96
+ def __init__(self, in_channels, conv=EdgeConv,
97
+ k=9, dilation=1, stochastic=False, epsilon=0.0,
98
+ **kwargs):
99
+ super(ResDynBlock, self).__init__()
100
+ self.body = DynConv(in_channels, in_channels, conv,
101
+ k, dilation, stochastic, epsilon, **kwargs)
102
+
103
+ def forward(self, x):
104
+ return self.body(x) + x
105
+
106
+
107
+ class DenseDynBlock(nn.Module):
108
+ """
109
+ Dense Dynamic graph convolution block
110
+ """
111
+ def __init__(self, in_channels, out_channels,conv=EdgeConv,
112
+ k=9, dilation=1, stochastic=False, epsilon=0.0,
113
+ **kwargs):
114
+ super(DenseDynBlock, self).__init__()
115
+ assert out_channels > in_channels, "#out channels should be larger than #in channels"
116
+ self.body = DynConv(in_channels, out_channels-in_channels, conv,
117
+ k, dilation, stochastic, epsilon, **kwargs)
118
+
119
+ def forward(self, x):
120
+ dense = self.body(x).squeeze(-1)
121
+ return torch.cat((x, dense), 1)
122
+
@@ -0,0 +1,415 @@
1
+ # group layer: find neighbors for each point
2
+ # knn, knn_sparse, ball query
3
+
4
+ # gather layer, gather features by index
5
+ from typing import Tuple
6
+ import copy, logging
7
+ import torch
8
+ import torch.nn as nn
9
+ from torch.autograd import Function
10
+ from openpoints.cpp import pointnet2_cuda
11
+
12
+ class KNN(nn.Module):
13
+ def __init__(self, neighbors, transpose_mode=True):
14
+ super(KNN, self).__init__()
15
+ self.neighbors = neighbors
16
+
17
+ @torch.no_grad()
18
+ def forward(self, support, query):
19
+ """
20
+ Args:
21
+ support ([tensor]): [B, N, C]
22
+ query ([tensor]): [B, M, C]
23
+ Returns:
24
+ [int]: neighbor idx. [B, M, K]
25
+ """
26
+ dist = torch.cdist(support, query)
27
+ k_dist = dist.topk(k=self.neighbors, dim=1, largest=False)
28
+ return k_dist.values, k_dist.indices.transpose(1, 2).contiguous().int()
29
+
30
+ # dilated knn
31
+ class DenseDilated(nn.Module):
32
+ """
33
+ Find dilated neighbor from neighbor list
34
+ index: (B, npoint, nsample)
35
+ """
36
+
37
+ def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0):
38
+ super(DenseDilated, self).__init__()
39
+ self.dilation = dilation
40
+ self.stochastic = stochastic
41
+ self.epsilon = epsilon
42
+ self.k = k
43
+
44
+ def forward(self, edge_index):
45
+ if self.stochastic:
46
+ if torch.rand(1) < self.epsilon and self.training:
47
+ num = self.k * self.dilation
48
+ randnum = torch.randperm(num)[:self.k]
49
+ edge_index = edge_index[:, :, randnum]
50
+ else:
51
+ edge_index = edge_index[:, :, ::self.dilation]
52
+ else:
53
+ edge_index = edge_index[:, :, ::self.dilation]
54
+ return edge_index.contiguous()
55
+
56
+
57
+ class DilatedKNN(nn.Module):
58
+ """
59
+ Find the neighbors' indices based on dilated knn
60
+ """
61
+
62
+ def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0):
63
+ super(DilatedKNN, self).__init__()
64
+ self.dilation = dilation
65
+ self.stochastic = stochastic
66
+ self.epsilon = epsilon
67
+ self.k = k
68
+ self._dilated = DenseDilated(k, dilation, stochastic, epsilon)
69
+ self.knn = KNN(k * self.dilation, transpose_mode=True)
70
+
71
+ def forward(self, query):
72
+ _, idx = self.knn(query, query)
73
+ return self._dilated(idx)
74
+
75
+
76
+ class GroupingOperation(Function):
77
+
78
+ @staticmethod
79
+ @torch.cuda.amp.custom_fwd(cast_inputs=torch.float32)
80
+ def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
81
+ """
82
+ :param ctx:
83
+ :param features: (B, C, N) tensor of features to group
84
+ :param idx: (B, npoint, nsample) tensor containing the indicies of features to group with
85
+ :return:
86
+ output: (B, C, npoint, nsample) tensor
87
+ """
88
+ assert features.is_contiguous()
89
+ assert idx.is_contiguous()
90
+
91
+ B, nfeatures, nsample = idx.size()
92
+ _, C, N = features.size()
93
+ output = torch.cuda.FloatTensor(B, C, nfeatures, nsample, device=features.device)
94
+
95
+ pointnet2_cuda.group_points_wrapper(B, C, N, nfeatures, nsample, features, idx, output)
96
+
97
+ ctx.for_backwards = (idx, N)
98
+ return output
99
+
100
+ @staticmethod
101
+ def backward(ctx, grad_out: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
102
+ """
103
+ :param ctx:
104
+ :param grad_out: (B, C, npoint, nsample) tensor of the gradients of the output from forward
105
+ :return:
106
+ grad_features: (B, C, N) gradient of the features
107
+ """
108
+ idx, N = ctx.for_backwards
109
+
110
+ B, C, npoint, nsample = grad_out.size()
111
+ grad_features = torch.zeros([B, C, N], dtype=torch.float, device=grad_out.device, requires_grad=True)
112
+ grad_out_data = grad_out.data.contiguous()
113
+ pointnet2_cuda.group_points_grad_wrapper(B, C, N, npoint, nsample, grad_out_data, idx, grad_features.data)
114
+ return grad_features, None
115
+
116
+
117
+ grouping_operation = GroupingOperation.apply
118
+
119
+
120
+ def torch_grouping_operation(features, idx):
121
+ r"""from torch points kernels
122
+ Parameters
123
+ ----------
124
+ features : torch.Tensor
125
+ (B, C, N) tensor of features to group
126
+ idx : torch.Tensor
127
+ (B, npoint, nsample) tensor containing the indicies of features to group with
128
+
129
+ Returns
130
+ -------
131
+ torch.Tensor
132
+ (B, C, npoint, nsample) tensor
133
+ """
134
+ all_idx = idx.reshape(idx.shape[0], -1)
135
+ all_idx = all_idx.unsqueeze(1).repeat(1, features.shape[1], 1)
136
+ grouped_features = features.gather(2, all_idx)
137
+ return grouped_features.reshape(idx.shape[0], features.shape[1], idx.shape[1], idx.shape[2])
138
+
139
+
140
+ class GatherOperation(Function):
141
+
142
+ @staticmethod
143
+ def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
144
+ """
145
+ :param ctx:
146
+ :param features: (B, C, N)
147
+ :param idx: (B, npoint) index tensor of the features to gather
148
+ :return:
149
+ output: (B, C, npoint)
150
+ """
151
+ assert features.is_contiguous()
152
+ assert idx.is_contiguous()
153
+
154
+ B, npoint = idx.size()
155
+ _, C, N = features.size()
156
+ output = torch.cuda.FloatTensor(B, C, npoint, device=features.device)
157
+
158
+ pointnet2_cuda.gather_points_wrapper(B, C, N, npoint, features, idx, output)
159
+
160
+ ctx.for_backwards = (idx, C, N)
161
+ return output
162
+
163
+ @staticmethod
164
+ def backward(ctx, grad_out):
165
+ idx, C, N = ctx.for_backwards
166
+ B, npoint = idx.size()
167
+
168
+ grad_features = torch.zeros([B, C, N], dtype=torch.float, device=grad_out.device, requires_grad=True)
169
+ grad_out_data = grad_out.data.contiguous()
170
+ pointnet2_cuda.gather_points_grad_wrapper(B, C, N, npoint, grad_out_data, idx, grad_features.data)
171
+ return grad_features, None
172
+
173
+
174
+ gather_operation = GatherOperation.apply
175
+
176
+
177
+ class BallQuery(Function):
178
+ @staticmethod
179
+ def forward(ctx, radius: float, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tensor) -> torch.Tensor:
180
+ """
181
+ :param ctx:
182
+ :param radius: float, radius of the balls
183
+ :param nsample: int, maximum number of features in the balls
184
+ :param xyz: (B, N, 3) xyz coordinates of the features
185
+ :param new_xyz: (B, npoint, 3) centers of the ball query
186
+ :return:
187
+ idx: (B, npoint, nsample) tensor with the indicies of the features that form the query balls
188
+ """
189
+ assert new_xyz.is_contiguous()
190
+ assert xyz.is_contiguous()
191
+
192
+ B, N, _ = xyz.size()
193
+ npoint = new_xyz.size(1)
194
+ idx = torch.cuda.IntTensor(B, npoint, nsample, device=xyz.device).zero_()
195
+ pointnet2_cuda.ball_query_wrapper(B, N, npoint, radius, nsample, new_xyz, xyz, idx)
196
+ return idx
197
+
198
+ @staticmethod
199
+ def backward(ctx, a=None):
200
+ return None, None, None, None
201
+
202
+
203
+ ball_query = BallQuery.apply
204
+
205
+
206
+ class QueryAndGroup(nn.Module):
207
+ def __init__(self, radius: float, nsample: int,
208
+ relative_xyz=True,
209
+ normalize_dp=False,
210
+ normalize_by_std=False,
211
+ normalize_by_allstd=False,
212
+ normalize_by_allstd2=False,
213
+ return_only_idx=False,
214
+ **kwargs
215
+ ):
216
+ """[summary]
217
+
218
+ Args:
219
+ radius (float): radius of ball
220
+ nsample (int): maximum number of features to gather in the ball
221
+ use_xyz (bool, optional): concate xyz. Defaults to True.
222
+ ret_grouped_xyz (bool, optional): [description]. Defaults to False.
223
+ normalize_dp (bool, optional): [description]. Defaults to False.
224
+ """
225
+ super().__init__()
226
+ self.radius, self.nsample = radius, nsample
227
+ self.normalize_dp = normalize_dp
228
+ self.normalize_by_std = normalize_by_std
229
+ self.normalize_by_allstd = normalize_by_allstd
230
+ self.normalize_by_allstd2 = normalize_by_allstd2
231
+ assert self.normalize_dp + self.normalize_by_std + self.normalize_by_allstd < 2 # only nomalize by one method
232
+ self.relative_xyz = relative_xyz
233
+ self.return_only_idx = return_only_idx
234
+
235
+ def forward(self, query_xyz: torch.Tensor, support_xyz: torch.Tensor, features: torch.Tensor = None) -> Tuple[
236
+ torch.Tensor]:
237
+ """
238
+ :param query_xyz: (B, npoint, 3) xyz coordinates of the features
239
+ :param support_xyz: (B, N, 3) centroids
240
+ :param features: (B, C, N) descriptors of the features
241
+ :return:
242
+ new_features: (B, 3 + C, npoint, nsample)
243
+ """
244
+ idx = ball_query(self.radius, self.nsample, support_xyz, query_xyz)
245
+
246
+ if self.return_only_idx:
247
+ return idx
248
+ xyz_trans = support_xyz.transpose(1, 2).contiguous()
249
+ grouped_xyz = grouping_operation(xyz_trans, idx) # (B, 3, npoint, nsample)
250
+ if self.relative_xyz:
251
+ grouped_xyz = grouped_xyz - query_xyz.transpose(1, 2).unsqueeze(-1) # relative position
252
+ if self.normalize_dp:
253
+ grouped_xyz /= self.radius
254
+ grouped_features = grouping_operation(features, idx) if features is not None else None
255
+ return grouped_xyz, grouped_features
256
+
257
+
258
+ class GroupAll(nn.Module):
259
+ def __init__(self, ):
260
+ super().__init__()
261
+
262
+ def forward(self, new_xyz: torch.Tensor, xyz: torch.Tensor, features: torch.Tensor = None):
263
+ """
264
+ :param xyz: (B, N, 3) xyz coordinates of the features
265
+ :param new_xyz: ignored
266
+ :param features: (B, C, N) descriptors of the features
267
+ :return:
268
+ new_features: (B, C + 3, 1, N)
269
+ """
270
+ grouped_xyz = xyz.transpose(1, 2).unsqueeze(2)
271
+ grouped_features = features.unsqueeze(2) if features is not None else None
272
+ return grouped_xyz, grouped_features
273
+
274
+
275
+ class KNNGroup(nn.Module):
276
+ def __init__(self, nsample: int,
277
+ relative_xyz=True,
278
+ normalize_dp=False,
279
+ return_only_idx=False,
280
+ **kwargs
281
+ ):
282
+ """[summary]
283
+
284
+ Args:
285
+ nsample (int): maximum number of features to gather in the ball
286
+ use_xyz (bool, optional): concate xyz. Defaults to True.
287
+ ret_grouped_xyz (bool, optional): [description]. Defaults to False.
288
+ normalize_dp (bool, optional): [description]. Defaults to False.
289
+ """
290
+ super().__init__()
291
+ self.nsample = nsample
292
+ self.knn = KNN(nsample, transpose_mode=True)
293
+ self.relative_xyz = relative_xyz
294
+ self.normalize_dp = normalize_dp
295
+ self.return_only_idx = return_only_idx
296
+
297
+ def forward(self, query_xyz: torch.Tensor, support_xyz: torch.Tensor, features: torch.Tensor = None) -> Tuple[
298
+ torch.Tensor]:
299
+ """
300
+ :param query_xyz: (B, N, 3) xyz coordinates of the features
301
+ :param support_xyz: (B, npoint, 3) centroids
302
+ :param features: (B, C, N) descriptors of the features
303
+ :return:
304
+ new_features: (B, 3 + C, npoint, nsample)
305
+ """
306
+ _, idx = self.knn(support_xyz, query_xyz)
307
+ if self.return_only_idx:
308
+ return idx
309
+ idx = idx.int()
310
+ xyz_trans = support_xyz.transpose(1, 2).contiguous()
311
+ grouped_xyz = grouping_operation(xyz_trans, idx) # (B, 3, npoint, nsample)
312
+ if self.relative_xyz:
313
+ grouped_xyz -= query_xyz.transpose(1, 2).unsqueeze(-1) # relative position
314
+ if self.normalize_dp:
315
+ grouped_xyz /= torch.amax(torch.sqrt(torch.sum(grouped_xyz**2, dim=1)), dim=(1, 2)).view(-1, 1, 1, 1)
316
+ if features is not None:
317
+ grouped_features = grouping_operation(features, idx)
318
+ return grouped_xyz, grouped_features
319
+ else:
320
+ return grouped_xyz, None
321
+
322
+
323
+ def get_aggregation_feautres(p, dp, f, fj, feature_type='dp_fj'):
324
+ if feature_type == 'dp_fj':
325
+ fj = torch.cat([dp, fj], 1)
326
+ elif feature_type == 'dp_fj_df':
327
+ df = fj - f.unsqueeze(-1)
328
+ fj = torch.cat([dp, fj, df], 1)
329
+ elif feature_type == 'pi_dp_fj_df':
330
+ df = fj - f.unsqueeze(-1)
331
+ fj = torch.cat([p.transpose(1, 2).unsqueeze(-1).expand(-1, -1, -1, df.shape[-1]), dp, fj, df], 1)
332
+ elif feature_type == 'dp_df':
333
+ df = fj - f.unsqueeze(-1)
334
+ fj = torch.cat([dp, df], 1)
335
+ return fj
336
+
337
+
338
+ def create_grouper(group_args):
339
+ group_args_copy = copy.deepcopy(group_args)
340
+ method = group_args_copy.pop('NAME', 'ballquery')
341
+ radius = group_args_copy.pop('radius', 0.1)
342
+ nsample = group_args_copy.pop('nsample', 20)
343
+
344
+ logging.info(group_args)
345
+ if nsample is not None:
346
+ if method == 'ballquery':
347
+ grouper = QueryAndGroup(radius, nsample, **group_args_copy)
348
+ elif method == 'knn':
349
+ grouper = KNNGroup(nsample, **group_args_copy)
350
+ else:
351
+ grouper = GroupAll()
352
+ return grouper
353
+
354
+
355
+ if __name__ == "__main__":
356
+ import time
357
+
358
+ B, C, N = 2, 3, 40960
359
+ K = 16
360
+ device = 'cuda'
361
+ points = torch.randn([B, N, C], device=device, dtype=torch.float)
362
+ print(points.shape, '\n', points)
363
+
364
+ # --------------- debug downsampling
365
+ from openpoints.models.layers.layer3d import RandomSample, random_sample, furthest_point_sample
366
+
367
+ npoints = 10000
368
+ # rs = RandomSample(num_to_sample=npoints)
369
+ # query, _= rs(points)
370
+ idx = random_sample(points, npoints)
371
+ # torch gather is faster then operation gather.
372
+ query = torch.gather(points, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
373
+ print(query.shape, '\n', query)
374
+
375
+ idx = furthest_point_sample(points, npoints).to(torch.int64)
376
+ query = torch.gather(points, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
377
+ print(query.shape, '\n', query)
378
+
379
+ # # --------------- debug KNN
380
+ # knn = KNN(k=K, transpose_mode=True)
381
+ # # knn to get the neighborhood
382
+
383
+ # # compare time usage.
384
+ # st = time.time()
385
+ # for _ in range(100):
386
+ # _, knnidx = knn(points, query) # B G M
387
+ # idx_base = torch.arange(0, B, device=points.device).view(-1, 1, 1) * N
388
+ # idx = knnidx + idx_base
389
+ # idx = idx.view(-1)
390
+ # neighborhood = points.view(B * N, -1)[idx, :]
391
+ # neighborhood = neighborhood.view(B, npoints, K, 3).contiguous()
392
+ # # normalize
393
+ # neighborhood1 = neighborhood - query.unsqueeze(2)
394
+ # print(time.time() - st)
395
+ # # print(neighborhood1.shape, '\n', neighborhood1)
396
+
397
+ # knngroup = KNNGroup(K)
398
+ # # KNN Group is faster then above torch indexing when warpped in class.
399
+ # st = time.time()
400
+ # for _ in range(100):
401
+ # neighborhood2 = knngroup(query, points)
402
+ # print(time.time() - st)
403
+ # # print(neighborhood2.shape, '\n', neighborhood2)
404
+ # flag = torch.allclose(neighborhood1, neighborhood2.permute(0, 2, 3, 1))
405
+ # print(flag)
406
+
407
+ # ------------- debug ball query
408
+ query_group = QueryAndGroup(0.1, K)
409
+
410
+ st = time.time()
411
+ for _ in range(100):
412
+ # ball querying is 40 times faster then KNN
413
+ features = query_group(query, points)
414
+ print(time.time() - st)
415
+ print(features.shape)