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,663 @@
1
+ """Official implementation of PointNext
2
+ PointNeXt: Revisiting PointNet++ with Improved Training and Scaling Strategies
3
+ https://arxiv.org/abs/2206.04670
4
+ Guocheng Qian, Yuchen Li, Houwen Peng, Jinjie Mai, Hasan Abed Al Kader Hammoud, Mohamed Elhoseiny, Bernard Ghanem
5
+ """
6
+ from typing import List, Type
7
+ import logging
8
+ import torch
9
+ import torch.nn as nn
10
+ from ..build import MODELS
11
+ from ..layers import create_convblock1d, create_convblock2d, create_act, CHANNEL_MAP, \
12
+ create_grouper, furthest_point_sample, random_sample, three_interpolation, get_aggregation_feautres
13
+
14
+
15
+ def get_reduction_fn(reduction):
16
+ reduction = 'mean' if reduction.lower() == 'avg' else reduction
17
+ assert reduction in ['sum', 'max', 'mean']
18
+ if reduction == 'max':
19
+ pool = lambda x: torch.max(x, dim=-1, keepdim=False)[0]
20
+ elif reduction == 'mean':
21
+ pool = lambda x: torch.mean(x, dim=-1, keepdim=False)
22
+ elif reduction == 'sum':
23
+ pool = lambda x: torch.sum(x, dim=-1, keepdim=False)
24
+ return pool
25
+
26
+
27
+ class LocalAggregation(nn.Module):
28
+ """Local aggregation layer for a set
29
+ Set abstraction layer abstracts features from a larger set to a smaller set
30
+ Local aggregation layer aggregates features from the same set
31
+ """
32
+
33
+ def __init__(self,
34
+ channels: List[int],
35
+ norm_args={'norm': 'bn1d'},
36
+ act_args={'act': 'relu'},
37
+ group_args={'NAME': 'ballquery', 'radius': 0.1, 'nsample': 16},
38
+ conv_args=None,
39
+ feature_type='dp_fj',
40
+ reduction='max',
41
+ last_act=True,
42
+ **kwargs
43
+ ):
44
+ super().__init__()
45
+ if kwargs:
46
+ logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
47
+ channels[0] = CHANNEL_MAP[feature_type](channels[0])
48
+ convs = []
49
+ for i in range(len(channels) - 1): # #layers in each blocks
50
+ convs.append(create_convblock2d(channels[i], channels[i + 1],
51
+ norm_args=norm_args,
52
+ act_args=None if i == (
53
+ len(channels) - 2) and not last_act else act_args,
54
+ **conv_args)
55
+ )
56
+ self.convs = nn.Sequential(*convs)
57
+ self.grouper = create_grouper(group_args)
58
+ self.reduction = reduction.lower()
59
+ self.pool = get_reduction_fn(self.reduction)
60
+ self.feature_type = feature_type
61
+
62
+ def forward(self, pf) -> torch.Tensor:
63
+ # p: position, f: feature
64
+ p, f = pf
65
+ # neighborhood_features
66
+ dp, fj = self.grouper(p, p, f)
67
+ fj = get_aggregation_feautres(p, dp, f, fj, self.feature_type)
68
+ f = self.pool(self.convs(fj))
69
+ """ DEBUG neighbor numbers.
70
+ if f.shape[-1] != 1:
71
+ query_xyz, support_xyz = p, p
72
+ radius = self.grouper.radius
73
+ dist = torch.cdist(query_xyz.cpu(), support_xyz.cpu())
74
+ points = len(dist[dist < radius]) / (dist.shape[0] * dist.shape[1])
75
+ logging.info(
76
+ f'query size: {query_xyz.shape}, support size: {support_xyz.shape}, radius: {radius}, num_neighbors: {points}')
77
+ DEBUG end """
78
+ return f
79
+
80
+
81
+ class SetAbstraction(nn.Module):
82
+ """The modified set abstraction module in PointNet++ with residual connection support
83
+ """
84
+
85
+ def __init__(self,
86
+ in_channels, out_channels,
87
+ layers=1,
88
+ stride=1,
89
+ group_args={'NAME': 'ballquery',
90
+ 'radius': 0.1, 'nsample': 16},
91
+ norm_args={'norm': 'bn1d'},
92
+ act_args={'act': 'relu'},
93
+ conv_args=None,
94
+ sampler='fps',
95
+ feature_type='dp_fj',
96
+ use_res=False,
97
+ is_head=False,
98
+ **kwargs,
99
+ ):
100
+ super().__init__()
101
+ self.stride = stride
102
+ self.is_head = is_head
103
+ self.all_aggr = not is_head and stride == 1
104
+ self.use_res = use_res and not self.all_aggr and not self.is_head
105
+ self.feature_type = feature_type
106
+
107
+ mid_channel = out_channels // 2 if stride > 1 else out_channels
108
+ channels = [in_channels] + [mid_channel] * \
109
+ (layers - 1) + [out_channels]
110
+ channels[0] = in_channels if is_head else CHANNEL_MAP[feature_type](channels[0])
111
+
112
+ if self.use_res:
113
+ self.skipconv = create_convblock1d(
114
+ in_channels, channels[-1], norm_args=None, act_args=None) if in_channels != channels[
115
+ -1] else nn.Identity()
116
+ self.act = create_act(act_args)
117
+
118
+ # actually, one can use local aggregation layer to replace the following
119
+ create_conv = create_convblock1d if is_head else create_convblock2d
120
+ convs = []
121
+ for i in range(len(channels) - 1):
122
+ convs.append(create_conv(channels[i], channels[i + 1],
123
+ norm_args=norm_args if not is_head else None,
124
+ act_args=None if i == len(channels) - 2
125
+ and (self.use_res or is_head) else act_args,
126
+ **conv_args)
127
+ )
128
+ self.convs = nn.Sequential(*convs)
129
+ if not is_head:
130
+ if self.all_aggr:
131
+ group_args.nsample = None
132
+ group_args.radius = None
133
+ self.grouper = create_grouper(group_args)
134
+ self.pool = lambda x: torch.max(x, dim=-1, keepdim=False)[0]
135
+ if sampler.lower() == 'fps':
136
+ self.sample_fn = furthest_point_sample
137
+ elif sampler.lower() == 'random':
138
+ self.sample_fn = random_sample
139
+
140
+ def forward(self, pf):
141
+ p, f = pf
142
+ if self.is_head:
143
+ f = self.convs(f) # (n, c)
144
+ else:
145
+ if not self.all_aggr:
146
+ idx = self.sample_fn(p, p.shape[1] // self.stride).long()
147
+ new_p = torch.gather(p, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
148
+ else:
149
+ new_p = p
150
+ """ DEBUG neighbor numbers.
151
+ query_xyz, support_xyz = new_p, p
152
+ radius = self.grouper.radius
153
+ dist = torch.cdist(query_xyz.cpu(), support_xyz.cpu())
154
+ points = len(dist[dist < radius]) / (dist.shape[0] * dist.shape[1])
155
+ logging.info(f'query size: {query_xyz.shape}, support size: {support_xyz.shape}, radius: {radius}, num_neighbors: {points}')
156
+ DEBUG end """
157
+ if self.use_res or 'df' in self.feature_type:
158
+ fi = torch.gather(
159
+ f, -1, idx.unsqueeze(1).expand(-1, f.shape[1], -1))
160
+ if self.use_res:
161
+ identity = self.skipconv(fi)
162
+ else:
163
+ fi = None
164
+ dp, fj = self.grouper(new_p, p, f)
165
+ fj = get_aggregation_feautres(new_p, dp, fi, fj, feature_type=self.feature_type)
166
+ f = self.pool(self.convs(fj))
167
+ if self.use_res:
168
+ f = self.act(f + identity)
169
+ p = new_p
170
+ return p, f
171
+
172
+
173
+ class FeaturePropogation(nn.Module):
174
+ """The Feature Propogation module in PointNet++
175
+ """
176
+
177
+ def __init__(self, mlp,
178
+ upsample=True,
179
+ norm_args={'norm': 'bn1d'},
180
+ act_args={'act': 'relu'}
181
+ ):
182
+ """
183
+ Args:
184
+ mlp: [current_channels, next_channels, next_channels]
185
+ out_channels:
186
+ norm_args:
187
+ act_args:
188
+ """
189
+ super().__init__()
190
+ if not upsample:
191
+ self.linear2 = nn.Sequential(
192
+ nn.Linear(mlp[0], mlp[1]), nn.ReLU(inplace=True))
193
+ mlp[1] *= 2
194
+ linear1 = []
195
+ for i in range(1, len(mlp) - 1):
196
+ linear1.append(create_convblock1d(mlp[i], mlp[i + 1],
197
+ norm_args=norm_args, act_args=act_args
198
+ ))
199
+ self.linear1 = nn.Sequential(*linear1)
200
+ else:
201
+ convs = []
202
+ for i in range(len(mlp) - 1):
203
+ convs.append(create_convblock1d(mlp[i], mlp[i + 1],
204
+ norm_args=norm_args, act_args=act_args
205
+ ))
206
+ self.convs = nn.Sequential(*convs)
207
+
208
+ self.pool = lambda x: torch.mean(x, dim=-1, keepdim=False)
209
+
210
+ def forward(self, pf1, pf2=None):
211
+ # pfb1 is with the same size of upsampled points
212
+ if pf2 is None:
213
+ _, f = pf1 # (B, N, 3), (B, C, N)
214
+ f_global = self.pool(f)
215
+ f = torch.cat(
216
+ (f, self.linear2(f_global).unsqueeze(-1).expand(-1, -1, f.shape[-1])), dim=1)
217
+ f = self.linear1(f)
218
+ else:
219
+ p1, f1 = pf1
220
+ p2, f2 = pf2
221
+ if f1 is not None:
222
+ f = self.convs(
223
+ torch.cat((f1, three_interpolation(p1, p2, f2)), dim=1))
224
+ else:
225
+ f = self.convs(three_interpolation(p1, p2, f2))
226
+ return f
227
+
228
+
229
+ class InvResMLP(nn.Module):
230
+ def __init__(self,
231
+ in_channels,
232
+ norm_args=None,
233
+ act_args=None,
234
+ aggr_args={'feature_type': 'dp_fj', "reduction": 'max'},
235
+ group_args={'NAME': 'ballquery'},
236
+ conv_args=None,
237
+ expansion=1,
238
+ use_res=True,
239
+ num_posconvs=2,
240
+ less_act=False,
241
+ **kwargs
242
+ ):
243
+ super().__init__()
244
+ self.use_res = use_res
245
+ mid_channels = int(in_channels * expansion)
246
+ self.convs = LocalAggregation([in_channels, in_channels],
247
+ norm_args=norm_args, act_args=act_args if num_posconvs > 0 else None,
248
+ group_args=group_args, conv_args=conv_args,
249
+ **aggr_args, **kwargs)
250
+ if num_posconvs < 1:
251
+ channels = []
252
+ elif num_posconvs == 1:
253
+ channels = [in_channels, in_channels]
254
+ else:
255
+ channels = [in_channels, mid_channels, in_channels]
256
+ pwconv = []
257
+ # point wise after depth wise conv (without last layer)
258
+ for i in range(len(channels) - 1):
259
+ pwconv.append(create_convblock1d(channels[i], channels[i + 1],
260
+ norm_args=norm_args,
261
+ act_args=act_args if
262
+ (i != len(channels) - 2) and not less_act else None,
263
+ **conv_args)
264
+ )
265
+ self.pwconv = nn.Sequential(*pwconv)
266
+ self.act = create_act(act_args)
267
+
268
+ def forward(self, pf):
269
+ p, f = pf
270
+ identity = f
271
+ f = self.convs([p, f])
272
+ f = self.pwconv(f)
273
+ if f.shape[-1] == identity.shape[-1] and self.use_res:
274
+ f += identity
275
+ f = self.act(f)
276
+ return [p, f]
277
+
278
+
279
+ class ResBlock(nn.Module):
280
+ def __init__(self,
281
+ in_channels,
282
+ norm_args=None,
283
+ act_args=None,
284
+ aggr_args={'feature_type': 'dp_fj', "reduction": 'max'},
285
+ group_args={'NAME': 'ballquery'},
286
+ conv_args=None,
287
+ expansion=1,
288
+ use_res=True,
289
+ **kwargs
290
+ ):
291
+ super().__init__()
292
+ self.use_res = use_res
293
+ mid_channels = in_channels * expansion
294
+ self.convs = LocalAggregation([in_channels, in_channels, mid_channels, in_channels],
295
+ norm_args=norm_args, act_args=None,
296
+ group_args=group_args, conv_args=conv_args,
297
+ **aggr_args, **kwargs)
298
+ self.act = create_act(act_args)
299
+
300
+ def forward(self, pf):
301
+ p, f = pf
302
+ identity = f
303
+ f = self.convs([p, f])
304
+ if f.shape[-1] == identity.shape[-1] and self.use_res:
305
+ f += identity
306
+ f = self.act(f)
307
+ return [p, f]
308
+
309
+
310
+ @MODELS.register_module()
311
+ class PointNextEncoder(nn.Module):
312
+ r"""The Encoder for PointNext
313
+ `"PointNeXt: Revisiting PointNet++ with Improved Training and Scaling Strategies".
314
+ <https://arxiv.org/abs/2206.04670>`_.
315
+ .. note::
316
+ For an example of using :obj:`PointNextEncoder`, see
317
+ `examples/segmentation/main.py <https://github.com/guochengqian/PointNeXt/blob/master/cfgs/s3dis/README.md>`_.
318
+ Args:
319
+ in_channels (int, optional): input channels . Defaults to 4.
320
+ width (int, optional): width of network, the output mlp of the stem MLP. Defaults to 32.
321
+ blocks (List[int], optional): # of blocks per stage (including the SA block). Defaults to [1, 4, 7, 4, 4].
322
+ strides (List[int], optional): the downsampling ratio of each stage. Defaults to [4, 4, 4, 4].
323
+ block (strorType[InvResMLP], optional): the block to use for depth scaling. Defaults to 'InvResMLP'.
324
+ nsample (intorList[int], optional): the number of neighbors to query for each block. Defaults to 32.
325
+ radius (floatorList[float], optional): the initial radius. Defaults to 0.1.
326
+ aggr_args (_type_, optional): the args for local aggregataion. Defaults to {'feature_type': 'dp_fj', "reduction": 'max'}.
327
+ group_args (_type_, optional): the args for grouping. Defaults to {'NAME': 'ballquery'}.
328
+ norm_args (_type_, optional): the args for normalization layer. Defaults to {'norm': 'bn'}.
329
+ act_args (_type_, optional): the args for activation layer. Defaults to {'act': 'relu'}.
330
+ expansion (int, optional): the expansion ratio of the InvResMLP block. Defaults to 4.
331
+ sa_layers (int, optional): the number of MLP layers to use in the SA block. Defaults to 1.
332
+ sa_use_res (bool, optional): wheter to use residual connection in SA block. Set to True only for PointNeXt-S.
333
+ """
334
+
335
+ def __init__(self,
336
+ in_channels: int = 4,
337
+ width: int = 32,
338
+ blocks: List[int] = [1, 4, 7, 4, 4],
339
+ strides: List[int] = [4, 4, 4, 4],
340
+ block: str or Type[InvResMLP] = 'InvResMLP',
341
+ nsample: int or List[int] = 32,
342
+ radius: float or List[float] = 0.1,
343
+ aggr_args: dict = {'feature_type': 'dp_fj', "reduction": 'max'},
344
+ group_args: dict = {'NAME': 'ballquery'},
345
+ sa_layers: int = 1,
346
+ sa_use_res: bool = False,
347
+ **kwargs
348
+ ):
349
+ super().__init__()
350
+ if isinstance(block, str):
351
+ block = eval(block)
352
+ self.blocks = blocks
353
+ self.strides = strides
354
+ self.in_channels = in_channels
355
+ self.aggr_args = aggr_args
356
+ self.norm_args = kwargs.get('norm_args', {'norm': 'bn'})
357
+ self.act_args = kwargs.get('act_args', {'act': 'relu'})
358
+ self.conv_args = kwargs.get('conv_args', None)
359
+ self.sampler = kwargs.get('sampler', 'fps')
360
+ self.expansion = kwargs.get('expansion', 4)
361
+ self.sa_layers = sa_layers
362
+ self.sa_use_res = sa_use_res
363
+ self.use_res = kwargs.get('use_res', True)
364
+ radius_scaling = kwargs.get('radius_scaling', 2)
365
+ nsample_scaling = kwargs.get('nsample_scaling', 1)
366
+
367
+ self.radii = self._to_full_list(radius, radius_scaling)
368
+ self.nsample = self._to_full_list(nsample, nsample_scaling)
369
+ logging.info(f'radius: {self.radii},\n nsample: {self.nsample}')
370
+
371
+ # double width after downsampling.
372
+ channels = []
373
+ for stride in strides:
374
+ if stride != 1:
375
+ width *= 2
376
+ channels.append(width)
377
+ encoder = []
378
+ for i in range(len(blocks)):
379
+ group_args.radius = self.radii[i]
380
+ group_args.nsample = self.nsample[i]
381
+ encoder.append(self._make_enc(
382
+ block, channels[i], blocks[i], stride=strides[i], group_args=group_args,
383
+ is_head=i == 0 and strides[i] == 1
384
+ ))
385
+ self.encoder = nn.Sequential(*encoder)
386
+ self.out_channels = channels[-1]
387
+ self.channel_list = channels
388
+
389
+ def _to_full_list(self, param, param_scaling=1):
390
+ # param can be: radius, nsample
391
+ param_list = []
392
+ if isinstance(param, List):
393
+ # make param a full list
394
+ for i, value in enumerate(param):
395
+ value = [value] if not isinstance(value, List) else value
396
+ if len(value) != self.blocks[i]:
397
+ value += [value[-1]] * (self.blocks[i] - len(value))
398
+ param_list.append(value)
399
+ else: # radius is a scalar (in this case, only initial raidus is provide), then create a list (radius for each block)
400
+ for i, stride in enumerate(self.strides):
401
+ if stride == 1:
402
+ param_list.append([param] * self.blocks[i])
403
+ else:
404
+ param_list.append(
405
+ [param] + [param * param_scaling] * (self.blocks[i] - 1))
406
+ param *= param_scaling
407
+ return param_list
408
+
409
+ def _make_enc(self, block, channels, blocks, stride, group_args, is_head=False):
410
+ layers = []
411
+ radii = group_args.radius
412
+ nsample = group_args.nsample
413
+ group_args.radius = radii[0]
414
+ group_args.nsample = nsample[0]
415
+ layers.append(SetAbstraction(self.in_channels, channels,
416
+ self.sa_layers if not is_head else 1, stride,
417
+ group_args=group_args,
418
+ sampler=self.sampler,
419
+ norm_args=self.norm_args, act_args=self.act_args, conv_args=self.conv_args,
420
+ is_head=is_head, use_res=self.sa_use_res, **self.aggr_args
421
+ ))
422
+ self.in_channels = channels
423
+ for i in range(1, blocks):
424
+ group_args.radius = radii[i]
425
+ group_args.nsample = nsample[i]
426
+ layers.append(block(self.in_channels,
427
+ aggr_args=self.aggr_args,
428
+ norm_args=self.norm_args, act_args=self.act_args, group_args=group_args,
429
+ conv_args=self.conv_args, expansion=self.expansion,
430
+ use_res=self.use_res
431
+ ))
432
+ return nn.Sequential(*layers)
433
+
434
+ def forward_cls_feat(self, p0, f0=None):
435
+ if hasattr(p0, 'keys'):
436
+ p0, f0 = p0['pos'], p0.get('x', None)
437
+ if f0 is None:
438
+ f0 = p0.clone().transpose(1, 2).contiguous()
439
+ for i in range(0, len(self.encoder)):
440
+ p0, f0 = self.encoder[i]([p0, f0])
441
+ return f0.squeeze(-1)
442
+
443
+ def forward_seg_feat(self, p0, f0=None):
444
+ if hasattr(p0, 'keys'):
445
+ p0, f0 = p0['pos'], p0.get('x', None)
446
+ if f0 is None:
447
+ f0 = p0.clone().transpose(1, 2).contiguous()
448
+ p, f = [p0], [f0]
449
+ for i in range(0, len(self.encoder)):
450
+ _p, _f = self.encoder[i]([p[-1], f[-1]])
451
+ p.append(_p)
452
+ f.append(_f)
453
+ return p, f
454
+
455
+ def forward(self, p0, f0=None):
456
+ return self.forward_seg_feat(p0, f0)
457
+
458
+
459
+ @MODELS.register_module()
460
+ class PointNextDecoder(nn.Module):
461
+ def __init__(self,
462
+ encoder_channel_list: List[int],
463
+ decoder_layers: int = 2,
464
+ decoder_stages: int = 4,
465
+ **kwargs
466
+ ):
467
+ super().__init__()
468
+ self.decoder_layers = decoder_layers
469
+ self.in_channels = encoder_channel_list[-1]
470
+ skip_channels = encoder_channel_list[:-1]
471
+ if len(skip_channels) < decoder_stages:
472
+ skip_channels.insert(0, kwargs.get('in_channels', 3))
473
+ # the output channel after interpolation
474
+ fp_channels = encoder_channel_list[:decoder_stages]
475
+
476
+ n_decoder_stages = len(fp_channels)
477
+ decoder = [[] for _ in range(n_decoder_stages)]
478
+ for i in range(-1, -n_decoder_stages - 1, -1):
479
+ decoder[i] = self._make_dec(
480
+ skip_channels[i], fp_channels[i])
481
+ self.decoder = nn.Sequential(*decoder)
482
+ self.out_channels = fp_channels[-n_decoder_stages]
483
+
484
+ def _make_dec(self, skip_channels, fp_channels):
485
+ layers = []
486
+ mlp = [skip_channels + self.in_channels] + \
487
+ [fp_channels] * self.decoder_layers
488
+ layers.append(FeaturePropogation(mlp))
489
+ self.in_channels = fp_channels
490
+ return nn.Sequential(*layers)
491
+
492
+ def forward(self, p, f):
493
+ for i in range(-1, -len(self.decoder) - 1, -1):
494
+ f[i - 1] = self.decoder[i][1:](
495
+ [p[i], self.decoder[i][0]([p[i - 1], f[i - 1]], [p[i], f[i]])])[1]
496
+ return f[-len(self.decoder) - 1]
497
+
498
+
499
+ @MODELS.register_module()
500
+ class PointNextPartDecoder(nn.Module):
501
+ def __init__(self,
502
+ encoder_channel_list: List[int],
503
+ decoder_layers: int = 2,
504
+ decoder_blocks: List[int] = [1, 1, 1, 1],
505
+ decoder_strides: List[int] = [4, 4, 4, 4],
506
+ act_args: str = 'relu',
507
+ cls_map='pointnet2',
508
+ num_classes: int = 16,
509
+ cls2partembed=None,
510
+ **kwargs
511
+ ):
512
+ super().__init__()
513
+ self.decoder_layers = decoder_layers
514
+ self.in_channels = encoder_channel_list[-1]
515
+ skip_channels = encoder_channel_list[:-1]
516
+ fp_channels = encoder_channel_list[:-1]
517
+
518
+ # the following is for decoder blocks
519
+ self.conv_args = kwargs.get('conv_args', None)
520
+ radius_scaling = kwargs.get('radius_scaling', 2)
521
+ nsample_scaling = kwargs.get('nsample_scaling', 1)
522
+ block = kwargs.get('block', 'InvResMLP')
523
+ if isinstance(block, str):
524
+ block = eval(block)
525
+ self.blocks = decoder_blocks
526
+ self.strides = decoder_strides
527
+ self.norm_args = kwargs.get('norm_args', {'norm': 'bn'})
528
+ self.act_args = kwargs.get('act_args', {'act': 'relu'})
529
+ self.expansion = kwargs.get('expansion', 4)
530
+ radius = kwargs.get('radius', 0.1)
531
+ nsample = kwargs.get('nsample', 16)
532
+ self.radii = self._to_full_list(radius, radius_scaling)
533
+ self.nsample = self._to_full_list(nsample, nsample_scaling)
534
+ self.cls_map = cls_map
535
+ self.num_classes = num_classes
536
+ self.use_res = kwargs.get('use_res', True)
537
+ group_args = kwargs.get('group_args', {'NAME': 'ballquery'})
538
+ self.aggr_args = kwargs.get('aggr_args',
539
+ {'feature_type': 'dp_fj', "reduction": 'max'}
540
+ )
541
+ if self.cls_map == 'curvenet':
542
+ # global features
543
+ self.global_conv2 = nn.Sequential(
544
+ create_convblock1d(fp_channels[-1] * 2, 128,
545
+ norm_args=None,
546
+ act_args=act_args))
547
+ self.global_conv1 = nn.Sequential(
548
+ create_convblock1d(fp_channels[-2] * 2, 64,
549
+ norm_args=None,
550
+ act_args=act_args))
551
+ skip_channels[0] += 64 + 128 + 16 # shape categories labels
552
+ elif self.cls_map == 'pointnet2':
553
+ self.convc = nn.Sequential(create_convblock1d(16, 64,
554
+ norm_args=None,
555
+ act_args=act_args))
556
+ skip_channels[0] += 64 # shape categories labels
557
+
558
+ elif self.cls_map == 'pointnext':
559
+ self.global_conv2 = nn.Sequential(
560
+ create_convblock1d(fp_channels[-1] * 2, 128,
561
+ norm_args=None,
562
+ act_args=act_args))
563
+ self.global_conv1 = nn.Sequential(
564
+ create_convblock1d(fp_channels[-2] * 2, 64,
565
+ norm_args=None,
566
+ act_args=act_args))
567
+ skip_channels[0] += 64 + 128 + 50 # shape categories labels
568
+ self.cls2partembed = cls2partembed
569
+ elif self.cls_map == 'pointnext1':
570
+ self.convc = nn.Sequential(create_convblock1d(50, 64,
571
+ norm_args=None,
572
+ act_args=act_args))
573
+ skip_channels[0] += 64 # shape categories labels
574
+ self.cls2partembed = cls2partembed
575
+
576
+ n_decoder_stages = len(fp_channels)
577
+ decoder = [[] for _ in range(n_decoder_stages)]
578
+ for i in range(-1, -n_decoder_stages - 1, -1):
579
+ group_args.radius = self.radii[i]
580
+ group_args.nsample = self.nsample[i]
581
+ decoder[i] = self._make_dec(
582
+ skip_channels[i], fp_channels[i], group_args=group_args, block=block, blocks=self.blocks[i])
583
+
584
+ self.decoder = nn.Sequential(*decoder)
585
+ self.out_channels = fp_channels[-n_decoder_stages]
586
+
587
+ def _make_dec(self, skip_channels, fp_channels, group_args=None, block=None, blocks=1):
588
+ layers = []
589
+ radii = group_args.radius
590
+ nsample = group_args.nsample
591
+ mlp = [skip_channels + self.in_channels] + \
592
+ [fp_channels] * self.decoder_layers
593
+ layers.append(FeaturePropogation(mlp, act_args=self.act_args))
594
+ self.in_channels = fp_channels
595
+ for i in range(1, blocks):
596
+ group_args.radius = radii[i]
597
+ group_args.nsample = nsample[i]
598
+ layers.append(block(self.in_channels,
599
+ aggr_args=self.aggr_args,
600
+ norm_args=self.norm_args, act_args=self.act_args, group_args=group_args,
601
+ conv_args=self.conv_args, expansion=self.expansion,
602
+ use_res=self.use_res
603
+ ))
604
+ return nn.Sequential(*layers)
605
+
606
+ def _to_full_list(self, param, param_scaling=1):
607
+ # param can be: radius, nsample
608
+ param_list = []
609
+ if isinstance(param, List):
610
+ # make param a full list
611
+ for i, value in enumerate(param):
612
+ value = [value] if not isinstance(value, List) else value
613
+ if len(value) != self.blocks[i]:
614
+ value += [value[-1]] * (self.blocks[i] - len(value))
615
+ param_list.append(value)
616
+ else: # radius is a scalar (in this case, only initial raidus is provide), then create a list (radius for each block)
617
+ for i, stride in enumerate(self.strides):
618
+ if stride == 1:
619
+ param_list.append([param] * self.blocks[i])
620
+ else:
621
+ param_list.append(
622
+ [param] + [param * param_scaling] * (self.blocks[i] - 1))
623
+ param *= param_scaling
624
+ return param_list
625
+
626
+ def forward(self, p, f, cls_label):
627
+ B, N = p[0].shape[0:2]
628
+ if self.cls_map == 'curvenet':
629
+ emb1 = self.global_conv1(f[-2])
630
+ emb1 = emb1.max(dim=-1, keepdim=True)[0] # bs, 64, 1
631
+ emb2 = self.global_conv2(f[-1])
632
+ emb2 = emb2.max(dim=-1, keepdim=True)[0] # bs, 128, 1
633
+ cls_one_hot = torch.zeros((B, self.num_classes), device=p[0].device)
634
+ cls_one_hot = cls_one_hot.scatter_(1, cls_label, 1).unsqueeze(-1)
635
+ cls_one_hot = torch.cat((emb1, emb2, cls_one_hot), dim=1)
636
+ cls_one_hot = cls_one_hot.expand(-1, -1, N)
637
+ elif self.cls_map == 'pointnet2':
638
+ cls_one_hot = torch.zeros((B, self.num_classes), device=p[0].device)
639
+ cls_one_hot = cls_one_hot.scatter_(1, cls_label, 1).unsqueeze(-1).repeat(1, 1, N)
640
+ cls_one_hot = self.convc(cls_one_hot)
641
+ elif self.cls_map == 'pointnext':
642
+ emb1 = self.global_conv1(f[-2])
643
+ emb1 = emb1.max(dim=-1, keepdim=True)[0] # bs, 64, 1
644
+ emb2 = self.global_conv2(f[-1])
645
+ emb2 = emb2.max(dim=-1, keepdim=True)[0] # bs, 128, 1
646
+ self.cls2partembed = self.cls2partembed.to(p[0].device)
647
+ cls_one_hot = self.cls2partembed[cls_label.squeeze()].unsqueeze(-1)
648
+ cls_one_hot = torch.cat((emb1, emb2, cls_one_hot), dim=1)
649
+ cls_one_hot = cls_one_hot.expand(-1, -1, N)
650
+ elif self.cls_map == 'pointnext1':
651
+ self.cls2partembed = self.cls2partembed.to(p[0].device)
652
+ cls_one_hot = self.cls2partembed[cls_label.squeeze()].unsqueeze(-1).expand(-1, -1, N)
653
+ cls_one_hot = self.convc(cls_one_hot)
654
+
655
+ for i in range(-1, -len(self.decoder), -1):
656
+ f[i - 1] = self.decoder[i][1:](
657
+ [p[i-1], self.decoder[i][0]([p[i - 1], f[i - 1]], [p[i], f[i]])])[1]
658
+
659
+ # TODO: study where to add this ?
660
+ f[-len(self.decoder) - 1] = self.decoder[0][1:](
661
+ [p[1], self.decoder[0][0]([p[1], torch.cat([cls_one_hot, f[1]], 1)], [p[2], f[2]])])[1]
662
+
663
+ return f[-len(self.decoder) - 1]