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,216 @@
1
+ """ Maksed PointViT in PyTorch
2
+ Copyright 2022@PointNeXt team
3
+ """
4
+ import logging
5
+ import torch
6
+ import torch.nn as nn
7
+ from ..build import MODELS
8
+ from ..layers.attention import Block
9
+ from ..layers import create_norm, create_linearblock
10
+ from ..layers.graph_conv import GraphConv, DilatedKNN
11
+
12
+
13
+ @MODELS.register_module()
14
+ class MaskedTransformerDecoder(nn.Module):
15
+ """ MaskedTransformerDecoder
16
+ """
17
+
18
+ def __init__(self,
19
+ embed_dim, # the out features dim of the encoder
20
+ group_size=32, # the number of points inside each group
21
+ decoder_embed_dim=192, decoder_depth=4, decoder_num_heads=16,
22
+ norm_args={'norm': 'ln', 'eps': 1.0e-6},
23
+ act_args={'act': 'gelu'},
24
+ add_pos_each_block=True,
25
+ **kwargs
26
+ ):
27
+ super().__init__()
28
+ if kwargs:
29
+ logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
30
+
31
+ # ------------------------------------------------------------------
32
+ # MAE decoder
33
+ self.decoder_embed = nn.Linear(embed_dim, decoder_embed_dim, bias=True)
34
+ self.mask_token = nn.Parameter(torch.randn(1, 1, decoder_embed_dim))
35
+ self.decoder_cls_pos = nn.Parameter(torch.randn(1, 1, decoder_embed_dim))
36
+ self.decoder_pos_embed = nn.Sequential(
37
+ nn.Linear(3, 128),
38
+ nn.GELU(),
39
+ nn.Linear(128, decoder_embed_dim)
40
+ )
41
+ self.add_pos_each_block = add_pos_each_block
42
+ self.decoder_blocks = nn.ModuleList([
43
+ Block(
44
+ dim=decoder_embed_dim, num_heads=decoder_num_heads,
45
+ norm_args=norm_args, act_args=act_args
46
+ )
47
+ for _ in range(decoder_depth)])
48
+ self.decoder_norm = create_norm(norm_args, decoder_embed_dim)
49
+ self.decoder_pred = nn.Linear(decoder_embed_dim, group_size * 3, bias=True) # decoder to patch
50
+ # ------------------------------------------------------------------
51
+ self.initialize_weights()
52
+
53
+ def initialize_weights(self):
54
+ torch.nn.init.normal_(self.decoder_cls_pos, std=.02)
55
+ torch.nn.init.normal_(self.mask_token, std=.02)
56
+ # initialize nn.Linear and nn.LayerNorm
57
+ self.apply(self._init_weights)
58
+
59
+ @staticmethod
60
+ def _init_weights(m):
61
+ if isinstance(m, nn.Linear):
62
+ torch.nn.init.xavier_uniform_(m.weight)
63
+ if isinstance(m, nn.Linear) and m.bias is not None:
64
+ nn.init.constant_(m.bias, 0)
65
+ elif isinstance(m, (nn.LayerNorm, nn.GroupNorm, nn.BatchNorm2d, nn.BatchNorm1d)):
66
+ nn.init.constant_(m.bias, 0)
67
+ nn.init.constant_(m.weight, 1.0)
68
+
69
+ def forward(self, center_xyz, features, ids_restore):
70
+ # embed tokens
71
+ features = self.decoder_embed(features)
72
+ B, L, C = features.shape # batch size, length, channels
73
+
74
+ # in the decoder part. we know the positional encoding of groups
75
+ decoder_pos_embed = torch.cat(
76
+ (self.decoder_cls_pos.expand(B, -1, -1), self.decoder_pos_embed(center_xyz)), dim=1)
77
+
78
+ # append mask tokens to sequence
79
+ # use mask tokens to fill the masked features. this is why missing part should be 1 not 0.
80
+ mask_tokens = self.mask_token.repeat(B, ids_restore.shape[1] + 1 - L, 1) # +1, since features contains additional cls token.
81
+ x_ = torch.cat([features[:, 1:, :], mask_tokens], dim=1) # no cls token
82
+ x_ = torch.gather(x_, dim=1, index=ids_restore.unsqueeze(-1).repeat(1, 1, C)) # unshuffle
83
+ features = torch.cat([features[:, :1, :], x_], dim=1) # append cls token
84
+
85
+ if self.add_pos_each_block:
86
+ for block in self.decoder_blocks:
87
+ features = block(features + decoder_pos_embed)
88
+ else:
89
+ features = self.pos_drop(features + decoder_pos_embed)
90
+ for block in self.decoder_blocks:
91
+ features = block(features)
92
+
93
+ features = self.decoder_norm(features)
94
+ # predictor projection
95
+ features = self.decoder_pred(features)
96
+ # remove cls token
97
+ features = features[:, 1:, :]
98
+ return features
99
+
100
+
101
+ @MODELS.register_module()
102
+ class FoldingNet(nn.Module):
103
+ """ FoldingNet.
104
+ Used in many methods, e.g. FoldingNet, PCN, OcCo, Point-BERT
105
+ learning point reconstruction only from global feature
106
+ """
107
+ def __init__(self, in_channels, emb_dims=1024,
108
+ num_fine=1024,
109
+ grid_size=2,
110
+ **kwargs):
111
+ super().__init__()
112
+ if kwargs:
113
+ logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
114
+
115
+ self.grid_size = grid_size
116
+ self.num_coarse = num_fine // grid_size**2
117
+ self.num_fine = num_fine
118
+
119
+ self.folding1 = nn.Sequential(
120
+ nn.Linear(in_channels, emb_dims),
121
+ nn.ReLU(inplace=True),
122
+ nn.Linear(emb_dims, emb_dims),
123
+ nn.ReLU(inplace=True),
124
+ nn.Linear(emb_dims, self.num_coarse * 3))
125
+
126
+ self.folding2 = nn.Sequential(
127
+ nn.Linear(emb_dims+2+3, 512),
128
+ nn.ReLU(),
129
+ nn.Linear(512, 512),
130
+ nn.ReLU(),
131
+ nn.Linear(512, 3))
132
+
133
+ a = torch.linspace(-0.05, 0.05, steps=self.grid_size, dtype=torch.float).view(1, self.grid_size).expand(self.grid_size, self.grid_size).reshape(1, -1)
134
+ b = torch.linspace(-0.05, 0.05, steps=self.grid_size, dtype=torch.float).view(self.grid_size, 1).expand(self.grid_size, self.grid_size).reshape(1, -1)
135
+ self.register_buffer('folding_seed', torch.cat([a, b], dim=0).reshape(1, 2, self.grid_size ** 2).transpose(1, 2))
136
+ self.model_init()
137
+
138
+ def model_init(self):
139
+ for m in self.modules():
140
+ if isinstance(m, (torch.nn.Conv2d, torch.nn.Conv1d)):
141
+ torch.nn.init.kaiming_normal_(m.weight)
142
+ m.weight.requires_grad = True
143
+ if m.bias is not None:
144
+ m.bias.data.zero_()
145
+ m.bias.requires_grad = True
146
+ elif isinstance(m, (nn.LayerNorm, nn.GroupNorm, nn.BatchNorm2d, nn.BatchNorm1d)):
147
+ nn.init.constant_(m.bias, 0)
148
+ nn.init.constant_(m.weight, 1.0)
149
+
150
+ def forward(self, xyz, x, **kwargs):
151
+ B = x.shape[0]
152
+ coarse = self.folding1(x)
153
+ coarse = coarse.view(-1, self.num_coarse, 3)
154
+ point_feat = coarse.unsqueeze(2).expand(-1, -1, self.grid_size**2, -1).reshape([-1, self.num_fine, 3])
155
+
156
+ seed = self.folding_seed.unsqueeze(1).expand(B, self.num_coarse, -1, -1).reshape(B, self.num_fine, -1)
157
+
158
+ x = x.unsqueeze(1).expand(-1, self.num_fine, -1)
159
+ feat = torch.cat([x, seed, point_feat], dim=-1)
160
+
161
+ center = coarse.unsqueeze(2).expand(-1, -1, self.grid_size**2, -1).reshape([-1, self.num_fine, 3])
162
+
163
+ fine = self.folding2(feat) + center
164
+
165
+ return coarse, fine
166
+
167
+
168
+ @MODELS.register_module()
169
+ class NodeShuffle(nn.Module):
170
+ """ NodeShuffle
171
+ proposed in PU-GCN
172
+ """
173
+
174
+ def __init__(self,
175
+ in_channels,
176
+ up_ratio=16,
177
+ emb_dims=1024,
178
+ k=16,
179
+ norm_args={'norm': 'bn'},
180
+ act_args={'act': 'relu'},
181
+ **kwargs):
182
+ super().__init__()
183
+ if kwargs:
184
+ logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
185
+
186
+ self.up_ratio = up_ratio
187
+ conv = 'edge'
188
+ self.knn = DilatedKNN(k, 1)
189
+ self.convs = nn.Sequential(
190
+ GraphConv(in_channels, emb_dims, conv, norm_args=norm_args, act_args=act_args),
191
+ GraphConv(emb_dims, emb_dims, conv, norm_args=norm_args, act_args=act_args)
192
+ )
193
+ self.proj = create_linearblock(emb_dims, 3 * up_ratio)
194
+ self.model_init()
195
+
196
+ def model_init(self):
197
+ for m in self.modules():
198
+ if isinstance(m, (torch.nn.Conv2d, torch.nn.Conv1d)):
199
+ torch.nn.init.kaiming_normal_(m.weight)
200
+ m.weight.requires_grad = True
201
+ if m.bias is not None:
202
+ m.bias.data.zero_()
203
+ m.bias.requires_grad = True
204
+ elif isinstance(m, (nn.LayerNorm, nn.GroupNorm, nn.BatchNorm2d, nn.BatchNorm1d)):
205
+ nn.init.constant_(m.bias, 0)
206
+ nn.init.constant_(m.weight, 1.0)
207
+
208
+ def forward(self, xyz, feature, **kwargs):
209
+ # learn displacement
210
+ B, C, N = feature.shape
211
+ feature = feature.unsqueeze(-1)
212
+ edge_index = self.knn(xyz)
213
+ for conv in self.convs:
214
+ feature = conv(feature, edge_index)
215
+ new_xyz = self.proj(feature.squeeze(-1).transpose(1, 2)).view(B, N, -1, 3) + xyz.unsqueeze(2).repeat(-1, -1, self.up_ratio, -1)
216
+ return new_xyz.view(B, -1, 3)
@@ -0,0 +1,116 @@
1
+ """ Maksed PointViT in PyTorch
2
+ Copyright 2022@PointNeXt team
3
+ """
4
+ import logging
5
+ import torch
6
+ import torch.nn as nn
7
+ from openpoints.cpp.chamfer_dist import ChamferDistanceL1
8
+ from ..build import build_model_from_cfg, MODELS
9
+
10
+
11
+ @MODELS.register_module()
12
+ class MaskedPoint(nn.Module):
13
+ """ Masked AutoEncoder for Point-based methods
14
+ """
15
+ def __init__(self,
16
+ backbone_args,
17
+ decoder_args,
18
+ mask_ratio,
19
+ **kwargs
20
+ ):
21
+ super().__init__()
22
+ if kwargs:
23
+ logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
24
+
25
+ # ------------------------------------------------------------------
26
+ # MAE encoder. e.g. DGCNN, DeepGCN, PointNet++
27
+ self.encoder = build_model_from_cfg(backbone_args)
28
+
29
+ # ------------------------------------------------------------------
30
+ # MAE decoder. e.g. FoldingNet (works bad in random sampling), PU-GCN (NodeShuffle)
31
+ self.use_global_feat = True if decoder_args.NAME.lower() in ['foldingnet', 'pointcompletion'] else False
32
+ self.maxpool = lambda x: torch.max(x, dim=-1, keepdim=False)[0]
33
+ self.decoder = build_model_from_cfg(decoder_args)
34
+
35
+ # ------------------------------------------------------------------
36
+ # loss
37
+ self.mask_ratio = mask_ratio
38
+ self.build_loss_func()
39
+
40
+ @staticmethod
41
+ def random_masking(x, features=None, mask_ratio=0.9):
42
+ """
43
+ Perform per-sample random masking by per-sample shuffling.
44
+ Per-sample shuffling is done by argsort random noise.
45
+ x: [N, L, 3], sequence
46
+ features: [N, D, L], sequence
47
+ TODO: suppport other masking. Like OcCo, block masking as ablation
48
+ """
49
+ N, L, _ = x.shape # batch, length, dim
50
+ len_keep = int(L * (1 - mask_ratio))
51
+
52
+ noise = torch.rand(N, L, device=x.device) # noise in [0, 1]
53
+
54
+ # sort noise for each sample
55
+ ids_shuffle = torch.argsort(noise, dim=1) # ascend: small is keep, large is remove
56
+ ids_restore = torch.argsort(ids_shuffle, dim=1)
57
+
58
+ # keep the first subset
59
+ ids_keep = ids_shuffle[:, :len_keep]
60
+ x_masked = torch.gather(x, dim=1, index=ids_keep.unsqueeze(-1).expand(-1, -1, 3))
61
+
62
+ # generate the binary mask: 0 is keep, 1 is remove
63
+ mask = torch.ones([N, L], device=x.device)
64
+ mask[:, :len_keep] = 0
65
+ # unshuffle to get the binary mask
66
+ mask = torch.gather(mask, dim=1, index=ids_restore)
67
+
68
+ # features
69
+ if features is not None:
70
+ features_masked = torch.gather(x, dim=2, index=ids_keep.unsqueeze(1).expand(-1, features.shape[1], -1))
71
+ else:
72
+ features_masked = None
73
+ return x_masked, features_masked, mask, ids_restore, ids_keep
74
+
75
+ def build_loss_func(self, smoothing=False):
76
+ self.criterion = ChamferDistanceL1()
77
+
78
+ def forward_loss(self, xyz, pred, mask=None):
79
+ """
80
+ # chamfer distance. two options
81
+ 1. chamfer distance on the merged point clouds.
82
+ 2. chamfer distance per local patch
83
+
84
+
85
+ xyz: the original points [B, N, 3]
86
+ grouped_xyz: the points after grouping. [B, 3, L, K]
87
+ pred: [B, L, K*3]
88
+ mask: [B, L], 0 is keep, 1 is remove,
89
+ idx_keep: [B, L]
90
+ """
91
+ if isinstance(pred, (tuple, list)):
92
+ loss = 0.
93
+ for pred_i in pred:
94
+ loss += self.criterion(pred_i, xyz)
95
+ else:
96
+ loss = self.criterion(pred, xyz)
97
+ # mask = mask.unsqueeze(-1)
98
+ # grouped_xyz = torch.mul(grouped_xyz.permute(0, 2, 3, 1), mask.unsqueeze(-1)).reshape(-1, K, C)
99
+ # pred = torch.mul(pred, mask).reshape(-1, K, C)
100
+ # loss = self.criterion(pred, grouped_xyz)/ mask.sum()
101
+ return loss
102
+
103
+ def forward(self, xyz, features=None):
104
+ xyz_masked, features_masked, mask, ids_restore, idx_keep = self.random_masking(xyz, features, self.mask_ratio)
105
+ latent = self.encoder(xyz_masked, features_masked)
106
+ if self.use_global_feat:
107
+ latent = self.maxpool(latent)
108
+ pred = self.decoder(xyz_masked, latent, ids_restore) # [N, L, p*p*3]
109
+
110
+ """visualize pred. TODO: add to Tensorboard
111
+ from openpoints.dataset import vis_multi_points
112
+ vis_multi_points((xyz[0].cpu().detach().numpy(), xyz_masked[0].cpu().detach().numpy(), pred[-1][0].cpu().detach().numpy()))
113
+ """
114
+ loss = self.forward_loss(xyz, pred, mask)
115
+ return loss, pred
116
+
@@ -0,0 +1,168 @@
1
+ """ Maksed PointViT in PyTorch
2
+ Copyright 2022@PointNeXt team
3
+ """
4
+ import logging
5
+ import torch
6
+ import torch.nn as nn
7
+ from openpoints.cpp.chamfer_dist import ChamferDistanceL1
8
+ from ..build import build_model_from_cfg, MODELS
9
+ from ..layers.subsample import furthest_point_sample, random_sample
10
+ from ..layers.group import KNNGroup, QueryAndGroup
11
+
12
+
13
+ @MODELS.register_module()
14
+ class MaskedPointGroup(nn.Module):
15
+ """ Masked AutoEncoder for Point-based methods
16
+ """
17
+
18
+ def __init__(self,
19
+ encoder_args,
20
+ decoder_args,
21
+ mask_ratio,
22
+ subsample='fps', # random, FPS
23
+ group='knn',
24
+ group_size=32,
25
+ sample_ratio=0.25,
26
+ radius=0.1,
27
+ add_cls_token=False,
28
+ **kwargs
29
+ ):
30
+ super().__init__()
31
+ if kwargs:
32
+ logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
33
+
34
+ # ------------------------------------------------------------------
35
+ # Grouping
36
+ self.group_size = group_size
37
+ self.sample_ratio = sample_ratio # downsample 4x
38
+ if subsample.lower() == 'fps':
39
+ self.sample_fn = furthest_point_sample
40
+ elif 'random' in subsample.lower():
41
+ self.sample_fn = random_sample
42
+
43
+ self.group = group.lower()
44
+ if 'ball' in self.group or 'query' in self.group:
45
+ self.grouper = QueryAndGroup(nsample=self.group_size,
46
+ relative_p=False, normalize_p=False,
47
+ radius=radius)
48
+ elif 'knn' in self.group.lower():
49
+ self.grouper = KNNGroup(self.group_size, relative_p=False)
50
+ else:
51
+ raise NotImplementedError(f'{self.group.lower()} is not implemented. Only support ballquery, knn')
52
+
53
+ # ------------------------------------------------------------------
54
+ # MAE encoder. e.g. DGCNN, DeepGCN, PointNet++
55
+ self.encoder = build_model_from_cfg(encoder_args)
56
+ self.add_cls_token = add_cls_token
57
+ if self.add_cls_token:
58
+ self.cls_token = nn.Parameter(torch.randn(1, 1, decoder_args.embed_dim))
59
+ torch.nn.init.normal_(self.cls_token, std=.02)
60
+ # ------------------------------------------------------------------
61
+ # MAE decoder. e.g. FoldingNet (works bad in random sampling), PU-GCN (NodeShuffle)
62
+ self.use_global_feat = True if decoder_args.NAME.lower() in ['foldingnet', 'pointcompletion'] else False
63
+ self.maxpool = lambda x: torch.max(x, dim=-1, keepdim=False)[0]
64
+ self.decoder = build_model_from_cfg(decoder_args)
65
+
66
+ # ------------------------------------------------------------------
67
+ # loss
68
+ self.mask_ratio = mask_ratio
69
+ self.build_loss_func()
70
+
71
+ @staticmethod
72
+ def group_random_masking(x, f=None, mask_ratio=0.9):
73
+ """
74
+ Perform per-sample random masking by per-sample shuffling.
75
+ Per-sample shuffling is done by argsort random noise.
76
+ x: [N, L, 3], sequence
77
+ f: [N, D, L], sequence
78
+ """
79
+ B, _, N, K = x.shape # batch, dim, num_points, num_neighbors
80
+ len_keep = int(N * (1 - mask_ratio))
81
+
82
+ noise = torch.rand(B, N, device=x.device) # noise in [0, 1]
83
+
84
+ # sort noise for each sample
85
+ ids_shuffle = torch.argsort(noise, dim=1) # ascend: small is keep, large is remove
86
+ ids_restore = torch.argsort(ids_shuffle, dim=1)
87
+
88
+ # keep the first subset
89
+ ids_keep = ids_shuffle[:, :len_keep]
90
+ x_masked = torch.gather(x, dim=2, index=ids_keep.unsqueeze(1).unsqueeze(-1).expand(-1, 3, -1, K))
91
+
92
+ # generate the binary mask: 0 is keep, 1 is remove
93
+ mask = torch.ones([B, N], device=x.device)
94
+ mask[:, :len_keep] = 0
95
+ # unshuffle to get the binary mask
96
+ mask = torch.gather(mask, dim=1, index=ids_restore)
97
+
98
+ # f
99
+ if f is not None:
100
+ f_masked = torch.gather(f, dim=2,
101
+ index=ids_keep.unsqueeze(1).unsqueeze(-1).expand(-1, f.shape[1], -1,
102
+ f.shape[-1]))
103
+ else:
104
+ f_masked = None
105
+ return x_masked, f_masked, mask, ids_restore, ids_keep
106
+
107
+ def build_loss_func(self, smoothing=False):
108
+ self.criterion = ChamferDistanceL1()
109
+
110
+ def forward_loss(self, dp, pred, mask=None):
111
+ """
112
+ # chamfer distance. two options
113
+ 1. chamfer distance on the merged point clouds.
114
+ 2. chamfer distance per local patch
115
+
116
+
117
+ p: the original points [B, N, 3]
118
+ dp: the points after grouping. [B, 3, L, K]
119
+ pred: [B, L, K*3]
120
+ mask: [B, L], 0 is keep, 1 is remove,
121
+ idx_keep: [B, L]
122
+ """
123
+ # option 2, per patch chamfer distance.
124
+ B, C, L, K = dp.shape
125
+ # reshape dp and pred as [BL, K, C]
126
+ dp = dp.permute(0, 2, 3, 1).reshape(-1, K, C)
127
+ pred = pred.reshape(-1, K, C)
128
+ loss = self.criterion(pred, dp)
129
+ return loss
130
+
131
+ def forward(self, p, f=None):
132
+ # downsample, N -> L, e.g. 1024 -> 256
133
+ if isinstance(p, dict):
134
+ p, f = p['pos'], p.get('x', None)
135
+ if f is None:
136
+ f = p.transpose(1, 2).contiguous()
137
+ B, N, _ = p.shape[:3]
138
+ idx = self.sample_fn(p, int(N * self.sample_ratio)).long()
139
+ center_p = torch.gather(p, 1, idx.unsqueeze(-1).expand(-1, -1, 3)) # e.g. [B, L, 3]
140
+
141
+ # query neighbors. dp: [B, 3, L, K], a typical K is 8
142
+ dp, gf = self.grouper(center_p, p, f)
143
+ dp_masked, gf_masked, mask, ids_restore, idx_keep = self.group_random_masking(
144
+ dp, gf, self.mask_ratio)
145
+
146
+ gf_masked = torch.cat((dp_masked, gf_masked), dim=1)
147
+ latent = self.encoder.ssl_forward(dp_masked, gf_masked) # latent: [B, C, MK]
148
+ if self.use_global_feat:
149
+ latent = self.maxpool(latent)
150
+ else:
151
+ latent = latent.transpose(1, 2)
152
+
153
+ # concat token
154
+ if self.add_cls_token:
155
+ latent = torch.cat((self.cls_token.expand(B, -1, -1), latent), dim=1)
156
+ pred = self.decoder(center_p, latent, ids_restore) # [N, L, p*p*3]
157
+
158
+ # pred is the reconsructed grouped p.
159
+ """visualize pred. TODO: add to Tensorboard
160
+ from openpoints.dataset import vis_multi_points
161
+ B, C, L, K = dp.shape
162
+ input_group_pts = dp.permute(0, 2, 3, 1).reshape(B, -1, 3).detach().cpu().numpy()
163
+ pred_group_pts = pred.clone().reshape(B, -1, 3).detach().cpu().numpy()
164
+ # vis_multi_points((p[0].cpu().numpy(), center_p[0].detach().cpu().numpy(), dp_masked[0].detach().cpu().numpy(), pred_group_pts[0]))
165
+ vis_multi_points((center_p[0].detach().cpu().numpy(), input_group_pts[0], dp_masked[0].detach().cpu().numpy(), pred_group_pts[0]))
166
+ """
167
+ loss = self.forward_loss(dp, pred, mask)
168
+ return loss, pred