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.
- openpoints/__init__.py +3 -0
- openpoints/cpp/__init__.py +6 -0
- openpoints/cpp/chamfer_dist/__init__.py +110 -0
- openpoints/cpp/chamfer_dist/setup.py +19 -0
- openpoints/cpp/chamfer_dist/test.py +34 -0
- openpoints/cpp/emd/__init__.py +3 -0
- openpoints/cpp/emd/emd.py +88 -0
- openpoints/cpp/emd/setup.py +27 -0
- openpoints/cpp/emd/test_emd_loss.py +45 -0
- openpoints/cpp/pointnet2_batch/__init__.py +24 -0
- openpoints/cpp/pointnet2_batch/setup.py +23 -0
- openpoints/cpp/pointops/__init__.py +0 -0
- openpoints/cpp/pointops/functions/__init__.py +0 -0
- openpoints/cpp/pointops/functions/pointops.py +314 -0
- openpoints/cpp/pointops/setup.py +37 -0
- openpoints/cpp/pointops/src/__init__.py +0 -0
- openpoints/dataset/__init__.py +10 -0
- openpoints/dataset/atom3d/__init__.py +1 -0
- openpoints/dataset/atom3d/psr.py +38 -0
- openpoints/dataset/build.py +98 -0
- openpoints/dataset/data_util.py +192 -0
- openpoints/dataset/datalist.py +67 -0
- openpoints/dataset/dataset_base.py +96 -0
- openpoints/dataset/graph_dataset/__init__.py +3 -0
- openpoints/dataset/graph_dataset/graph_dataset.py +93 -0
- openpoints/dataset/graph_dataset/stack_with_pad.py +91 -0
- openpoints/dataset/graph_dataset/structural_dataset.py +73 -0
- openpoints/dataset/graph_dataset/svd_encodings_dataset.py +110 -0
- openpoints/dataset/grid_sample.py +21 -0
- openpoints/dataset/matterport3d/__init__.py +1 -0
- openpoints/dataset/matterport3d/category_mapping.tsv +1660 -0
- openpoints/dataset/matterport3d/matterport3d.py +210 -0
- openpoints/dataset/matterport3d/matterport3d_dataprocessing.py +105 -0
- openpoints/dataset/modelnet/__init__.py +3 -0
- openpoints/dataset/modelnet/modelnet40_normal_resampled_loader.py +124 -0
- openpoints/dataset/modelnet/modelnet40_ply_2048_loader.py +160 -0
- openpoints/dataset/molhiv/__init__.py +1 -0
- openpoints/dataset/molhiv/data.py +59 -0
- openpoints/dataset/molpcba/__init__.py +1 -0
- openpoints/dataset/molpcba/data.py +59 -0
- openpoints/dataset/parsers/__init__.py +1 -0
- openpoints/dataset/parsers/class_map.py +19 -0
- openpoints/dataset/parsers/constants.py +1 -0
- openpoints/dataset/parsers/parser.py +17 -0
- openpoints/dataset/parsers/parser_factory.py +29 -0
- openpoints/dataset/parsers/parser_image_folder.py +69 -0
- openpoints/dataset/parsers/parser_image_in_tar.py +222 -0
- openpoints/dataset/parsers/parser_image_tar.py +72 -0
- openpoints/dataset/parsers/parser_tfds.py +297 -0
- openpoints/dataset/pcqm4m/__init__.py +1 -0
- openpoints/dataset/pcqm4m/data.py +62 -0
- openpoints/dataset/pcqm4mv2/__init__.py +1 -0
- openpoints/dataset/pcqm4mv2/data.py +87 -0
- openpoints/dataset/s3dis/__init__.py +2 -0
- openpoints/dataset/s3dis/s3dis.py +156 -0
- openpoints/dataset/s3dis/s3dis_block.py +96 -0
- openpoints/dataset/s3dis/s3dis_sphere.py +349 -0
- openpoints/dataset/scannetv2/__init__.py +1 -0
- openpoints/dataset/scannetv2/scannet.py +176 -0
- openpoints/dataset/scanobjectnn/__init__.py +3 -0
- openpoints/dataset/scanobjectnn/scanobjectnn.py +110 -0
- openpoints/dataset/semantic_kitti/__init__.py +1 -0
- openpoints/dataset/semantic_kitti/helper_tool.py +286 -0
- openpoints/dataset/semantic_kitti/label_mapping.yaml +211 -0
- openpoints/dataset/semantic_kitti/semantickitti.py +229 -0
- openpoints/dataset/semantic_kitti/utils/meta/anno_paths.txt +272 -0
- openpoints/dataset/semantic_kitti/utils/meta/class_names.txt +13 -0
- openpoints/dataset/semantic_kitti/utils/semantic-kitti.yaml +211 -0
- openpoints/dataset/shapenet/__init__.py +1 -0
- openpoints/dataset/shapenet/shapenet55.py +76 -0
- openpoints/dataset/shapenet/shapenetpart.py +121 -0
- openpoints/dataset/shapenetpart/__init__.py +1 -0
- openpoints/dataset/shapenetpart/shapenet55.py +75 -0
- openpoints/dataset/shapenetpart/shapenetpart.py +388 -0
- openpoints/dataset/vis2d.py +17 -0
- openpoints/dataset/vis3d.py +153 -0
- openpoints/loss/__init__.py +3 -0
- openpoints/loss/build.py +281 -0
- openpoints/loss/cross_entropy.py +38 -0
- openpoints/loss/distill_loss.py +76 -0
- openpoints/models/__init__.py +10 -0
- openpoints/models/backbone/Stratified_transformer.py +558 -0
- openpoints/models/backbone/__init__.py +11 -0
- openpoints/models/backbone/baafnet.py +527 -0
- openpoints/models/backbone/ball_dgcnn.py +123 -0
- openpoints/models/backbone/curvenet.py +793 -0
- openpoints/models/backbone/debug_invvit.py +114 -0
- openpoints/models/backbone/deepgcn.py +143 -0
- openpoints/models/backbone/dgcnn.py +119 -0
- openpoints/models/backbone/graphvit3d.py +134 -0
- openpoints/models/backbone/grouppointnet.py +100 -0
- openpoints/models/backbone/pct.py +163 -0
- openpoints/models/backbone/pointmlp.py +417 -0
- openpoints/models/backbone/pointnet.py +199 -0
- openpoints/models/backbone/pointnetv2.py +511 -0
- openpoints/models/backbone/pointnext.py +663 -0
- openpoints/models/backbone/pointnextPyG.py +555 -0
- openpoints/models/backbone/pointtransformer.py +293 -0
- openpoints/models/backbone/pointvector.py +853 -0
- openpoints/models/backbone/pointvit.py +392 -0
- openpoints/models/backbone/pointvit_inv.py +942 -0
- openpoints/models/backbone/pointvit_inv_old.py +784 -0
- openpoints/models/backbone/randlenet.py +318 -0
- openpoints/models/backbone/resnet.py +342 -0
- openpoints/models/backbone/simpleview.py +153 -0
- openpoints/models/backbone/simpleview_util.py +292 -0
- openpoints/models/build.py +13 -0
- openpoints/models/classification/__init__.py +5 -0
- openpoints/models/classification/cls_base.py +136 -0
- openpoints/models/classification/point_bert.py +154 -0
- openpoints/models/layers/__init__.py +14 -0
- openpoints/models/layers/activation.py +57 -0
- openpoints/models/layers/attention.py +103 -0
- openpoints/models/layers/conv.py +167 -0
- openpoints/models/layers/drop.py +164 -0
- openpoints/models/layers/graph_conv.py +122 -0
- openpoints/models/layers/group.py +415 -0
- openpoints/models/layers/group_embed.py +286 -0
- openpoints/models/layers/helpers.py +43 -0
- openpoints/models/layers/kmeans.py +119 -0
- openpoints/models/layers/knn.py +110 -0
- openpoints/models/layers/local_aggregation.py +286 -0
- openpoints/models/layers/mlp.py +129 -0
- openpoints/models/layers/norm.py +106 -0
- openpoints/models/layers/padding.py +56 -0
- openpoints/models/layers/patch_embed.py +37 -0
- openpoints/models/layers/registry.py +168 -0
- openpoints/models/layers/subsample.py +185 -0
- openpoints/models/layers/upsampling.py +106 -0
- openpoints/models/layers/weight_init.py +89 -0
- openpoints/models/reconstruction/__init__.py +8 -0
- openpoints/models/reconstruction/base_recontruct.py +216 -0
- openpoints/models/reconstruction/maskedpoint.py +116 -0
- openpoints/models/reconstruction/maskedpointgroup.py +168 -0
- openpoints/models/reconstruction/maskedpointvit.py +253 -0
- openpoints/models/reconstruction/nodeshuffle.py +11 -0
- openpoints/models/registry.py +149 -0
- openpoints/models/segmentation/__init__.py +6 -0
- openpoints/models/segmentation/base_seg.py +278 -0
- openpoints/models/segmentation/vit_seg.py +126 -0
- openpoints/optim/__init__.py +15 -0
- openpoints/optim/adabelief.py +201 -0
- openpoints/optim/adafactor.py +167 -0
- openpoints/optim/adahessian.py +156 -0
- openpoints/optim/adamp.py +105 -0
- openpoints/optim/adamw.py +122 -0
- openpoints/optim/lamb.py +192 -0
- openpoints/optim/lars.py +135 -0
- openpoints/optim/lookahead.py +61 -0
- openpoints/optim/madgrad.py +184 -0
- openpoints/optim/nadam.py +92 -0
- openpoints/optim/nvnovograd.py +120 -0
- openpoints/optim/optim_factory.py +306 -0
- openpoints/optim/radam.py +89 -0
- openpoints/optim/rmsprop_tf.py +139 -0
- openpoints/optim/sgdp.py +70 -0
- openpoints/scheduler/__init__.py +8 -0
- openpoints/scheduler/cosine_lr.py +124 -0
- openpoints/scheduler/multistep_lr.py +65 -0
- openpoints/scheduler/plateau_lr.py +113 -0
- openpoints/scheduler/poly_lr.py +116 -0
- openpoints/scheduler/scheduler.py +110 -0
- openpoints/scheduler/scheduler_factory.py +118 -0
- openpoints/scheduler/step_lr.py +65 -0
- openpoints/scheduler/tanh_lr.py +117 -0
- openpoints/transforms/__init__.py +7 -0
- openpoints/transforms/point_transform_cpu.py +332 -0
- openpoints/transforms/point_transformer_gpu.py +764 -0
- openpoints/transforms/transforms_factory.py +60 -0
- openpoints/utils/__init__.py +8 -0
- openpoints/utils/ckpt_util.py +438 -0
- openpoints/utils/config.py +113 -0
- openpoints/utils/dist_utils.py +54 -0
- openpoints/utils/logger.py +170 -0
- openpoints/utils/metrics.py +311 -0
- openpoints/utils/random.py +16 -0
- openpoints/utils/registry.py +294 -0
- openpoints/utils/str2bool.py +11 -0
- openpoints/utils/wandb.py +88 -0
- openpoints-0.1.0.dist-info/METADATA +150 -0
- openpoints-0.1.0.dist-info/RECORD +184 -0
- openpoints-0.1.0.dist-info/WHEEL +5 -0
- openpoints-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|