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,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]
|