from __future__ import absolute_import, division, print_function import math import torch.nn.functional as F from torch import nn from torch.nn import init from .DCNv2.dcn_v2 import DCN class DeformConv(nn.Module): def __init__(self, chi, cho): super(DeformConv, self).__init__() self.actf = nn.Sequential( nn.BatchNorm2d(cho, momentum=0.1), nn.ReLU(inplace=True) ) self.conv = DCN(chi, cho, kernel_size=(3, 3), stride=1, padding=1, dilation=1, deformable_groups=1) def forward(self, x): x = self.conv(x) x = self.actf(x) return x class IDAUp(nn.Module): def __init__(self, o, channels, up_f): super(IDAUp, self).__init__() for i in range(1, len(channels)): c = channels[i] f = int(up_f[i]) proj = DeformConv(c, o) node = DeformConv(o, o) up = nn.ConvTranspose2d(o, o, f * 2, stride=f, padding=f // 2, output_padding=0, groups=o, bias=False) fill_up_weights(up) setattr(self, 'proj_' + str(i), proj) setattr(self, 'up_' + str(i), up) setattr(self, 'node_' + str(i), node) def forward(self, layers, startp, endp): for i in range(startp + 1, endp): upsample = getattr(self, 'up_' + str(i - startp)) project = getattr(self, 'proj_' + str(i - startp)) layers[i] = upsample(project(layers[i])) node = getattr(self, 'node_' + str(i - startp)) layers[i] = node(layers[i] + layers[i - 1]) class hswish(nn.Module): def forward(self, x): out = x * F.relu6(x + 3, inplace=True) / 6 return out class hsigmoid(nn.Module): def forward(self, x): out = F.relu6(x + 3, inplace=True) / 6 return out class SeModule(nn.Module): def __init__(self, in_size, reduction=4): super(SeModule, self).__init__() self.se = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False), nn.BatchNorm2d(in_size // reduction), nn.ReLU(inplace=True), nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False), nn.BatchNorm2d(in_size), hsigmoid() ) def forward(self, x): return x * self.se(x) class Block(nn.Module): '''expand + depthwise + pointwise''' def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride): super(Block, self).__init__() self.stride = stride self.se = semodule self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False) self.bn1 = nn.BatchNorm2d(expand_size) self.nolinear1 = nolinear self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, padding=kernel_size // 2, groups=expand_size, bias=False) self.bn2 = nn.BatchNorm2d(expand_size) self.nolinear2 = nolinear self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False) self.bn3 = nn.BatchNorm2d(out_size) self.shortcut = nn.Sequential() if stride == 1 and in_size != out_size: self.shortcut = nn.Sequential( nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False), nn.BatchNorm2d(out_size), ) def forward(self, x): out = self.nolinear1(self.bn1(self.conv1(x))) out = self.nolinear2(self.bn2(self.conv2(out))) out = self.bn3(self.conv3(out)) if self.se != None: out = self.se(out) out = out + self.shortcut(x) if self.stride == 1 else out return out def fill_up_weights(up): w = up.weight.data f = math.ceil(w.size(2) / 2) c = (2 * f - 1 - f % 2) / (2. * f) for i in range(w.size(2)): for j in range(w.size(3)): w[0, 0, i, j] = \ (1 - math.fabs(i / f - c)) * (1 - math.fabs(j / f - c)) for c in range(1, w.size(0)): w[c, 0, :, :] = w[0, 0, :, :] class MobileNetV3(nn.Module): def __init__(self, final_kernel): super(MobileNetV3, self).__init__() self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(16) self.hs1 = hswish() self.bneck0 = nn.Sequential( Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1), Block(3, 16, 64, 24, nn.ReLU(inplace=True), None, 2), Block(3, 24, 72, 24, nn.ReLU(inplace=True), None, 1), ) self.bneck1 = nn.Sequential( Block(5, 24, 72, 40, nn.ReLU(inplace=True), SeModule(40), 2), Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1), ) self.bneck2 = nn.Sequential( Block(3, 40, 240, 80, hswish(), None, 2), Block(3, 80, 200, 80, hswish(), None, 1), Block(3, 80, 184, 80, hswish(), None, 1), Block(3, 80, 184, 80, hswish(), None, 1), Block(3, 80, 480, 112, hswish(), SeModule(112), 1), Block(3, 112, 672, 112, hswish(), SeModule(112), 1), Block(5, 112, 672, 160, hswish(), SeModule(160), 1), ) self.bneck3 = nn.Sequential( Block(5, 160, 672, 160, hswish(), SeModule(160), 2), Block(5, 160, 960, 160, hswish(), SeModule(160), 1), ) self.conv2 = nn.Conv2d(160, 960, kernel_size=1, stride=1, padding=0, bias=False) self.bn2 = nn.BatchNorm2d(960) self.hs2 = hswish() self.ida_up = IDAUp(24, [24, 40, 160, 960], [2 ** i for i in range(4)]) self.init_params() def init_params(self): for m in self.modules(): if isinstance(m, nn.Conv2d): init.kaiming_normal_(m.weight, mode='fan_out') if m.bias is not None: init.constant_(m.bias, 0) elif isinstance(m, nn.BatchNorm2d): init.constant_(m.weight, 1) init.constant_(m.bias, 0) elif isinstance(m, nn.Linear): init.normal_(m.weight, std=0.001) if m.bias is not None: init.constant_(m.bias, 0) def forward(self, x): out = self.hs1(self.bn1(self.conv1(x))) out0 = self.bneck0(out) out1 = self.bneck1(out0) out2 = self.bneck2(out1) out3 = self.bneck3(out2) out3 = self.hs2(self.bn2(self.conv2(out3))) out = [out0, out1, out2, out3] y = [] for i in range(4): y.append(out[i].clone()) self.ida_up(y, 0, len(y)) return y[-1] def get_mobilev3_pose_net(num_layers, cfg): model = MobileNetV3(final_kernel=1) return model