"""
Point Transformer V1 for Object Classification
Might be a bit different from the original paper
Author: Xiaoyang Wu (xiaoyang.wu.cs@gmail.com)
Please cite our work if the code is helpful to you.
"""
import torch
import torch.nn as nn
from .point_transformer_seg import TransitionDown, Bottleneck
from pimm.models.builder import MODELS
class PointTransformerCls(nn.Module):
def __init__(self, block, blocks, in_channels=6, num_classes=40):
super().__init__()
self.in_channels = in_channels
self.in_planes, planes = in_channels, [32, 64, 128, 256, 512]
fpn_planes, fpnhead_planes, share_planes = 128, 64, 8
stride, nsample = [1, 4, 4, 4, 4], [8, 16, 16, 16, 16]
self.enc1 = self._make_enc(
block,
planes[0],
blocks[0],
share_planes,
stride=stride[0],
nsample=nsample[0],
) # N/1
self.enc2 = self._make_enc(
block,
planes[1],
blocks[1],
share_planes,
stride=stride[1],
nsample=nsample[1],
) # N/4
self.enc3 = self._make_enc(
block,
planes[2],
blocks[2],
share_planes,
stride=stride[2],
nsample=nsample[2],
) # N/16
self.enc4 = self._make_enc(
block,
planes[3],
blocks[3],
share_planes,
stride=stride[3],
nsample=nsample[3],
) # N/64
self.enc5 = self._make_enc(
block,
planes[4],
blocks[4],
share_planes,
stride=stride[4],
nsample=nsample[4],
) # N/256
self.cls = nn.Sequential(
nn.Linear(planes[4], 256),
nn.BatchNorm1d(256),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(256, 128),
nn.BatchNorm1d(128),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(128, num_classes),
)
def _make_enc(self, block, planes, blocks, share_planes=8, stride=1, nsample=16):
layers = [
TransitionDown(self.in_planes, planes * block.expansion, stride, nsample)
]
self.in_planes = planes * block.expansion
for _ in range(1, blocks):
layers.append(
block(self.in_planes, self.in_planes, share_planes, nsample=nsample)
)
return nn.Sequential(*layers)
def forward(self, data_dict):
p0 = data_dict["coord"]
x0 = data_dict["feat"]
o0 = data_dict["offset"].int()
x0 = p0 if self.in_channels == 3 else torch.cat((p0, x0), 1)
p1, x1, o1 = self.enc1([p0, x0, o0])
p2, x2, o2 = self.enc2([p1, x1, o1])
p3, x3, o3 = self.enc3([p2, x2, o2])
p4, x4, o4 = self.enc4([p3, x3, o3])
p5, x5, o5 = self.enc5([p4, x4, o4])
x = []
for i in range(o5.shape[0]):
if i == 0:
s_i, e_i, cnt = 0, o5[0], o5[0]
else:
s_i, e_i, cnt = o5[i - 1], o5[i], o5[i] - o5[i - 1]
x_b = x5[s_i:e_i, :].sum(0, True) / cnt
x.append(x_b)
x = torch.cat(x, 0)
x = self.cls(x)
return x