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,286 @@
|
|
|
1
|
+
# Patch embedding for 2D/3D data
|
|
2
|
+
# Reference:
|
|
3
|
+
import math
|
|
4
|
+
import torch
|
|
5
|
+
from torch import nn as nn
|
|
6
|
+
import torch.nn.functional as F
|
|
7
|
+
from .subsample import furthest_point_sample, random_sample
|
|
8
|
+
from .group import KNNGroup, QueryAndGroup, create_grouper, get_aggregation_feautres
|
|
9
|
+
from .conv import create_convblock1d, create_convblock2d, create_linearblock, create_norm, create_act
|
|
10
|
+
from .local_aggregation import CHANNEL_MAP
|
|
11
|
+
from ..build import MODELS
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SubsampleGroup(nn.Module):
|
|
15
|
+
""" Point cloud to subsampled groups
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self,
|
|
19
|
+
num_groups=256, group_size=32,
|
|
20
|
+
subsample='fps', # random, FPS
|
|
21
|
+
group='ballquery',
|
|
22
|
+
radius=0.1,
|
|
23
|
+
**kwargs
|
|
24
|
+
):
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.num_groups = num_groups
|
|
27
|
+
self.group_size = group_size
|
|
28
|
+
|
|
29
|
+
self.subsample = subsample
|
|
30
|
+
self.group = group
|
|
31
|
+
|
|
32
|
+
if 'ball' in self.group.lower() or 'query' in self.group.lower():
|
|
33
|
+
self.grouper = QueryAndGroup(radius, self.group_size)
|
|
34
|
+
elif 'knn' in self.group.lower():
|
|
35
|
+
self.grouper = KNNGroup(self.group_size)
|
|
36
|
+
else:
|
|
37
|
+
raise NotImplementedError(f'{self.group.lower()} is not implemented. Only support ballquery, knn')
|
|
38
|
+
|
|
39
|
+
def forward(self, p, x=None):
|
|
40
|
+
if 'fps' in self.subsample.lower() or 'furthest' in self.subsample.lower() or 'farthest' in self.subsample.lower():
|
|
41
|
+
idx = furthest_point_sample(p, self.num_groups).to(torch.int64)
|
|
42
|
+
elif 'random' in self.subsample.lower() or 'rs' in self.subsample.lower():
|
|
43
|
+
idx = random_sample(p, self.num_groups)
|
|
44
|
+
else:
|
|
45
|
+
raise NotImplementedError(f'{self.subsample.lower()} is not implemented. Only support fps, random')
|
|
46
|
+
center_p = torch.gather(p, 1,
|
|
47
|
+
idx.unsqueeze(-1).expand(-1, -1, 3)) # downsampled point cloud, [B, npoint, 3]
|
|
48
|
+
if x is not None:
|
|
49
|
+
B, C, N = x.shape[:3]
|
|
50
|
+
center_x = torch.gather(x, 2, idx.unsqueeze(1).expand(-1, C, -1)).unsqueeze(-1)
|
|
51
|
+
grouped_p, fj = self.grouper(center_p, p, x)
|
|
52
|
+
return grouped_p, center_p, fj, center_x
|
|
53
|
+
else:
|
|
54
|
+
grouped_p, _ = self.grouper(center_p, p)
|
|
55
|
+
return grouped_p, center_p
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@MODELS.register_module()
|
|
59
|
+
class PointPatchEmbed(nn.Module):
|
|
60
|
+
""" Point cloud to Group Embedding using GCN
|
|
61
|
+
Patch Embedding for 3d data (point cloud)
|
|
62
|
+
A convolution based approach to patchifying a point cloud w/ embedding projection.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(self,
|
|
66
|
+
sample_ratio=0.0625, group_size=32,
|
|
67
|
+
in_channels=3,
|
|
68
|
+
layers=4,
|
|
69
|
+
embed_dim=256,
|
|
70
|
+
channels=None,
|
|
71
|
+
subsample='fps', # random, FPS
|
|
72
|
+
group='ballquery',
|
|
73
|
+
normalize_dp=False,
|
|
74
|
+
radius=0.1,
|
|
75
|
+
feature_type='dp_df',
|
|
76
|
+
relative_xyz=True,
|
|
77
|
+
norm_args={'norm': 'bn1d'},
|
|
78
|
+
act_args={'act': 'relu'},
|
|
79
|
+
conv_args={'order': 'conv-norm-act'},
|
|
80
|
+
reduction='max',
|
|
81
|
+
**kwargs
|
|
82
|
+
):
|
|
83
|
+
super().__init__()
|
|
84
|
+
self.sample_ratio = sample_ratio
|
|
85
|
+
self.group_size = group_size
|
|
86
|
+
|
|
87
|
+
self.feature_type = feature_type
|
|
88
|
+
# subsample layer and group layer
|
|
89
|
+
if subsample.lower() == 'fps':
|
|
90
|
+
self.sample_fn = furthest_point_sample
|
|
91
|
+
elif 'random' in subsample.lower():
|
|
92
|
+
self.sample_fn = random_sample
|
|
93
|
+
|
|
94
|
+
# TODO: make this embedding progressively
|
|
95
|
+
self.group = group.lower()
|
|
96
|
+
if 'ball' in self.group or 'query' in self.group:
|
|
97
|
+
self.grouper = QueryAndGroup(nsample=self.group_size,
|
|
98
|
+
relative_xyz=relative_xyz, normalize_dp=normalize_dp,
|
|
99
|
+
radius=radius)
|
|
100
|
+
elif 'knn' in self.group.lower():
|
|
101
|
+
self.grouper = KNNGroup(self.group_size, relative_xyz=relative_xyz, normalize_dp=normalize_dp)
|
|
102
|
+
else:
|
|
103
|
+
raise NotImplementedError(f'{self.group.lower()} is not implemented. Only support ballquery, knn')
|
|
104
|
+
|
|
105
|
+
# # convolutions
|
|
106
|
+
if channels is None:
|
|
107
|
+
channels = [CHANNEL_MAP[feature_type](in_channels)] + [embed_dim] * (layers // 2) + [embed_dim * 2] * (
|
|
108
|
+
layers // 2 - 1) + [embed_dim]
|
|
109
|
+
else:
|
|
110
|
+
channels = [CHANNEL_MAP[feature_type](in_channels)] + channels + [embed_dim]
|
|
111
|
+
layers = len(channels) -1
|
|
112
|
+
conv1 = []
|
|
113
|
+
for i in range(layers // 2):
|
|
114
|
+
conv1.append(create_convblock2d(channels[i], channels[i + 1],
|
|
115
|
+
norm_args=norm_args if i!=(layers//2-1) else None,
|
|
116
|
+
act_args=act_args if i!=(layers//2-1) else None,
|
|
117
|
+
**conv_args))
|
|
118
|
+
self.conv1 = nn.Sequential(*conv1)
|
|
119
|
+
|
|
120
|
+
channels[layers // 2] *= 2
|
|
121
|
+
conv2 = []
|
|
122
|
+
for i in range(layers // 2, layers):
|
|
123
|
+
conv2.append(create_convblock2d(channels[i], channels[i + 1],
|
|
124
|
+
norm_args=norm_args if i!=(layers-1) else None,
|
|
125
|
+
act_args=act_args if i!=(layers-1) else None,
|
|
126
|
+
**conv_args
|
|
127
|
+
))
|
|
128
|
+
self.conv2 = nn.Sequential(*conv2)
|
|
129
|
+
|
|
130
|
+
# reduction layer
|
|
131
|
+
if reduction in ['mean', 'avg', 'meanpool', 'avgpool']:
|
|
132
|
+
self.pool = lambda x: torch.mean(x, dim=-1, keepdim=True)
|
|
133
|
+
else:
|
|
134
|
+
self.pool = lambda x: torch.max(x, dim=-1, keepdim=True)[0]
|
|
135
|
+
self.out_channels = channels[-1]
|
|
136
|
+
self.channel_list = [in_channels, embed_dim]
|
|
137
|
+
|
|
138
|
+
def forward(self, p, x=None):
|
|
139
|
+
# downsample
|
|
140
|
+
B, N, _ = p.shape[:3]
|
|
141
|
+
idx = self.sample_fn(p, int(N * self.sample_ratio)).long()
|
|
142
|
+
center_p = torch.gather(p, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
|
|
143
|
+
|
|
144
|
+
# query neighbors.
|
|
145
|
+
dp, fj = self.grouper(center_p, p, x)
|
|
146
|
+
|
|
147
|
+
"""visualization
|
|
148
|
+
from openpoints.dataset.vis3d import vis_multi_points
|
|
149
|
+
new_p = (dp.permute(0, 2, 3, 1) + center_p.unsqueeze(2)).view(B, -1, 3)
|
|
150
|
+
vis_multi_points([p[0].cpu().numpy(), new_p[0].cpu().numpy(), center_p[0].cpu().numpy()])
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
# concat neighborhood x
|
|
154
|
+
# TODO: using a local aggregation layer
|
|
155
|
+
if self.feature_type == 'dp':
|
|
156
|
+
fj = dp
|
|
157
|
+
elif self.feature_type == 'dp_fj':
|
|
158
|
+
fj = torch.cat([dp, fj], dim=1)
|
|
159
|
+
elif self.feature_type == 'dp_df':
|
|
160
|
+
center_x = torch.gather(x, 2, idx.unsqueeze(1).expand(-1, x.shape[1], -1))
|
|
161
|
+
fj = torch.cat([dp, fj - center_x.unsqueeze(-1)], dim=1)
|
|
162
|
+
elif self.feature_type == 'df':
|
|
163
|
+
center_x = torch.gather(x, 2, idx.unsqueeze(1).expand(-1, x.shape[1], -1))
|
|
164
|
+
fj = fj - center_x.unsqueeze(-1)
|
|
165
|
+
fj = self.conv1(fj)
|
|
166
|
+
|
|
167
|
+
fj = torch.cat(
|
|
168
|
+
[self.pool(fj).expand(-1, -1, -1, self.group_size),
|
|
169
|
+
fj],
|
|
170
|
+
dim=1)
|
|
171
|
+
out_f = self.pool(self.conv2(fj)).squeeze(-1)
|
|
172
|
+
return [p, center_p], [x, out_f]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@MODELS.register_module()
|
|
176
|
+
class P3Embed(nn.Module):
|
|
177
|
+
"""
|
|
178
|
+
Progressive Point Patch Embedding for 3d data (point cloud)
|
|
179
|
+
A convolution based approach to patchifying a point cloud w/ embedding projection.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def __init__(self,
|
|
183
|
+
sample_ratio=0.0625,
|
|
184
|
+
scale=4,
|
|
185
|
+
group_size=32,
|
|
186
|
+
in_channels=3,
|
|
187
|
+
layers=4,
|
|
188
|
+
embed_dim=256,
|
|
189
|
+
subsample='fps', # random, FPS
|
|
190
|
+
group='ballquery',
|
|
191
|
+
normalize_dp=False,
|
|
192
|
+
radius=0.1,
|
|
193
|
+
feature_type='dp_df',
|
|
194
|
+
relative_xyz=True,
|
|
195
|
+
norm_args={'norm': 'bn1d'},
|
|
196
|
+
act_args={'act': 'relu'},
|
|
197
|
+
conv_args={'order': 'conv-norm-act'},
|
|
198
|
+
reduction='max',
|
|
199
|
+
**kwargs
|
|
200
|
+
):
|
|
201
|
+
super().__init__()
|
|
202
|
+
self.sample_ratio = sample_ratio
|
|
203
|
+
self.group_size = group_size
|
|
204
|
+
|
|
205
|
+
self.feature_type = feature_type
|
|
206
|
+
# subsample layer and group layer
|
|
207
|
+
if subsample.lower() == 'fps':
|
|
208
|
+
self.sample_fn = furthest_point_sample
|
|
209
|
+
elif 'random' in subsample.lower():
|
|
210
|
+
self.sample_fn = random_sample
|
|
211
|
+
|
|
212
|
+
self.group = group.lower()
|
|
213
|
+
if 'ball' in self.group or 'query' in self.group:
|
|
214
|
+
self.grouper = QueryAndGroup(nsample=self.group_size,
|
|
215
|
+
relative_xyz=relative_xyz, normalize_dp=normalize_dp,
|
|
216
|
+
radius=radius)
|
|
217
|
+
elif 'knn' in self.group.lower():
|
|
218
|
+
self.grouper = KNNGroup(self.group_size, relative_xyz=relative_xyz, normalize_dp=normalize_dp)
|
|
219
|
+
else:
|
|
220
|
+
raise NotImplementedError(f'{self.group.lower()} is not implemented. Only support ballquery, knn')
|
|
221
|
+
|
|
222
|
+
# stages
|
|
223
|
+
stages = int(math.log(1/sample_ratio, scale))
|
|
224
|
+
embed_dim = int(embed_dim // 2 ** (stages-1))
|
|
225
|
+
self.convs = nn.ModuleList()
|
|
226
|
+
self.channel_list = [in_channels]
|
|
227
|
+
for _ in range(int(stages)):
|
|
228
|
+
# convolutions
|
|
229
|
+
channels = [CHANNEL_MAP[feature_type](in_channels)] + [embed_dim] * (layers // 2) + [embed_dim * 2] * (
|
|
230
|
+
layers // 2 - 1) + [embed_dim]
|
|
231
|
+
conv1 = []
|
|
232
|
+
for i in range(layers // 2):
|
|
233
|
+
conv1.append(create_convblock2d(channels[i], channels[i + 1],
|
|
234
|
+
norm_args=norm_args if i!=(layers//2-1) else None,
|
|
235
|
+
act_args=act_args if i!=(layers//2-1) else None,
|
|
236
|
+
**conv_args))
|
|
237
|
+
conv1 = nn.Sequential(*conv1)
|
|
238
|
+
|
|
239
|
+
channels[layers // 2] *= 2
|
|
240
|
+
conv2 = []
|
|
241
|
+
for i in range(layers // 2, layers):
|
|
242
|
+
conv2.append(create_convblock2d(channels[i], channels[i + 1],
|
|
243
|
+
norm_args=norm_args,
|
|
244
|
+
act_args=act_args,
|
|
245
|
+
**conv_args
|
|
246
|
+
))
|
|
247
|
+
conv2 = nn.Sequential(*conv2)
|
|
248
|
+
self.convs.append(nn.ModuleList([conv1, conv2]))
|
|
249
|
+
|
|
250
|
+
self.channel_list.append(embed_dim)
|
|
251
|
+
in_channels = embed_dim
|
|
252
|
+
embed_dim *= 2
|
|
253
|
+
|
|
254
|
+
# reduction layer
|
|
255
|
+
if reduction in ['mean', 'avg', 'meanpool', 'avgpool']:
|
|
256
|
+
self.pool = lambda x: torch.mean(x, dim=-1, keepdim=True)
|
|
257
|
+
else:
|
|
258
|
+
self.pool = lambda x: torch.max(x, dim=-1, keepdim=True)[0]
|
|
259
|
+
self.out_channels = channels[-1]
|
|
260
|
+
|
|
261
|
+
def forward(self, p, f=None):
|
|
262
|
+
B, N, _ = p.shape[:3]
|
|
263
|
+
out_p, out_f = [p], [f]
|
|
264
|
+
for convs in self.convs:
|
|
265
|
+
# Progressive downsampling
|
|
266
|
+
cur_p, cur_f = out_p[-1], out_f[-1]
|
|
267
|
+
idx = self.sample_fn(cur_p, int(N //4)).long()
|
|
268
|
+
N = N // 4
|
|
269
|
+
center_p = torch.gather(cur_p, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
|
|
270
|
+
center_f = torch.gather(cur_f, 2, idx.unsqueeze(1).expand(-1, cur_f.shape[1], -1))
|
|
271
|
+
|
|
272
|
+
# query neighbors.
|
|
273
|
+
dp, fj = self.grouper(center_p, cur_p, cur_f)
|
|
274
|
+
fj = get_aggregation_feautres(center_p, dp, center_f, fj, self.feature_type)
|
|
275
|
+
|
|
276
|
+
# graph convolutions
|
|
277
|
+
fj = convs[0](fj)
|
|
278
|
+
fj = torch.cat(
|
|
279
|
+
[self.pool(fj).expand(-1, -1, -1, self.group_size),
|
|
280
|
+
fj],
|
|
281
|
+
dim=1)
|
|
282
|
+
|
|
283
|
+
# output
|
|
284
|
+
out_f.append(self.pool(convs[1](fj)).squeeze(-1))
|
|
285
|
+
out_p.append(center_p)
|
|
286
|
+
return out_p, out_f
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
""" Layer/Module Helpers
|
|
2
|
+
|
|
3
|
+
Hacked together by / Copyright 2020 Ross Wightman
|
|
4
|
+
"""
|
|
5
|
+
from itertools import repeat
|
|
6
|
+
import collections.abc
|
|
7
|
+
from torch import nn
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# From PyTorch internals
|
|
11
|
+
def _ntuple(n):
|
|
12
|
+
def parse(x):
|
|
13
|
+
if isinstance(x, collections.abc.Iterable):
|
|
14
|
+
return x
|
|
15
|
+
return tuple(repeat(x, n))
|
|
16
|
+
return parse
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
to_1tuple = _ntuple(1)
|
|
20
|
+
to_2tuple = _ntuple(2)
|
|
21
|
+
to_3tuple = _ntuple(3)
|
|
22
|
+
to_4tuple = _ntuple(4)
|
|
23
|
+
to_ntuple = _ntuple
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def make_divisible(v, divisor=8, min_value=None, round_limit=.9):
|
|
27
|
+
min_value = min_value or divisor
|
|
28
|
+
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
|
|
29
|
+
# Make sure that round down does not go down by more than 10%.
|
|
30
|
+
if new_v < round_limit * v:
|
|
31
|
+
new_v += divisor
|
|
32
|
+
return new_v
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MultipleSequential(nn.Sequential):
|
|
37
|
+
def forward(self, *inputs):
|
|
38
|
+
for module in self._modules.values():
|
|
39
|
+
if type(inputs) == tuple:
|
|
40
|
+
inputs = module(*inputs)
|
|
41
|
+
else:
|
|
42
|
+
inputs = module(inputs)
|
|
43
|
+
return inputs
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
from fast_pytorch_kmeans import KMeans, MultiKMeans
|
|
4
|
+
from torch_scatter import scatter
|
|
5
|
+
from .local_aggregation import CHANNEL_MAP
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KMeansEmbed(nn.Module):
|
|
9
|
+
""" Point cloud to subsampled groups
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self,
|
|
13
|
+
in_chans=3,
|
|
14
|
+
num_groups=256,
|
|
15
|
+
encoder_dim=256,
|
|
16
|
+
feature_type='dp',
|
|
17
|
+
**kwargs
|
|
18
|
+
):
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
self.num_groups = num_groups
|
|
22
|
+
|
|
23
|
+
self.kmeans = MultiKMeans(n_clusters=num_groups, n_kmeans=32, mode='euclidean', verbose=0)
|
|
24
|
+
|
|
25
|
+
channels = CHANNEL_MAP[feature_type](in_chans)
|
|
26
|
+
self.feature_type = feature_type
|
|
27
|
+
self.conv1 = nn.Sequential(
|
|
28
|
+
nn.Linear(channels, 128), # TODO: here, can be better, edgeconv.
|
|
29
|
+
nn.LayerNorm(128),
|
|
30
|
+
nn.ReLU(inplace=True),
|
|
31
|
+
nn.Linear(128, 256)
|
|
32
|
+
)
|
|
33
|
+
self.conv2 = nn.Sequential(
|
|
34
|
+
nn.Linear(512, 512),
|
|
35
|
+
nn.LayerNorm(512),
|
|
36
|
+
nn.ReLU(inplace=True),
|
|
37
|
+
nn.Linear(512, encoder_dim)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def forward(self, xyz, features=None):
|
|
41
|
+
B, N, _ = xyz.shape
|
|
42
|
+
self.kmeans.centroids = None # re-init kmeans
|
|
43
|
+
self.kmeans.n_kmeans = xyz.shape[0]
|
|
44
|
+
labels = self.kmeans.fit_predict(xyz) # B,N
|
|
45
|
+
|
|
46
|
+
# TODO: BUG, sometimes the value is even smaller than the number of centroids!!
|
|
47
|
+
centroids = self.kmeans.centroids # B, K, 3
|
|
48
|
+
idx = labels.unsqueeze(-1)
|
|
49
|
+
|
|
50
|
+
# p_j = xyz
|
|
51
|
+
p_i = torch.gather(centroids, 1, labels.unsqueeze(-1).expand(-1, -1, 3)) # B, N, 3
|
|
52
|
+
relative_xyz = xyz - p_i # p_j-p_i, B, N, 3
|
|
53
|
+
|
|
54
|
+
if self.feature_type == 'dp':
|
|
55
|
+
neighborhood_features = relative_xyz
|
|
56
|
+
elif self.feature_type == 'pj_dp':
|
|
57
|
+
neighborhood_features = torch.cat([xyz, relative_xyz], -1)
|
|
58
|
+
elif self.feature_type == 'pi_dp':
|
|
59
|
+
neighborhood_features = torch.cat([p_i, relative_xyz], -1)
|
|
60
|
+
|
|
61
|
+
neighborhood_features = self.conv1(neighborhood_features) # B, N, C
|
|
62
|
+
pooled_feat = scatter(neighborhood_features, idx, dim=1, reduce='max') # B, K, C
|
|
63
|
+
reapted_feat = torch.gather(pooled_feat, 1, idx.expand(-1, -1, pooled_feat.shape[-1]))
|
|
64
|
+
neighborhood_features = torch.cat([reapted_feat, neighborhood_features], dim=-1)
|
|
65
|
+
out_features = scatter(self.conv2(neighborhood_features), idx, dim=1, reduce='max')
|
|
66
|
+
return centroids, out_features, p_i, labels
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
import torch
|
|
71
|
+
import os, sys
|
|
72
|
+
sys.path.insert(0, os.path.join(os.path.abspath(__file__), "../../../../"))
|
|
73
|
+
from openpoints.dataset import ModelNet, vis_points, vis_multi_points
|
|
74
|
+
from openpoints.models.layers import fps
|
|
75
|
+
|
|
76
|
+
B, C, N = 8, 3, 8196
|
|
77
|
+
device = 'cuda'
|
|
78
|
+
|
|
79
|
+
dataset = ModelNet("data/ModelNet40",
|
|
80
|
+
N, 40, split='test')
|
|
81
|
+
test_datalodaer = torch.utils.data.DataLoader(dataset, batch_size=B, num_workers=1
|
|
82
|
+
)
|
|
83
|
+
data = iter(test_datalodaer).next()[0]
|
|
84
|
+
points = data['pos'].to(device)
|
|
85
|
+
|
|
86
|
+
points = fps(points, N)
|
|
87
|
+
print(points.shape)
|
|
88
|
+
# debug one batch
|
|
89
|
+
K = 12
|
|
90
|
+
print(points.shape, points.device)
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# kmeans_group = KMeansGroup(K).to(device)
|
|
94
|
+
# kmeans_group(points)
|
|
95
|
+
|
|
96
|
+
kmeans = KMeans(n_clusters=K, mode='euclidean', verbose=1)
|
|
97
|
+
labels = kmeans.fit_predict(points[0])
|
|
98
|
+
print(labels.shape)
|
|
99
|
+
# 0.5207s for 10000 points, too slow!
|
|
100
|
+
# vis_points(points[0], labels=labels)
|
|
101
|
+
center_points = kmeans.centroids
|
|
102
|
+
print(center_points.shape)
|
|
103
|
+
# vis_multi_points([points.cpu().numpy(), center_points])
|
|
104
|
+
vis_points(points[0], labels=labels)
|
|
105
|
+
|
|
106
|
+
# B, N, 3
|
|
107
|
+
# B, N, 1 (label index)
|
|
108
|
+
|
|
109
|
+
# debug 8 batch
|
|
110
|
+
# K = 8
|
|
111
|
+
# print(points.shape, points.device)
|
|
112
|
+
# kmeans = MultiKMeans(n_clusters=K, n_kmeans=B,
|
|
113
|
+
# mode='euclidean', verbose=1)
|
|
114
|
+
# labels = kmeans.fit_predict(points)
|
|
115
|
+
# print(labels.shape)
|
|
116
|
+
# # 0.5207s for 10000 points, too slow!
|
|
117
|
+
# # vis_multi_points(points.cpu().numpy()[:4], labels=labels.cpu().numpy()[:4])
|
|
118
|
+
# vis_points(points[0], labels=labels[0])
|
|
119
|
+
# vis_points(points[1], labels=labels[1])
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
# from knn_cuda import KNN as KNNCUDA
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@torch.no_grad()
|
|
7
|
+
def knn_point(k, query, support=None):
|
|
8
|
+
"""Get the distances and indices to a fixed number of neighbors
|
|
9
|
+
Args:
|
|
10
|
+
support ([tensor]): [B, N, C]
|
|
11
|
+
query ([tensor]): [B, M, C]
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
[int]: neighbor idx. [B, M, K]
|
|
15
|
+
"""
|
|
16
|
+
if support is None:
|
|
17
|
+
support = query
|
|
18
|
+
dist = torch.cdist(query, support)
|
|
19
|
+
k_dist = dist.topk(k=k, dim=-1, largest=False, sorted=True)
|
|
20
|
+
return k_dist.values, k_dist.indices
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class KNN(nn.Module):
|
|
24
|
+
"""Get the distances and indices to a fixed number of neighbors
|
|
25
|
+
|
|
26
|
+
Reference: https://gist.github.com/ModarTensai/60fe0d0e3536adc28778448419908f47
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
neighbors: number of neighbors to consider
|
|
30
|
+
p_norm: distances are computed based on L_p norm
|
|
31
|
+
farthest: whether to get the farthest or the nearest neighbors
|
|
32
|
+
ordered: distance sorted (descending if `farthest`)
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
(distances, indices) both of shape [B, N, `num_neighbors`]
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, neighbors,
|
|
39
|
+
farthest=False,
|
|
40
|
+
sorted=True,
|
|
41
|
+
**kwargs):
|
|
42
|
+
super(KNN, self).__init__()
|
|
43
|
+
self.neighbors = neighbors
|
|
44
|
+
self.farthest = farthest
|
|
45
|
+
self.sorted = sorted
|
|
46
|
+
|
|
47
|
+
@torch.no_grad()
|
|
48
|
+
def forward(self, query, support=None):
|
|
49
|
+
"""
|
|
50
|
+
Args:
|
|
51
|
+
support ([tensor]): [B, N, C]
|
|
52
|
+
query ([tensor]): [B, M, C]
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
[int]: neighbor idx. [B, M, K]
|
|
56
|
+
"""
|
|
57
|
+
if support is None:
|
|
58
|
+
support = query
|
|
59
|
+
dist = torch.cdist(query, support)
|
|
60
|
+
k_dist = dist.topk(k=self.neighbors, dim=-1, largest=self.farthest, sorted=self.sorted)
|
|
61
|
+
return k_dist.values, k_dist.indices.int()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# dilated knn
|
|
65
|
+
class DenseDilated(nn.Module):
|
|
66
|
+
"""
|
|
67
|
+
Find dilated neighbor from neighbor list
|
|
68
|
+
index: (B, npoint, nsample)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0):
|
|
72
|
+
super(DenseDilated, self).__init__()
|
|
73
|
+
self.dilation = dilation
|
|
74
|
+
self.stochastic = stochastic
|
|
75
|
+
self.epsilon = epsilon
|
|
76
|
+
self.k = k
|
|
77
|
+
|
|
78
|
+
def forward(self, edge_index):
|
|
79
|
+
if self.stochastic:
|
|
80
|
+
if torch.rand(1) < self.epsilon and self.training:
|
|
81
|
+
num = self.k * self.dilation
|
|
82
|
+
randnum = torch.randperm(num)[:self.k]
|
|
83
|
+
edge_index = edge_index[:, :, randnum]
|
|
84
|
+
else:
|
|
85
|
+
edge_index = edge_index[:, :, ::self.dilation]
|
|
86
|
+
else:
|
|
87
|
+
edge_index = edge_index[:, :, ::self.dilation]
|
|
88
|
+
return edge_index.contiguous()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class DilatedKNN(nn.Module):
|
|
92
|
+
"""
|
|
93
|
+
Find the neighbors' indices based on dilated knn
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0):
|
|
97
|
+
super(DilatedKNN, self).__init__()
|
|
98
|
+
self.dilation = dilation
|
|
99
|
+
self.stochastic = stochastic
|
|
100
|
+
self.epsilon = epsilon
|
|
101
|
+
self.k = k
|
|
102
|
+
self._dilated = DenseDilated(k, dilation, stochastic, epsilon)
|
|
103
|
+
# self.knn = KNNCUDA(k * self.dilation, transpose_mode=True)
|
|
104
|
+
self.knn = KNN(k * self.dilation, transpose_mode=True)
|
|
105
|
+
|
|
106
|
+
def forward(self, query):
|
|
107
|
+
_, idx = self.knn(query, query)
|
|
108
|
+
return self._dilated(idx)
|
|
109
|
+
|
|
110
|
+
|