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,122 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
from .group import grouping_operation
|
|
4
|
+
from .knn import DilatedKNN
|
|
5
|
+
from openpoints.models.layers.conv import create_convblock2d
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def gather_features(features, indices, sparse_grad=True):
|
|
9
|
+
"""Gather the features specified by indices
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
features: tensor of shape [B, C, N, 1]
|
|
13
|
+
indices: long tensor of shape [B, N, K]
|
|
14
|
+
sparse_grad: whether to use a sparse tensor for the gradient
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
gathered_features [B, C, N, K]
|
|
18
|
+
"""
|
|
19
|
+
indices = indices.unsqueeze(1).long()
|
|
20
|
+
features, indices = torch.broadcast_tensors(features, indices)
|
|
21
|
+
return features.gather(dim=-2, index=indices, sparse_grad=sparse_grad)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MRConv(nn.Module):
|
|
25
|
+
"""
|
|
26
|
+
Max-Relative Graph Convolution (Paper: https://arxiv.org/abs/1904.03751) for dense data type
|
|
27
|
+
"""
|
|
28
|
+
def __init__(self, in_channels, out_channels, **kwargs):
|
|
29
|
+
super(MRConv, self).__init__()
|
|
30
|
+
self.nn = create_convblock2d(in_channels*2, out_channels, **kwargs)
|
|
31
|
+
|
|
32
|
+
def forward(self, x, edge_index):
|
|
33
|
+
x_j = grouping_operation(x.squeeze(-1), edge_index)
|
|
34
|
+
x_j, _ = torch.max(x_j - x.unsequence(-1), -1, keepdim=True)
|
|
35
|
+
return self.nn(torch.cat([x, x_j], dim=1))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class EdgeConv(nn.Module):
|
|
39
|
+
"""
|
|
40
|
+
Edge convolution layer (with activation, batch normalization) for dense data type
|
|
41
|
+
"""
|
|
42
|
+
def __init__(self, in_channels, out_channels, **kwargs):
|
|
43
|
+
super(EdgeConv, self).__init__()
|
|
44
|
+
self.nn = create_convblock2d(in_channels * 2, out_channels, **kwargs)
|
|
45
|
+
|
|
46
|
+
def forward(self, x, edge_index):
|
|
47
|
+
# x_j = gather_features(x,edge_index)
|
|
48
|
+
x_j = grouping_operation(x.squeeze(-1), edge_index.int())
|
|
49
|
+
max_value, _ = torch.max(self.nn(torch.cat([x.expand(-1, -1, -1, edge_index.shape[-1]), x_j - x],
|
|
50
|
+
dim=1)), -1, keepdim=True)
|
|
51
|
+
return max_value
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_GCN_LAYER_DEFAULT = dict(
|
|
55
|
+
mrconv=MRConv,
|
|
56
|
+
edgeconv=EdgeConv,
|
|
57
|
+
edge=EdgeConv
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class GraphConv(nn.Module):
|
|
62
|
+
"""
|
|
63
|
+
Static graph convolution layer
|
|
64
|
+
"""
|
|
65
|
+
def __init__(self, in_channels, out_channels, conv=EdgeConv, **kwargs):
|
|
66
|
+
super(GraphConv, self).__init__()
|
|
67
|
+
if isinstance(conv, str):
|
|
68
|
+
conv = _GCN_LAYER_DEFAULT[conv]
|
|
69
|
+
self.gconv = conv(in_channels, out_channels, **kwargs)
|
|
70
|
+
|
|
71
|
+
def forward(self, x, edge_index):
|
|
72
|
+
return self.gconv(x, edge_index)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class DynConv(GraphConv):
|
|
76
|
+
"""
|
|
77
|
+
Dynamic graph convolution layer
|
|
78
|
+
"""
|
|
79
|
+
def __init__(self, in_channels, out_channels, conv=EdgeConv,
|
|
80
|
+
k=9, dilation=1, stochastic=False, epsilon=0.0,
|
|
81
|
+
**kwargs):
|
|
82
|
+
super(DynConv, self).__init__(in_channels, out_channels, conv, **kwargs)
|
|
83
|
+
self.k = k
|
|
84
|
+
self.d = dilation
|
|
85
|
+
self.dilated_knn_graph = DilatedKNN(k, dilation, stochastic, epsilon)
|
|
86
|
+
|
|
87
|
+
def forward(self, x):
|
|
88
|
+
edge_index = self.dilated_knn_graph(x.squeeze(-1).transpose(1, 2))
|
|
89
|
+
return super(DynConv, self).forward(x, edge_index)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ResDynBlock(nn.Module):
|
|
93
|
+
"""
|
|
94
|
+
Residual Dynamic graph convolution block
|
|
95
|
+
"""
|
|
96
|
+
def __init__(self, in_channels, conv=EdgeConv,
|
|
97
|
+
k=9, dilation=1, stochastic=False, epsilon=0.0,
|
|
98
|
+
**kwargs):
|
|
99
|
+
super(ResDynBlock, self).__init__()
|
|
100
|
+
self.body = DynConv(in_channels, in_channels, conv,
|
|
101
|
+
k, dilation, stochastic, epsilon, **kwargs)
|
|
102
|
+
|
|
103
|
+
def forward(self, x):
|
|
104
|
+
return self.body(x) + x
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class DenseDynBlock(nn.Module):
|
|
108
|
+
"""
|
|
109
|
+
Dense Dynamic graph convolution block
|
|
110
|
+
"""
|
|
111
|
+
def __init__(self, in_channels, out_channels,conv=EdgeConv,
|
|
112
|
+
k=9, dilation=1, stochastic=False, epsilon=0.0,
|
|
113
|
+
**kwargs):
|
|
114
|
+
super(DenseDynBlock, self).__init__()
|
|
115
|
+
assert out_channels > in_channels, "#out channels should be larger than #in channels"
|
|
116
|
+
self.body = DynConv(in_channels, out_channels-in_channels, conv,
|
|
117
|
+
k, dilation, stochastic, epsilon, **kwargs)
|
|
118
|
+
|
|
119
|
+
def forward(self, x):
|
|
120
|
+
dense = self.body(x).squeeze(-1)
|
|
121
|
+
return torch.cat((x, dense), 1)
|
|
122
|
+
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# group layer: find neighbors for each point
|
|
2
|
+
# knn, knn_sparse, ball query
|
|
3
|
+
|
|
4
|
+
# gather layer, gather features by index
|
|
5
|
+
from typing import Tuple
|
|
6
|
+
import copy, logging
|
|
7
|
+
import torch
|
|
8
|
+
import torch.nn as nn
|
|
9
|
+
from torch.autograd import Function
|
|
10
|
+
from openpoints.cpp import pointnet2_cuda
|
|
11
|
+
|
|
12
|
+
class KNN(nn.Module):
|
|
13
|
+
def __init__(self, neighbors, transpose_mode=True):
|
|
14
|
+
super(KNN, self).__init__()
|
|
15
|
+
self.neighbors = neighbors
|
|
16
|
+
|
|
17
|
+
@torch.no_grad()
|
|
18
|
+
def forward(self, support, query):
|
|
19
|
+
"""
|
|
20
|
+
Args:
|
|
21
|
+
support ([tensor]): [B, N, C]
|
|
22
|
+
query ([tensor]): [B, M, C]
|
|
23
|
+
Returns:
|
|
24
|
+
[int]: neighbor idx. [B, M, K]
|
|
25
|
+
"""
|
|
26
|
+
dist = torch.cdist(support, query)
|
|
27
|
+
k_dist = dist.topk(k=self.neighbors, dim=1, largest=False)
|
|
28
|
+
return k_dist.values, k_dist.indices.transpose(1, 2).contiguous().int()
|
|
29
|
+
|
|
30
|
+
# dilated knn
|
|
31
|
+
class DenseDilated(nn.Module):
|
|
32
|
+
"""
|
|
33
|
+
Find dilated neighbor from neighbor list
|
|
34
|
+
index: (B, npoint, nsample)
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0):
|
|
38
|
+
super(DenseDilated, self).__init__()
|
|
39
|
+
self.dilation = dilation
|
|
40
|
+
self.stochastic = stochastic
|
|
41
|
+
self.epsilon = epsilon
|
|
42
|
+
self.k = k
|
|
43
|
+
|
|
44
|
+
def forward(self, edge_index):
|
|
45
|
+
if self.stochastic:
|
|
46
|
+
if torch.rand(1) < self.epsilon and self.training:
|
|
47
|
+
num = self.k * self.dilation
|
|
48
|
+
randnum = torch.randperm(num)[:self.k]
|
|
49
|
+
edge_index = edge_index[:, :, randnum]
|
|
50
|
+
else:
|
|
51
|
+
edge_index = edge_index[:, :, ::self.dilation]
|
|
52
|
+
else:
|
|
53
|
+
edge_index = edge_index[:, :, ::self.dilation]
|
|
54
|
+
return edge_index.contiguous()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DilatedKNN(nn.Module):
|
|
58
|
+
"""
|
|
59
|
+
Find the neighbors' indices based on dilated knn
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, k=9, dilation=1, stochastic=False, epsilon=0.0):
|
|
63
|
+
super(DilatedKNN, self).__init__()
|
|
64
|
+
self.dilation = dilation
|
|
65
|
+
self.stochastic = stochastic
|
|
66
|
+
self.epsilon = epsilon
|
|
67
|
+
self.k = k
|
|
68
|
+
self._dilated = DenseDilated(k, dilation, stochastic, epsilon)
|
|
69
|
+
self.knn = KNN(k * self.dilation, transpose_mode=True)
|
|
70
|
+
|
|
71
|
+
def forward(self, query):
|
|
72
|
+
_, idx = self.knn(query, query)
|
|
73
|
+
return self._dilated(idx)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class GroupingOperation(Function):
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
@torch.cuda.amp.custom_fwd(cast_inputs=torch.float32)
|
|
80
|
+
def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
|
|
81
|
+
"""
|
|
82
|
+
:param ctx:
|
|
83
|
+
:param features: (B, C, N) tensor of features to group
|
|
84
|
+
:param idx: (B, npoint, nsample) tensor containing the indicies of features to group with
|
|
85
|
+
:return:
|
|
86
|
+
output: (B, C, npoint, nsample) tensor
|
|
87
|
+
"""
|
|
88
|
+
assert features.is_contiguous()
|
|
89
|
+
assert idx.is_contiguous()
|
|
90
|
+
|
|
91
|
+
B, nfeatures, nsample = idx.size()
|
|
92
|
+
_, C, N = features.size()
|
|
93
|
+
output = torch.cuda.FloatTensor(B, C, nfeatures, nsample, device=features.device)
|
|
94
|
+
|
|
95
|
+
pointnet2_cuda.group_points_wrapper(B, C, N, nfeatures, nsample, features, idx, output)
|
|
96
|
+
|
|
97
|
+
ctx.for_backwards = (idx, N)
|
|
98
|
+
return output
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def backward(ctx, grad_out: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
102
|
+
"""
|
|
103
|
+
:param ctx:
|
|
104
|
+
:param grad_out: (B, C, npoint, nsample) tensor of the gradients of the output from forward
|
|
105
|
+
:return:
|
|
106
|
+
grad_features: (B, C, N) gradient of the features
|
|
107
|
+
"""
|
|
108
|
+
idx, N = ctx.for_backwards
|
|
109
|
+
|
|
110
|
+
B, C, npoint, nsample = grad_out.size()
|
|
111
|
+
grad_features = torch.zeros([B, C, N], dtype=torch.float, device=grad_out.device, requires_grad=True)
|
|
112
|
+
grad_out_data = grad_out.data.contiguous()
|
|
113
|
+
pointnet2_cuda.group_points_grad_wrapper(B, C, N, npoint, nsample, grad_out_data, idx, grad_features.data)
|
|
114
|
+
return grad_features, None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
grouping_operation = GroupingOperation.apply
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def torch_grouping_operation(features, idx):
|
|
121
|
+
r"""from torch points kernels
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
features : torch.Tensor
|
|
125
|
+
(B, C, N) tensor of features to group
|
|
126
|
+
idx : torch.Tensor
|
|
127
|
+
(B, npoint, nsample) tensor containing the indicies of features to group with
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
torch.Tensor
|
|
132
|
+
(B, C, npoint, nsample) tensor
|
|
133
|
+
"""
|
|
134
|
+
all_idx = idx.reshape(idx.shape[0], -1)
|
|
135
|
+
all_idx = all_idx.unsqueeze(1).repeat(1, features.shape[1], 1)
|
|
136
|
+
grouped_features = features.gather(2, all_idx)
|
|
137
|
+
return grouped_features.reshape(idx.shape[0], features.shape[1], idx.shape[1], idx.shape[2])
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class GatherOperation(Function):
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
|
|
144
|
+
"""
|
|
145
|
+
:param ctx:
|
|
146
|
+
:param features: (B, C, N)
|
|
147
|
+
:param idx: (B, npoint) index tensor of the features to gather
|
|
148
|
+
:return:
|
|
149
|
+
output: (B, C, npoint)
|
|
150
|
+
"""
|
|
151
|
+
assert features.is_contiguous()
|
|
152
|
+
assert idx.is_contiguous()
|
|
153
|
+
|
|
154
|
+
B, npoint = idx.size()
|
|
155
|
+
_, C, N = features.size()
|
|
156
|
+
output = torch.cuda.FloatTensor(B, C, npoint, device=features.device)
|
|
157
|
+
|
|
158
|
+
pointnet2_cuda.gather_points_wrapper(B, C, N, npoint, features, idx, output)
|
|
159
|
+
|
|
160
|
+
ctx.for_backwards = (idx, C, N)
|
|
161
|
+
return output
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def backward(ctx, grad_out):
|
|
165
|
+
idx, C, N = ctx.for_backwards
|
|
166
|
+
B, npoint = idx.size()
|
|
167
|
+
|
|
168
|
+
grad_features = torch.zeros([B, C, N], dtype=torch.float, device=grad_out.device, requires_grad=True)
|
|
169
|
+
grad_out_data = grad_out.data.contiguous()
|
|
170
|
+
pointnet2_cuda.gather_points_grad_wrapper(B, C, N, npoint, grad_out_data, idx, grad_features.data)
|
|
171
|
+
return grad_features, None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
gather_operation = GatherOperation.apply
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class BallQuery(Function):
|
|
178
|
+
@staticmethod
|
|
179
|
+
def forward(ctx, radius: float, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tensor) -> torch.Tensor:
|
|
180
|
+
"""
|
|
181
|
+
:param ctx:
|
|
182
|
+
:param radius: float, radius of the balls
|
|
183
|
+
:param nsample: int, maximum number of features in the balls
|
|
184
|
+
:param xyz: (B, N, 3) xyz coordinates of the features
|
|
185
|
+
:param new_xyz: (B, npoint, 3) centers of the ball query
|
|
186
|
+
:return:
|
|
187
|
+
idx: (B, npoint, nsample) tensor with the indicies of the features that form the query balls
|
|
188
|
+
"""
|
|
189
|
+
assert new_xyz.is_contiguous()
|
|
190
|
+
assert xyz.is_contiguous()
|
|
191
|
+
|
|
192
|
+
B, N, _ = xyz.size()
|
|
193
|
+
npoint = new_xyz.size(1)
|
|
194
|
+
idx = torch.cuda.IntTensor(B, npoint, nsample, device=xyz.device).zero_()
|
|
195
|
+
pointnet2_cuda.ball_query_wrapper(B, N, npoint, radius, nsample, new_xyz, xyz, idx)
|
|
196
|
+
return idx
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def backward(ctx, a=None):
|
|
200
|
+
return None, None, None, None
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
ball_query = BallQuery.apply
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class QueryAndGroup(nn.Module):
|
|
207
|
+
def __init__(self, radius: float, nsample: int,
|
|
208
|
+
relative_xyz=True,
|
|
209
|
+
normalize_dp=False,
|
|
210
|
+
normalize_by_std=False,
|
|
211
|
+
normalize_by_allstd=False,
|
|
212
|
+
normalize_by_allstd2=False,
|
|
213
|
+
return_only_idx=False,
|
|
214
|
+
**kwargs
|
|
215
|
+
):
|
|
216
|
+
"""[summary]
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
radius (float): radius of ball
|
|
220
|
+
nsample (int): maximum number of features to gather in the ball
|
|
221
|
+
use_xyz (bool, optional): concate xyz. Defaults to True.
|
|
222
|
+
ret_grouped_xyz (bool, optional): [description]. Defaults to False.
|
|
223
|
+
normalize_dp (bool, optional): [description]. Defaults to False.
|
|
224
|
+
"""
|
|
225
|
+
super().__init__()
|
|
226
|
+
self.radius, self.nsample = radius, nsample
|
|
227
|
+
self.normalize_dp = normalize_dp
|
|
228
|
+
self.normalize_by_std = normalize_by_std
|
|
229
|
+
self.normalize_by_allstd = normalize_by_allstd
|
|
230
|
+
self.normalize_by_allstd2 = normalize_by_allstd2
|
|
231
|
+
assert self.normalize_dp + self.normalize_by_std + self.normalize_by_allstd < 2 # only nomalize by one method
|
|
232
|
+
self.relative_xyz = relative_xyz
|
|
233
|
+
self.return_only_idx = return_only_idx
|
|
234
|
+
|
|
235
|
+
def forward(self, query_xyz: torch.Tensor, support_xyz: torch.Tensor, features: torch.Tensor = None) -> Tuple[
|
|
236
|
+
torch.Tensor]:
|
|
237
|
+
"""
|
|
238
|
+
:param query_xyz: (B, npoint, 3) xyz coordinates of the features
|
|
239
|
+
:param support_xyz: (B, N, 3) centroids
|
|
240
|
+
:param features: (B, C, N) descriptors of the features
|
|
241
|
+
:return:
|
|
242
|
+
new_features: (B, 3 + C, npoint, nsample)
|
|
243
|
+
"""
|
|
244
|
+
idx = ball_query(self.radius, self.nsample, support_xyz, query_xyz)
|
|
245
|
+
|
|
246
|
+
if self.return_only_idx:
|
|
247
|
+
return idx
|
|
248
|
+
xyz_trans = support_xyz.transpose(1, 2).contiguous()
|
|
249
|
+
grouped_xyz = grouping_operation(xyz_trans, idx) # (B, 3, npoint, nsample)
|
|
250
|
+
if self.relative_xyz:
|
|
251
|
+
grouped_xyz = grouped_xyz - query_xyz.transpose(1, 2).unsqueeze(-1) # relative position
|
|
252
|
+
if self.normalize_dp:
|
|
253
|
+
grouped_xyz /= self.radius
|
|
254
|
+
grouped_features = grouping_operation(features, idx) if features is not None else None
|
|
255
|
+
return grouped_xyz, grouped_features
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class GroupAll(nn.Module):
|
|
259
|
+
def __init__(self, ):
|
|
260
|
+
super().__init__()
|
|
261
|
+
|
|
262
|
+
def forward(self, new_xyz: torch.Tensor, xyz: torch.Tensor, features: torch.Tensor = None):
|
|
263
|
+
"""
|
|
264
|
+
:param xyz: (B, N, 3) xyz coordinates of the features
|
|
265
|
+
:param new_xyz: ignored
|
|
266
|
+
:param features: (B, C, N) descriptors of the features
|
|
267
|
+
:return:
|
|
268
|
+
new_features: (B, C + 3, 1, N)
|
|
269
|
+
"""
|
|
270
|
+
grouped_xyz = xyz.transpose(1, 2).unsqueeze(2)
|
|
271
|
+
grouped_features = features.unsqueeze(2) if features is not None else None
|
|
272
|
+
return grouped_xyz, grouped_features
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class KNNGroup(nn.Module):
|
|
276
|
+
def __init__(self, nsample: int,
|
|
277
|
+
relative_xyz=True,
|
|
278
|
+
normalize_dp=False,
|
|
279
|
+
return_only_idx=False,
|
|
280
|
+
**kwargs
|
|
281
|
+
):
|
|
282
|
+
"""[summary]
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
nsample (int): maximum number of features to gather in the ball
|
|
286
|
+
use_xyz (bool, optional): concate xyz. Defaults to True.
|
|
287
|
+
ret_grouped_xyz (bool, optional): [description]. Defaults to False.
|
|
288
|
+
normalize_dp (bool, optional): [description]. Defaults to False.
|
|
289
|
+
"""
|
|
290
|
+
super().__init__()
|
|
291
|
+
self.nsample = nsample
|
|
292
|
+
self.knn = KNN(nsample, transpose_mode=True)
|
|
293
|
+
self.relative_xyz = relative_xyz
|
|
294
|
+
self.normalize_dp = normalize_dp
|
|
295
|
+
self.return_only_idx = return_only_idx
|
|
296
|
+
|
|
297
|
+
def forward(self, query_xyz: torch.Tensor, support_xyz: torch.Tensor, features: torch.Tensor = None) -> Tuple[
|
|
298
|
+
torch.Tensor]:
|
|
299
|
+
"""
|
|
300
|
+
:param query_xyz: (B, N, 3) xyz coordinates of the features
|
|
301
|
+
:param support_xyz: (B, npoint, 3) centroids
|
|
302
|
+
:param features: (B, C, N) descriptors of the features
|
|
303
|
+
:return:
|
|
304
|
+
new_features: (B, 3 + C, npoint, nsample)
|
|
305
|
+
"""
|
|
306
|
+
_, idx = self.knn(support_xyz, query_xyz)
|
|
307
|
+
if self.return_only_idx:
|
|
308
|
+
return idx
|
|
309
|
+
idx = idx.int()
|
|
310
|
+
xyz_trans = support_xyz.transpose(1, 2).contiguous()
|
|
311
|
+
grouped_xyz = grouping_operation(xyz_trans, idx) # (B, 3, npoint, nsample)
|
|
312
|
+
if self.relative_xyz:
|
|
313
|
+
grouped_xyz -= query_xyz.transpose(1, 2).unsqueeze(-1) # relative position
|
|
314
|
+
if self.normalize_dp:
|
|
315
|
+
grouped_xyz /= torch.amax(torch.sqrt(torch.sum(grouped_xyz**2, dim=1)), dim=(1, 2)).view(-1, 1, 1, 1)
|
|
316
|
+
if features is not None:
|
|
317
|
+
grouped_features = grouping_operation(features, idx)
|
|
318
|
+
return grouped_xyz, grouped_features
|
|
319
|
+
else:
|
|
320
|
+
return grouped_xyz, None
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def get_aggregation_feautres(p, dp, f, fj, feature_type='dp_fj'):
|
|
324
|
+
if feature_type == 'dp_fj':
|
|
325
|
+
fj = torch.cat([dp, fj], 1)
|
|
326
|
+
elif feature_type == 'dp_fj_df':
|
|
327
|
+
df = fj - f.unsqueeze(-1)
|
|
328
|
+
fj = torch.cat([dp, fj, df], 1)
|
|
329
|
+
elif feature_type == 'pi_dp_fj_df':
|
|
330
|
+
df = fj - f.unsqueeze(-1)
|
|
331
|
+
fj = torch.cat([p.transpose(1, 2).unsqueeze(-1).expand(-1, -1, -1, df.shape[-1]), dp, fj, df], 1)
|
|
332
|
+
elif feature_type == 'dp_df':
|
|
333
|
+
df = fj - f.unsqueeze(-1)
|
|
334
|
+
fj = torch.cat([dp, df], 1)
|
|
335
|
+
return fj
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def create_grouper(group_args):
|
|
339
|
+
group_args_copy = copy.deepcopy(group_args)
|
|
340
|
+
method = group_args_copy.pop('NAME', 'ballquery')
|
|
341
|
+
radius = group_args_copy.pop('radius', 0.1)
|
|
342
|
+
nsample = group_args_copy.pop('nsample', 20)
|
|
343
|
+
|
|
344
|
+
logging.info(group_args)
|
|
345
|
+
if nsample is not None:
|
|
346
|
+
if method == 'ballquery':
|
|
347
|
+
grouper = QueryAndGroup(radius, nsample, **group_args_copy)
|
|
348
|
+
elif method == 'knn':
|
|
349
|
+
grouper = KNNGroup(nsample, **group_args_copy)
|
|
350
|
+
else:
|
|
351
|
+
grouper = GroupAll()
|
|
352
|
+
return grouper
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
if __name__ == "__main__":
|
|
356
|
+
import time
|
|
357
|
+
|
|
358
|
+
B, C, N = 2, 3, 40960
|
|
359
|
+
K = 16
|
|
360
|
+
device = 'cuda'
|
|
361
|
+
points = torch.randn([B, N, C], device=device, dtype=torch.float)
|
|
362
|
+
print(points.shape, '\n', points)
|
|
363
|
+
|
|
364
|
+
# --------------- debug downsampling
|
|
365
|
+
from openpoints.models.layers.layer3d import RandomSample, random_sample, furthest_point_sample
|
|
366
|
+
|
|
367
|
+
npoints = 10000
|
|
368
|
+
# rs = RandomSample(num_to_sample=npoints)
|
|
369
|
+
# query, _= rs(points)
|
|
370
|
+
idx = random_sample(points, npoints)
|
|
371
|
+
# torch gather is faster then operation gather.
|
|
372
|
+
query = torch.gather(points, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
|
|
373
|
+
print(query.shape, '\n', query)
|
|
374
|
+
|
|
375
|
+
idx = furthest_point_sample(points, npoints).to(torch.int64)
|
|
376
|
+
query = torch.gather(points, 1, idx.unsqueeze(-1).expand(-1, -1, 3))
|
|
377
|
+
print(query.shape, '\n', query)
|
|
378
|
+
|
|
379
|
+
# # --------------- debug KNN
|
|
380
|
+
# knn = KNN(k=K, transpose_mode=True)
|
|
381
|
+
# # knn to get the neighborhood
|
|
382
|
+
|
|
383
|
+
# # compare time usage.
|
|
384
|
+
# st = time.time()
|
|
385
|
+
# for _ in range(100):
|
|
386
|
+
# _, knnidx = knn(points, query) # B G M
|
|
387
|
+
# idx_base = torch.arange(0, B, device=points.device).view(-1, 1, 1) * N
|
|
388
|
+
# idx = knnidx + idx_base
|
|
389
|
+
# idx = idx.view(-1)
|
|
390
|
+
# neighborhood = points.view(B * N, -1)[idx, :]
|
|
391
|
+
# neighborhood = neighborhood.view(B, npoints, K, 3).contiguous()
|
|
392
|
+
# # normalize
|
|
393
|
+
# neighborhood1 = neighborhood - query.unsqueeze(2)
|
|
394
|
+
# print(time.time() - st)
|
|
395
|
+
# # print(neighborhood1.shape, '\n', neighborhood1)
|
|
396
|
+
|
|
397
|
+
# knngroup = KNNGroup(K)
|
|
398
|
+
# # KNN Group is faster then above torch indexing when warpped in class.
|
|
399
|
+
# st = time.time()
|
|
400
|
+
# for _ in range(100):
|
|
401
|
+
# neighborhood2 = knngroup(query, points)
|
|
402
|
+
# print(time.time() - st)
|
|
403
|
+
# # print(neighborhood2.shape, '\n', neighborhood2)
|
|
404
|
+
# flag = torch.allclose(neighborhood1, neighborhood2.permute(0, 2, 3, 1))
|
|
405
|
+
# print(flag)
|
|
406
|
+
|
|
407
|
+
# ------------- debug ball query
|
|
408
|
+
query_group = QueryAndGroup(0.1, K)
|
|
409
|
+
|
|
410
|
+
st = time.time()
|
|
411
|
+
for _ in range(100):
|
|
412
|
+
# ball querying is 40 times faster then KNN
|
|
413
|
+
features = query_group(query, points)
|
|
414
|
+
print(time.time() - st)
|
|
415
|
+
print(features.shape)
|