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,153 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
from .simpleview_util import PCViews
|
|
4
|
+
from ..build import MODELS
|
|
5
|
+
# from roi_align import CropAndResize # crop_and_resize module
|
|
6
|
+
from openpoints.transforms import build_transforms_from_cfg
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Squeeze(nn.Module):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
def forward(self, inp):
|
|
14
|
+
return inp.squeeze()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BatchNormPoint(nn.Module):
|
|
18
|
+
def __init__(self, feat_size):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.feat_size = feat_size
|
|
21
|
+
self.bn = nn.BatchNorm1d(feat_size)
|
|
22
|
+
|
|
23
|
+
def forward(self, x):
|
|
24
|
+
assert len(x.shape) == 3
|
|
25
|
+
s1, s2, s3 = x.shape[0], x.shape[1], x.shape[2]
|
|
26
|
+
assert s3 == self.feat_size
|
|
27
|
+
x = x.view(s1 * s2, self.feat_size)
|
|
28
|
+
x = self.bn(x)
|
|
29
|
+
return x.view(s1, s2, s3)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@MODELS.register_module()
|
|
33
|
+
class MVFC(nn.Module):
|
|
34
|
+
"""
|
|
35
|
+
Final FC layers for the MV model
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, num_views, in_features, out_features, dropout):
|
|
39
|
+
super().__init__()
|
|
40
|
+
self.num_views = num_views
|
|
41
|
+
self.in_features = in_features
|
|
42
|
+
self.model = nn.Sequential(
|
|
43
|
+
BatchNormPoint(in_features),
|
|
44
|
+
# dropout before concatenation so that each view drops features independently
|
|
45
|
+
nn.Dropout(dropout),
|
|
46
|
+
nn.Flatten(),
|
|
47
|
+
nn.Linear(in_features=in_features * self.num_views,
|
|
48
|
+
out_features=in_features),
|
|
49
|
+
nn.BatchNorm1d(in_features),
|
|
50
|
+
nn.ReLU(),
|
|
51
|
+
nn.Dropout(dropout),
|
|
52
|
+
nn.Linear(in_features=in_features, out_features=out_features,
|
|
53
|
+
bias=True))
|
|
54
|
+
|
|
55
|
+
def forward(self, feat):
|
|
56
|
+
feat = feat.view((-1, self.num_views, self.in_features))
|
|
57
|
+
out = self.model(feat)
|
|
58
|
+
return out
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@MODELS.register_module()
|
|
62
|
+
class MVModel(nn.Module):
|
|
63
|
+
def __init__(self, task='cls', backbone='resnet18',
|
|
64
|
+
channels=16,
|
|
65
|
+
num_classes=15,
|
|
66
|
+
resolution=128,
|
|
67
|
+
use_img_transform=False,
|
|
68
|
+
**kwargs):
|
|
69
|
+
super().__init__()
|
|
70
|
+
assert task == 'cls'
|
|
71
|
+
self.task = task
|
|
72
|
+
self.num_classes = num_classes
|
|
73
|
+
self.dropout = kwargs.get('dropout', 0.5)
|
|
74
|
+
self.channels = channels
|
|
75
|
+
pc_views = PCViews()
|
|
76
|
+
self.num_views = pc_views.num_views
|
|
77
|
+
self._get_img = pc_views.get_img
|
|
78
|
+
|
|
79
|
+
img_layers, in_features = self.get_img_layers(
|
|
80
|
+
backbone, channels=channels)
|
|
81
|
+
self.img_model = nn.Sequential(*img_layers)
|
|
82
|
+
|
|
83
|
+
self.final_fc = MVFC(
|
|
84
|
+
num_views=self.num_views,
|
|
85
|
+
in_features=in_features,
|
|
86
|
+
out_features=self.num_classes,
|
|
87
|
+
dropout=self.dropout)
|
|
88
|
+
if use_img_transform:
|
|
89
|
+
self.img_transform = build_transforms_from_cfg('img', {'img': ['Zoom']})
|
|
90
|
+
else:
|
|
91
|
+
self.img_transform = None
|
|
92
|
+
|
|
93
|
+
def forward(self, pc):
|
|
94
|
+
"""
|
|
95
|
+
:param pc:
|
|
96
|
+
:return:
|
|
97
|
+
"""
|
|
98
|
+
if hasattr(pc, 'keys'):
|
|
99
|
+
pc = pc['pos']
|
|
100
|
+
img = self.get_img(pc)
|
|
101
|
+
|
|
102
|
+
if self.training and self.img_transform is not None:
|
|
103
|
+
img = self.img_transform(img, pc.shape[0] * self.num_views)
|
|
104
|
+
feat = self.img_model(img)
|
|
105
|
+
logit = self.final_fc(feat)
|
|
106
|
+
return logit
|
|
107
|
+
|
|
108
|
+
def forward_cls_feat(self, pc):
|
|
109
|
+
return self.forward(pc)
|
|
110
|
+
|
|
111
|
+
def get_img(self, pc):
|
|
112
|
+
img = self._get_img(pc)
|
|
113
|
+
img = img.unsqueeze(3)
|
|
114
|
+
# [num_pc * num_views, 1, RESOLUTION, RESOLUTION]
|
|
115
|
+
img = img.permute(0, 3, 1, 2)
|
|
116
|
+
return img
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def get_img_layers(backbone, channels):
|
|
120
|
+
"""
|
|
121
|
+
Return layers for the image model
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
from .resnet import _resnet, BasicBlock
|
|
125
|
+
assert backbone == 'resnet18'
|
|
126
|
+
layers = [2, 2, 2, 2]
|
|
127
|
+
block = BasicBlock
|
|
128
|
+
backbone_mod = _resnet(
|
|
129
|
+
arch=None,
|
|
130
|
+
block=block,
|
|
131
|
+
layers=layers,
|
|
132
|
+
pretrained=False,
|
|
133
|
+
progress=False,
|
|
134
|
+
feature_size=channels,
|
|
135
|
+
zero_init_residual=True)
|
|
136
|
+
|
|
137
|
+
all_layers = [x for x in backbone_mod.children()]
|
|
138
|
+
in_features = all_layers[-1].in_features
|
|
139
|
+
|
|
140
|
+
# all layers except the final fc layer and the initial conv layers
|
|
141
|
+
# WARNING: this is checked only for resnet models
|
|
142
|
+
main_layers = all_layers[4:-1]
|
|
143
|
+
img_layers = [
|
|
144
|
+
nn.Conv2d(1, channels, kernel_size=(3, 3), stride=(1, 1),
|
|
145
|
+
padding=(1, 1), bias=False),
|
|
146
|
+
nn.BatchNorm2d(channels, eps=1e-05, momentum=0.1,
|
|
147
|
+
affine=True, track_running_stats=True),
|
|
148
|
+
nn.ReLU(inplace=True),
|
|
149
|
+
*main_layers,
|
|
150
|
+
Squeeze()
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
return img_layers, in_features
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
import numpy as np
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
RESOLUTION = 128
|
|
6
|
+
TRANS = -1.4
|
|
7
|
+
|
|
8
|
+
def euler2mat(angle):
|
|
9
|
+
"""Convert euler angles to rotation matrix.
|
|
10
|
+
:param angle: [3] or [b, 3]
|
|
11
|
+
:return
|
|
12
|
+
rotmat: [3] or [b, 3, 3]
|
|
13
|
+
source
|
|
14
|
+
https://github.com/ClementPinard/SfmLearner-Pytorch/blob/master/inverse_warp.py
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
if len(angle.size()) == 1:
|
|
18
|
+
x, y, z = angle[0], angle[1], angle[2]
|
|
19
|
+
_dim = 0
|
|
20
|
+
_view = [3, 3]
|
|
21
|
+
elif len(angle.size()) == 2:
|
|
22
|
+
b, _ = angle.size()
|
|
23
|
+
x, y, z = angle[:, 0], angle[:, 1], angle[:, 2]
|
|
24
|
+
_dim = 1
|
|
25
|
+
_view = [b, 3, 3]
|
|
26
|
+
|
|
27
|
+
else:
|
|
28
|
+
assert False
|
|
29
|
+
|
|
30
|
+
cosz = torch.cos(z)
|
|
31
|
+
sinz = torch.sin(z)
|
|
32
|
+
|
|
33
|
+
# zero = torch.zeros([b], requires_grad=False, device=angle.device)[0]
|
|
34
|
+
# one = torch.ones([b], requires_grad=False, device=angle.device)[0]
|
|
35
|
+
zero = z.detach()*0
|
|
36
|
+
one = zero.detach()+1
|
|
37
|
+
zmat = torch.stack([cosz, -sinz, zero,
|
|
38
|
+
sinz, cosz, zero,
|
|
39
|
+
zero, zero, one], dim=_dim).reshape(_view)
|
|
40
|
+
|
|
41
|
+
cosy = torch.cos(y)
|
|
42
|
+
siny = torch.sin(y)
|
|
43
|
+
|
|
44
|
+
ymat = torch.stack([cosy, zero, siny,
|
|
45
|
+
zero, one, zero,
|
|
46
|
+
-siny, zero, cosy], dim=_dim).reshape(_view)
|
|
47
|
+
|
|
48
|
+
cosx = torch.cos(x)
|
|
49
|
+
sinx = torch.sin(x)
|
|
50
|
+
|
|
51
|
+
xmat = torch.stack([one, zero, zero,
|
|
52
|
+
zero, cosx, -sinx,
|
|
53
|
+
zero, sinx, cosx], dim=_dim).reshape(_view)
|
|
54
|
+
|
|
55
|
+
rot_mat = xmat @ ymat @ zmat
|
|
56
|
+
# print(rot_mat)
|
|
57
|
+
return rot_mat
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def distribute(depth, _x, _y, size_x, size_y, image_height, image_width):
|
|
61
|
+
"""
|
|
62
|
+
Distributes the depth associated with each point to the discrete coordinates (image_height, image_width) in a region
|
|
63
|
+
of size (size_x, size_y).
|
|
64
|
+
:param depth:
|
|
65
|
+
:param _x:
|
|
66
|
+
:param _y:
|
|
67
|
+
:param size_x:
|
|
68
|
+
:param size_y:
|
|
69
|
+
:param image_height:
|
|
70
|
+
:param image_width:
|
|
71
|
+
:return:
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
assert size_x % 2 == 0 or size_x == 1
|
|
75
|
+
assert size_y % 2 == 0 or size_y == 1
|
|
76
|
+
batch, _ = depth.size()
|
|
77
|
+
epsilon = torch.tensor([1e-12], requires_grad=False, device=depth.device)
|
|
78
|
+
_i = torch.linspace(-size_x / 2, (size_x / 2) - 1, size_x, requires_grad=False, device=depth.device)
|
|
79
|
+
_j = torch.linspace(-size_y / 2, (size_y / 2) - 1, size_y, requires_grad=False, device=depth.device)
|
|
80
|
+
|
|
81
|
+
extended_x = _x.unsqueeze(2).repeat([1, 1, size_x]) + _i # [batch, num_points, size_x]
|
|
82
|
+
extended_y = _y.unsqueeze(2).repeat([1, 1, size_y]) + _j # [batch, num_points, size_y]
|
|
83
|
+
|
|
84
|
+
extended_x = extended_x.unsqueeze(3).repeat([1, 1, 1, size_y]) # [batch, num_points, size_x, size_y]
|
|
85
|
+
extended_y = extended_y.unsqueeze(2).repeat([1, 1, size_x, 1]) # [batch, num_points, size_x, size_y]
|
|
86
|
+
|
|
87
|
+
extended_x.ceil_()
|
|
88
|
+
extended_y.ceil_()
|
|
89
|
+
|
|
90
|
+
value = depth.unsqueeze(2).unsqueeze(3).repeat([1, 1, size_x, size_y]) # [batch, num_points, size_x, size_y]
|
|
91
|
+
|
|
92
|
+
# all points that will be finally used
|
|
93
|
+
masked_points = ((extended_x >= 0)
|
|
94
|
+
* (extended_x <= image_height - 1)
|
|
95
|
+
* (extended_y >= 0)
|
|
96
|
+
* (extended_y <= image_width - 1)
|
|
97
|
+
* (value >= 0))
|
|
98
|
+
|
|
99
|
+
true_extended_x = extended_x
|
|
100
|
+
true_extended_y = extended_y
|
|
101
|
+
|
|
102
|
+
# to prevent error
|
|
103
|
+
extended_x = (extended_x % image_height)
|
|
104
|
+
extended_y = (extended_y % image_width)
|
|
105
|
+
|
|
106
|
+
# [batch, num_points, size_x, size_y]
|
|
107
|
+
distance = torch.abs((extended_x - _x.unsqueeze(2).unsqueeze(3))
|
|
108
|
+
* (extended_y - _y.unsqueeze(2).unsqueeze(3)))
|
|
109
|
+
weight = (masked_points.float()
|
|
110
|
+
* (1 / (value + epsilon))) # [batch, num_points, size_x, size_y]
|
|
111
|
+
weighted_value = value * weight
|
|
112
|
+
|
|
113
|
+
weight = weight.view([batch, -1])
|
|
114
|
+
weighted_value = weighted_value.view([batch, -1])
|
|
115
|
+
|
|
116
|
+
coordinates = (extended_x.view([batch, -1]) * image_width) + extended_y.view(
|
|
117
|
+
[batch, -1])
|
|
118
|
+
coord_max = image_height * image_width
|
|
119
|
+
true_coordinates = (true_extended_x.view([batch, -1]) * image_width) + true_extended_y.view(
|
|
120
|
+
[batch, -1])
|
|
121
|
+
true_coordinates[~masked_points.view([batch, -1])] = coord_max
|
|
122
|
+
weight_scattered = torch.zeros(
|
|
123
|
+
[batch, image_width * image_height],
|
|
124
|
+
device=depth.device).scatter_add(1, coordinates.long(), weight)
|
|
125
|
+
|
|
126
|
+
masked_zero_weight_scattered = (weight_scattered == 0.0)
|
|
127
|
+
weight_scattered += masked_zero_weight_scattered.float()
|
|
128
|
+
|
|
129
|
+
weighed_value_scattered = torch.zeros(
|
|
130
|
+
[batch, image_width * image_height],
|
|
131
|
+
device=depth.device).scatter_add(1, coordinates.long(), weighted_value)
|
|
132
|
+
|
|
133
|
+
return weighed_value_scattered, weight_scattered
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def points2depth(points, image_height, image_width, size_x=4, size_y=4):
|
|
137
|
+
"""
|
|
138
|
+
:param points: [B, num_points, 3]
|
|
139
|
+
:param image_width:
|
|
140
|
+
:param image_height:
|
|
141
|
+
:param size_x:
|
|
142
|
+
:param size_y:
|
|
143
|
+
:return:
|
|
144
|
+
depth_recovered: [B, image_width, image_height]
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
epsilon = torch.tensor([1e-12], requires_grad=False, device=points.device)
|
|
148
|
+
# epsilon not needed, kept here to ensure exact replication of old version
|
|
149
|
+
coord_x = (points[:, :, 0] / (points[:, :, 2] + epsilon)) * (image_width / image_height) # [batch, num_points]
|
|
150
|
+
coord_y = (points[:, :, 1] / (points[:, :, 2] + epsilon)) # [batch, num_points]
|
|
151
|
+
|
|
152
|
+
batch, total_points, _ = points.size()
|
|
153
|
+
depth = points[:, :, 2] # [batch, num_points]
|
|
154
|
+
# pdb.set_trace()
|
|
155
|
+
_x = ((coord_x + 1) * image_height) / 2
|
|
156
|
+
_y = ((coord_y + 1) * image_width) / 2
|
|
157
|
+
|
|
158
|
+
weighed_value_scattered, weight_scattered = distribute(
|
|
159
|
+
depth=depth,
|
|
160
|
+
_x=_x,
|
|
161
|
+
_y=_y,
|
|
162
|
+
size_x=size_x,
|
|
163
|
+
size_y=size_y,
|
|
164
|
+
image_height=image_height,
|
|
165
|
+
image_width=image_width)
|
|
166
|
+
|
|
167
|
+
depth_recovered = (weighed_value_scattered / weight_scattered).view([
|
|
168
|
+
batch, image_height, image_width
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
return depth_recovered
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# source: https://discuss.pytorch.org/t/batched-index-select/9115/6
|
|
175
|
+
def batched_index_select(inp, dim, index):
|
|
176
|
+
"""
|
|
177
|
+
input: B x * x ... x *
|
|
178
|
+
dim: 0 < scalar
|
|
179
|
+
index: B x M
|
|
180
|
+
"""
|
|
181
|
+
views = [inp.shape[0]] + \
|
|
182
|
+
[1 if i != dim else -1 for i in range(1, len(inp.shape))]
|
|
183
|
+
expanse = list(inp.shape)
|
|
184
|
+
expanse[0] = -1
|
|
185
|
+
expanse[dim] = -1
|
|
186
|
+
index = index.view(views).expand(expanse)
|
|
187
|
+
return torch.gather(inp, dim, index)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def point_fea_img_fea(point_fea, point_coo, h, w):
|
|
191
|
+
"""
|
|
192
|
+
each point_coo is of the form (x*w + h). points not in the canvas are removed
|
|
193
|
+
:param point_fea: [batch_size, num_points, feat_size]
|
|
194
|
+
:param point_coo: [batch_size, num_points]
|
|
195
|
+
:return:
|
|
196
|
+
"""
|
|
197
|
+
assert len(point_fea.shape) == 3
|
|
198
|
+
assert len(point_coo.shape) == 2
|
|
199
|
+
assert point_fea.shape[0:2] == point_coo.shape
|
|
200
|
+
|
|
201
|
+
coo_max = ((h - 1) * w) + (w - 1)
|
|
202
|
+
mask_point_coo = (point_coo >= 0) * (point_coo <= coo_max)
|
|
203
|
+
point_coo *= mask_point_coo.float()
|
|
204
|
+
point_fea *= mask_point_coo.float().unsqueeze(-1)
|
|
205
|
+
|
|
206
|
+
bs, _, fs = point_fea.shape
|
|
207
|
+
point_coo = point_coo.unsqueeze(2).repeat([1, 1, fs])
|
|
208
|
+
img_fea = torch.zeros([bs, h * w, fs], device=point_fea.device).scatter_add(1, point_coo.long(), point_fea)
|
|
209
|
+
|
|
210
|
+
return img_fea
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def distribute_img_fea_points(img_fea, point_coord):
|
|
214
|
+
"""
|
|
215
|
+
:param img_fea: [B, C, H, W]
|
|
216
|
+
:param point_coord: [B, num_points], each coordinate is a scalar value given by (x * W) + y
|
|
217
|
+
:return
|
|
218
|
+
point_fea: [B, num_points, C], for points with coordinates outside the image, we return 0
|
|
219
|
+
"""
|
|
220
|
+
B, C, H, W = list(img_fea.size())
|
|
221
|
+
img_fea = img_fea.permute(0, 2, 3, 1).view([B, H*W, C])
|
|
222
|
+
|
|
223
|
+
coord_max = ((H - 1) * W) + (W - 1)
|
|
224
|
+
mask_point_coord = (point_coord >= 0) * (point_coord <= coord_max)
|
|
225
|
+
mask_point_coord = mask_point_coord.float()
|
|
226
|
+
point_coord = mask_point_coord * point_coord
|
|
227
|
+
point_fea = batched_index_select(
|
|
228
|
+
inp=img_fea,
|
|
229
|
+
dim=1,
|
|
230
|
+
index=point_coord.long())
|
|
231
|
+
point_fea = mask_point_coord.unsqueeze(-1) * point_fea
|
|
232
|
+
return point_fea
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class PCViews:
|
|
236
|
+
"""For creating images from PC based on the view information. Faster as the
|
|
237
|
+
repeated operations are done only once whie initialization.
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
def __init__(self):
|
|
241
|
+
_views = np.asarray([
|
|
242
|
+
[[0 * np.pi / 2, 0, np.pi / 2], [0, 0, TRANS]],
|
|
243
|
+
[[1 * np.pi / 2, 0, np.pi / 2], [0, 0, TRANS]],
|
|
244
|
+
[[2 * np.pi / 2, 0, np.pi / 2], [0, 0, TRANS]],
|
|
245
|
+
[[3 * np.pi / 2, 0, np.pi / 2], [0, 0, TRANS]],
|
|
246
|
+
[[0, -np.pi / 2, np.pi / 2], [0, 0, TRANS]],
|
|
247
|
+
[[0, np.pi / 2, np.pi / 2], [0, 0, TRANS]]])
|
|
248
|
+
self.num_views = 6
|
|
249
|
+
angle = torch.tensor(_views[:, 0, :]).float().cuda()
|
|
250
|
+
self.rot_mat = euler2mat(angle).transpose(1, 2)
|
|
251
|
+
self.translation = torch.tensor(_views[:, 1, :]).float().cuda()
|
|
252
|
+
self.translation = self.translation.unsqueeze(1)
|
|
253
|
+
|
|
254
|
+
def get_img(self, points):
|
|
255
|
+
"""Get image based on the prespecified specifications.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
points (torch.tensor): of size [B, _, 3]
|
|
259
|
+
Returns:
|
|
260
|
+
img (torch.tensor): of size [B * self.num_views, RESOLUTION,
|
|
261
|
+
RESOLUTION]
|
|
262
|
+
"""
|
|
263
|
+
b, _, _ = points.shape
|
|
264
|
+
v = self.translation.shape[0]
|
|
265
|
+
|
|
266
|
+
_points = self.point_transform(
|
|
267
|
+
points=torch.repeat_interleave(points, v, dim=0),
|
|
268
|
+
rot_mat=self.rot_mat.repeat(b, 1, 1),
|
|
269
|
+
translation=self.translation.repeat(b, 1, 1))
|
|
270
|
+
|
|
271
|
+
img = points2depth(
|
|
272
|
+
points=_points,
|
|
273
|
+
image_height=RESOLUTION,
|
|
274
|
+
image_width=RESOLUTION,
|
|
275
|
+
size_x=1,
|
|
276
|
+
size_y=1,
|
|
277
|
+
)
|
|
278
|
+
return img
|
|
279
|
+
|
|
280
|
+
@staticmethod
|
|
281
|
+
def point_transform(points, rot_mat, translation):
|
|
282
|
+
"""
|
|
283
|
+
:param points: [batch, num_points, 3]
|
|
284
|
+
:param rot_mat: [batch, 3]
|
|
285
|
+
:param translation: [batch, 1, 3]
|
|
286
|
+
:return:
|
|
287
|
+
"""
|
|
288
|
+
rot_mat = rot_mat.to(points.device)
|
|
289
|
+
translation = translation.to(points.device)
|
|
290
|
+
points = torch.matmul(points, rot_mat)
|
|
291
|
+
points = points - translation
|
|
292
|
+
return points
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from openpoints.utils import registry
|
|
2
|
+
MODELS = registry.Registry('models')
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def build_model_from_cfg(cfg, **kwargs):
|
|
6
|
+
"""
|
|
7
|
+
Build a model, defined by `NAME`.
|
|
8
|
+
Args:
|
|
9
|
+
cfg (eDICT):
|
|
10
|
+
Returns:
|
|
11
|
+
Model: a constructed model specified by NAME.
|
|
12
|
+
"""
|
|
13
|
+
return MODELS.build(cfg, **kwargs)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import logging
|
|
4
|
+
from typing import List
|
|
5
|
+
from ..layers import create_linearblock
|
|
6
|
+
from ...utils import get_missing_parameters_message, get_unexpected_parameters_message
|
|
7
|
+
from ..build import MODELS, build_model_from_cfg
|
|
8
|
+
from ...loss import build_criterion_from_cfg
|
|
9
|
+
from ...utils import load_checkpoint
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@MODELS.register_module()
|
|
13
|
+
class BaseCls(nn.Module):
|
|
14
|
+
def __init__(self,
|
|
15
|
+
encoder_args=None,
|
|
16
|
+
cls_args=None,
|
|
17
|
+
criterion_args=None,
|
|
18
|
+
**kwargs):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.encoder = build_model_from_cfg(encoder_args)
|
|
21
|
+
|
|
22
|
+
if cls_args is not None:
|
|
23
|
+
in_channels = self.encoder.out_channels if hasattr(self.encoder, 'out_channels') else cls_args.get('in_channels', None)
|
|
24
|
+
cls_args.in_channels = in_channels
|
|
25
|
+
self.prediction = build_model_from_cfg(cls_args)
|
|
26
|
+
else:
|
|
27
|
+
self.prediction = nn.Identity()
|
|
28
|
+
self.criterion = build_criterion_from_cfg(criterion_args) if criterion_args is not None else None
|
|
29
|
+
|
|
30
|
+
def forward(self, data):
|
|
31
|
+
global_feat = self.encoder.forward_cls_feat(data)
|
|
32
|
+
return self.prediction(global_feat)
|
|
33
|
+
|
|
34
|
+
def get_loss(self, pred, gt, inputs=None):
|
|
35
|
+
return self.criterion(pred, gt.long())
|
|
36
|
+
|
|
37
|
+
def get_logits_loss(self, data, gt):
|
|
38
|
+
logits = self.forward(data)
|
|
39
|
+
return logits, self.criterion(logits, gt.long())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@MODELS.register_module()
|
|
43
|
+
class DistillCls(BaseCls):
|
|
44
|
+
def __init__(self,
|
|
45
|
+
encoder_args=None,
|
|
46
|
+
cls_args=None,
|
|
47
|
+
distill_args=None,
|
|
48
|
+
criterion_args=None,
|
|
49
|
+
**kwargs):
|
|
50
|
+
super().__init__(encoder_args, cls_args, criterion_args)
|
|
51
|
+
self.distill = encoder_args.get('distill', True)
|
|
52
|
+
in_channels = self.encoder.distill_channels
|
|
53
|
+
distill_args.distill_head_args.in_channels = in_channels
|
|
54
|
+
self.dist_head = build_model_from_cfg(distill_args.distill_head_args)
|
|
55
|
+
self.dist_model = build_model_from_cfg(distill_args).cuda()
|
|
56
|
+
load_checkpoint(self.dist_model, distill_args.pretrained_path)
|
|
57
|
+
self.dist_model.eval()
|
|
58
|
+
|
|
59
|
+
def forward(self, p0, f0=None):
|
|
60
|
+
if hasattr(p0, 'keys'):
|
|
61
|
+
p0, f0 = p0['pos'], p0['x']
|
|
62
|
+
if self.distill and self.training:
|
|
63
|
+
global_feat, distill_feature = self.encoder.forward_cls_feat(p0, f0)
|
|
64
|
+
return self.prediction(global_feat), self.dist_head(distill_feature)
|
|
65
|
+
else:
|
|
66
|
+
global_feat = self.encoder.forward_cls_feat(p0, f0)
|
|
67
|
+
return self.prediction(global_feat)
|
|
68
|
+
|
|
69
|
+
def get_loss(self, pred, gt, inputs):
|
|
70
|
+
return self.criterion(inputs, pred, gt.long(), self.dist_model)
|
|
71
|
+
|
|
72
|
+
def get_logits_loss(self, data, gt):
|
|
73
|
+
logits, dist_logits = self.forward(data)
|
|
74
|
+
return logits, self.criterion(data, [logits, dist_logits], gt.long(), self.dist_model)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@MODELS.register_module()
|
|
78
|
+
class ClsHead(nn.Module):
|
|
79
|
+
def __init__(self,
|
|
80
|
+
num_classes: int,
|
|
81
|
+
in_channels: int,
|
|
82
|
+
mlps: List[int]=[256],
|
|
83
|
+
norm_args: dict=None,
|
|
84
|
+
act_args: dict={'act': 'relu'},
|
|
85
|
+
dropout: float=0.5,
|
|
86
|
+
global_feat: str=None,
|
|
87
|
+
point_dim: int=2,
|
|
88
|
+
**kwargs
|
|
89
|
+
):
|
|
90
|
+
"""A general classification head. supports global pooling and [CLS] token
|
|
91
|
+
Args:
|
|
92
|
+
num_classes (int): class num
|
|
93
|
+
in_channels (int): input channels size
|
|
94
|
+
mlps (List[int], optional): channel sizes for hidden layers. Defaults to [256].
|
|
95
|
+
norm_args (dict, optional): dict of configuration for normalization. Defaults to None.
|
|
96
|
+
act_args (_type_, optional): dict of configuration for activation. Defaults to {'act': 'relu'}.
|
|
97
|
+
dropout (float, optional): use dropout when larger than 0. Defaults to 0.5.
|
|
98
|
+
cls_feat (str, optional): preprocessing input features to obtain global feature.
|
|
99
|
+
$\eg$ cls_feat='max,avg' means use the concatenateion of maxpooled and avgpooled features.
|
|
100
|
+
Defaults to None, which means the input feautre is the global feature
|
|
101
|
+
Returns:
|
|
102
|
+
logits: (B, num_classes, N)
|
|
103
|
+
"""
|
|
104
|
+
super().__init__()
|
|
105
|
+
if kwargs:
|
|
106
|
+
logging.warning(f"kwargs: {kwargs} are not used in {__class__.__name__}")
|
|
107
|
+
self.global_feat = global_feat.split(',') if global_feat is not None else None
|
|
108
|
+
self.point_dim = point_dim
|
|
109
|
+
in_channels = len(self.global_feat) * in_channels if global_feat is not None else in_channels
|
|
110
|
+
if mlps is not None:
|
|
111
|
+
mlps = [in_channels] + mlps + [num_classes]
|
|
112
|
+
else:
|
|
113
|
+
mlps = [in_channels, num_classes]
|
|
114
|
+
|
|
115
|
+
heads = []
|
|
116
|
+
for i in range(len(mlps) - 2):
|
|
117
|
+
heads.append(create_linearblock(mlps[i], mlps[i + 1],
|
|
118
|
+
norm_args=norm_args,
|
|
119
|
+
act_args=act_args))
|
|
120
|
+
if dropout:
|
|
121
|
+
heads.append(nn.Dropout(dropout))
|
|
122
|
+
heads.append(create_linearblock(mlps[-2], mlps[-1], act_args=None))
|
|
123
|
+
self.head = nn.Sequential(*heads)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def forward(self, end_points):
|
|
127
|
+
if self.global_feat is not None:
|
|
128
|
+
global_feats = []
|
|
129
|
+
for preprocess in self.global_feat:
|
|
130
|
+
if 'max' in preprocess:
|
|
131
|
+
global_feats.append(torch.max(end_points, dim=self.point_dim, keepdim=False)[0])
|
|
132
|
+
elif preprocess in ['avg', 'mean']:
|
|
133
|
+
global_feats.append(torch.mean(end_points, dim=self.point_dim, keepdim=False))
|
|
134
|
+
end_points = torch.cat(global_feats, dim=1)
|
|
135
|
+
logits = self.head(end_points)
|
|
136
|
+
return logits
|