feat: initial HSAP platform
Huaxu Sentinel Active Safety Platform with embedded algorithm code, Docker Compose setup, and vendored dataset scaffolds for clone-and-run. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
98
algorithms/lane_ufld/code/UFLD/model/backbone.py
Executable file
98
algorithms/lane_ufld/code/UFLD/model/backbone.py
Executable file
@@ -0,0 +1,98 @@
|
||||
import torch
|
||||
import torch.nn.modules
|
||||
import torchvision
|
||||
|
||||
from model.vovnet import VOVNET_ALIASES, STAGE_SPECS
|
||||
|
||||
RESNET_BACKBONES = ['18', '34', '50', '101', '152', '50next', '101next', '50wide', '101wide']
|
||||
VOVMNET_BACKBONES = list(VOVNET_ALIASES.keys())
|
||||
SUPPORTED_BACKBONES = RESNET_BACKBONES + VOVMNET_BACKBONES + ['vgg']
|
||||
|
||||
|
||||
def is_vovnet(backbone):
|
||||
return str(backbone) in VOVNET_ALIASES
|
||||
|
||||
|
||||
def get_backbone_spec(backbone):
|
||||
"""Channel layout for parsingNet pool / aux heads."""
|
||||
backbone = str(backbone)
|
||||
if is_vovnet(backbone):
|
||||
from model.vovnet import VOVNET_ALIASES as _aliases
|
||||
variant = _aliases[backbone]
|
||||
ch = STAGE_SPECS[variant]['stage_out_ch']
|
||||
return {
|
||||
'pool_in': ch[3],
|
||||
'aux': (ch[1], ch[2], ch[3]),
|
||||
'family': 'vov',
|
||||
}
|
||||
if backbone in ['18', '34']:
|
||||
return {'pool_in': 512, 'aux': (128, 256, 512), 'family': 'resnet'}
|
||||
if backbone == 'vgg':
|
||||
return {'pool_in': 512, 'aux': (128, 256, 512), 'family': 'vgg'}
|
||||
return {'pool_in': 2048, 'aux': (512, 1024, 2048), 'family': 'resnet'}
|
||||
|
||||
|
||||
def build_backbone(backbone, pretrained=False):
|
||||
backbone = str(backbone)
|
||||
if is_vovnet(backbone):
|
||||
from model.vovnet import vovnet
|
||||
return vovnet(backbone, pretrained=pretrained)
|
||||
if backbone == 'vgg':
|
||||
return vgg16bn(pretrained=pretrained)
|
||||
return resnet(backbone, pretrained=pretrained)
|
||||
|
||||
|
||||
class vgg16bn(torch.nn.Module):
|
||||
def __init__(self, pretrained=False):
|
||||
super(vgg16bn, self).__init__()
|
||||
model = list(torchvision.models.vgg16_bn(pretrained=pretrained).features.children())
|
||||
model = model[:33] + model[34:43]
|
||||
self.model = torch.nn.Sequential(*model)
|
||||
|
||||
def forward(self, x):
|
||||
return self.model(x)
|
||||
|
||||
|
||||
class resnet(torch.nn.Module):
|
||||
def __init__(self, layers, pretrained=False):
|
||||
super(resnet, self).__init__()
|
||||
if layers == '18':
|
||||
model = torchvision.models.resnet18(pretrained=pretrained)
|
||||
elif layers == '34':
|
||||
model = torchvision.models.resnet34(pretrained=pretrained)
|
||||
elif layers == '50':
|
||||
model = torchvision.models.resnet50(pretrained=pretrained)
|
||||
elif layers == '101':
|
||||
model = torchvision.models.resnet101(pretrained=pretrained)
|
||||
elif layers == '152':
|
||||
model = torchvision.models.resnet152(pretrained=pretrained)
|
||||
elif layers == '50next':
|
||||
model = torchvision.models.resnext50_32x4d(pretrained=pretrained)
|
||||
elif layers == '101next':
|
||||
model = torchvision.models.resnext101_32x8d(pretrained=pretrained)
|
||||
elif layers == '50wide':
|
||||
model = torchvision.models.wide_resnet50_2(pretrained=pretrained)
|
||||
elif layers == '101wide':
|
||||
model = torchvision.models.wide_resnet101_2(pretrained=pretrained)
|
||||
else:
|
||||
raise NotImplementedError(f'Unknown ResNet backbone: {layers}')
|
||||
|
||||
self.conv1 = model.conv1
|
||||
self.bn1 = model.bn1
|
||||
self.relu = model.relu
|
||||
self.maxpool = model.maxpool
|
||||
self.layer1 = model.layer1
|
||||
self.layer2 = model.layer2
|
||||
self.layer3 = model.layer3
|
||||
self.layer4 = model.layer4
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
x = self.bn1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
x = self.layer1(x)
|
||||
x2 = self.layer2(x)
|
||||
x3 = self.layer3(x2)
|
||||
x4 = self.layer4(x3)
|
||||
return x2, x3, x4
|
||||
126
algorithms/lane_ufld/code/UFLD/model/model.py
Executable file
126
algorithms/lane_ufld/code/UFLD/model/model.py
Executable file
@@ -0,0 +1,126 @@
|
||||
import torch
|
||||
from model.backbone import build_backbone, get_backbone_spec
|
||||
import numpy as np
|
||||
|
||||
class conv_bn_relu(torch.nn.Module):
|
||||
def __init__(self,in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1,bias=False):
|
||||
super(conv_bn_relu,self).__init__()
|
||||
self.conv = torch.nn.Conv2d(in_channels,out_channels, kernel_size,
|
||||
stride = stride, padding = padding, dilation = dilation,bias = bias)
|
||||
self.bn = torch.nn.BatchNorm2d(out_channels)
|
||||
self.relu = torch.nn.ReLU()
|
||||
|
||||
def forward(self,x):
|
||||
x = self.conv(x)
|
||||
x = self.bn(x)
|
||||
x = self.relu(x)
|
||||
return x
|
||||
class parsingNet(torch.nn.Module):
|
||||
def __init__(self, size=(288, 800), pretrained=True, backbone='50', cls_dim=(37, 10, 4), use_aux=False):
|
||||
super(parsingNet, self).__init__()
|
||||
|
||||
self.size = size
|
||||
self.w = size[0]
|
||||
self.h = size[1]
|
||||
self.cls_dim = cls_dim # (num_gridding, num_cls_per_lane, num_of_lanes)
|
||||
# num_cls_per_lane is the number of row anchors
|
||||
self.use_aux = use_aux
|
||||
self.total_dim = np.prod(cls_dim)
|
||||
|
||||
# input : nchw,
|
||||
# output: (w+1) * sample_rows * 4
|
||||
spec = get_backbone_spec(backbone)
|
||||
c2, c3, c4 = spec['aux']
|
||||
pool_in = spec['pool_in']
|
||||
|
||||
self.model = build_backbone(backbone, pretrained=pretrained)
|
||||
|
||||
if self.use_aux:
|
||||
self.aux_header2 = torch.nn.Sequential(
|
||||
conv_bn_relu(c2, 128, kernel_size=3, stride=1, padding=1),
|
||||
conv_bn_relu(128, 128, 3, padding=1),
|
||||
conv_bn_relu(128, 128, 3, padding=1),
|
||||
conv_bn_relu(128, 128, 3, padding=1),
|
||||
)
|
||||
self.aux_header3 = torch.nn.Sequential(
|
||||
conv_bn_relu(c3, 128, kernel_size=3, stride=1, padding=1),
|
||||
conv_bn_relu(128, 128, 3, padding=1),
|
||||
conv_bn_relu(128, 128, 3, padding=1),
|
||||
)
|
||||
self.aux_header4 = torch.nn.Sequential(
|
||||
conv_bn_relu(c4, 128, kernel_size=3, stride=1, padding=1),
|
||||
conv_bn_relu(128, 128, 3, padding=1),
|
||||
)
|
||||
self.aux_combine = torch.nn.Sequential(
|
||||
conv_bn_relu(384, 256, 3,padding=2,dilation=2),
|
||||
conv_bn_relu(256, 128, 3,padding=2,dilation=2),
|
||||
conv_bn_relu(128, 128, 3,padding=2,dilation=2),
|
||||
conv_bn_relu(128, 128, 3,padding=4,dilation=4),
|
||||
torch.nn.Conv2d(128, cls_dim[-1] + 1, 1)
|
||||
# output : n, num_of_lanes+1, h, w
|
||||
)
|
||||
initialize_weights(self.aux_header2,self.aux_header3,self.aux_header4,self.aux_combine)
|
||||
|
||||
self.cls = torch.nn.Sequential(
|
||||
torch.nn.Linear(1800, 2048),
|
||||
torch.nn.ReLU(),
|
||||
torch.nn.Linear(2048, self.total_dim),
|
||||
)
|
||||
|
||||
self.pool = torch.nn.Conv2d(pool_in, 8, 1)
|
||||
# 1/32,2048 channel
|
||||
# 288,800 -> 9,40,2048
|
||||
# (w+1) * sample_rows * 4
|
||||
# 37 * 10 * 4
|
||||
initialize_weights(self.cls)
|
||||
|
||||
def forward(self, x):
|
||||
# n c h w - > n 2048 sh sw
|
||||
# -> n 2048
|
||||
x2,x3,fea = self.model(x)
|
||||
if self.use_aux:
|
||||
x2 = self.aux_header2(x2)
|
||||
x3 = self.aux_header3(x3)
|
||||
x3 = torch.nn.functional.interpolate(x3,scale_factor = 2,mode='bilinear')
|
||||
x4 = self.aux_header4(fea)
|
||||
x4 = torch.nn.functional.interpolate(x4,scale_factor = 4,mode='bilinear')
|
||||
aux_seg = torch.cat([x2,x3,x4],dim=1)
|
||||
aux_seg = self.aux_combine(aux_seg)
|
||||
else:
|
||||
aux_seg = None
|
||||
|
||||
fea = self.pool(fea).view(-1, 1800)
|
||||
|
||||
group_cls = self.cls(fea).view(-1, *self.cls_dim)
|
||||
|
||||
if self.use_aux:
|
||||
return group_cls, aux_seg
|
||||
|
||||
return group_cls
|
||||
|
||||
|
||||
def initialize_weights(*models):
|
||||
for model in models:
|
||||
real_init_weights(model)
|
||||
|
||||
|
||||
def real_init_weights(m):
|
||||
|
||||
if isinstance(m, list):
|
||||
for mini_m in m:
|
||||
real_init_weights(mini_m)
|
||||
else:
|
||||
if isinstance(m, torch.nn.Conv2d):
|
||||
torch.nn.init.kaiming_normal_(m.weight, nonlinearity='relu')
|
||||
if m.bias is not None:
|
||||
torch.nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, torch.nn.Linear):
|
||||
m.weight.data.normal_(0.0, std=0.01)
|
||||
elif isinstance(m, torch.nn.BatchNorm2d):
|
||||
torch.nn.init.constant_(m.weight, 1)
|
||||
torch.nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m,torch.nn.Module):
|
||||
for mini_m in m.children():
|
||||
real_init_weights(mini_m)
|
||||
else:
|
||||
print('unkonwn module', m)
|
||||
120
algorithms/lane_ufld/code/UFLD/model/model0.py
Executable file
120
algorithms/lane_ufld/code/UFLD/model/model0.py
Executable file
@@ -0,0 +1,120 @@
|
||||
import torch
|
||||
from model.backbone import resnet
|
||||
import numpy as np
|
||||
|
||||
class conv_bn_relu(torch.nn.Module):
|
||||
def __init__(self,in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1,bias=False):
|
||||
super(conv_bn_relu,self).__init__()
|
||||
self.conv = torch.nn.Conv2d(in_channels,out_channels, kernel_size,
|
||||
stride = stride, padding = padding, dilation = dilation,bias = bias)
|
||||
self.bn = torch.nn.BatchNorm2d(out_channels)
|
||||
self.relu = torch.nn.ReLU()
|
||||
|
||||
def forward(self,x):
|
||||
x = self.conv(x)
|
||||
x = self.bn(x)
|
||||
x = self.relu(x)
|
||||
return x
|
||||
class parsingNet(torch.nn.Module):
|
||||
def __init__(self, size=(288, 800), pretrained=True, backbone='50', cls_dim=(37, 10, 4), use_aux=False):
|
||||
super(parsingNet, self).__init__()
|
||||
|
||||
self.size = size
|
||||
self.w = size[0]
|
||||
self.h = size[1]
|
||||
self.cls_dim = cls_dim # (num_gridding, num_cls_per_lane, num_of_lanes)
|
||||
# num_cls_per_lane is the number of row anchors
|
||||
self.use_aux = use_aux
|
||||
self.total_dim = np.prod(cls_dim)
|
||||
|
||||
# input : nchw,
|
||||
# output: (w+1) * sample_rows * 4
|
||||
self.model = resnet(backbone, pretrained=pretrained)
|
||||
|
||||
if self.use_aux:
|
||||
self.aux_header2 = torch.nn.Sequential(
|
||||
conv_bn_relu(128, 128, kernel_size=3, stride=1, padding=1) if backbone in ['34','18'] else conv_bn_relu(512, 128, kernel_size=3, stride=1, padding=1),
|
||||
conv_bn_relu(128,128,3,padding=1),
|
||||
conv_bn_relu(128,128,3,padding=1),
|
||||
conv_bn_relu(128,128,3,padding=1),
|
||||
)
|
||||
self.aux_header3 = torch.nn.Sequential(
|
||||
conv_bn_relu(256, 128, kernel_size=3, stride=1, padding=1) if backbone in ['34','18'] else conv_bn_relu(1024, 128, kernel_size=3, stride=1, padding=1),
|
||||
conv_bn_relu(128,128,3,padding=1),
|
||||
conv_bn_relu(128,128,3,padding=1),
|
||||
)
|
||||
self.aux_header4 = torch.nn.Sequential(
|
||||
conv_bn_relu(512, 128, kernel_size=3, stride=1, padding=1) if backbone in ['34','18'] else conv_bn_relu(2048, 128, kernel_size=3, stride=1, padding=1),
|
||||
conv_bn_relu(128,128,3,padding=1),
|
||||
)
|
||||
self.aux_combine = torch.nn.Sequential(
|
||||
conv_bn_relu(384, 256, 3,padding=2,dilation=2),
|
||||
conv_bn_relu(256, 128, 3,padding=2,dilation=2),
|
||||
conv_bn_relu(128, 128, 3,padding=2,dilation=2),
|
||||
conv_bn_relu(128, 128, 3,padding=4,dilation=4),
|
||||
torch.nn.Conv2d(128, cls_dim[-1] + 1,1)
|
||||
# output : n, num_of_lanes+1, h, w
|
||||
)
|
||||
initialize_weights(self.aux_header2,self.aux_header3,self.aux_header4,self.aux_combine)
|
||||
|
||||
self.cls = torch.nn.Sequential(
|
||||
torch.nn.Linear(1800, 2048),
|
||||
torch.nn.ReLU(),
|
||||
torch.nn.Linear(2048, self.total_dim),
|
||||
)
|
||||
|
||||
self.pool = torch.nn.Conv2d(512,8,1) if backbone in ['34','18'] else torch.nn.Conv2d(2048,8,1)
|
||||
# 1/32,2048 channel
|
||||
# 288,800 -> 9,40,2048
|
||||
# (w+1) * sample_rows * 4
|
||||
# 37 * 10 * 4
|
||||
initialize_weights(self.cls)
|
||||
|
||||
def forward(self, x):
|
||||
# n c h w - > n 2048 sh sw
|
||||
# -> n 2048
|
||||
x2,x3,fea = self.model(x)
|
||||
if self.use_aux:
|
||||
x2 = self.aux_header2(x2)
|
||||
x3 = self.aux_header3(x3)
|
||||
x3 = torch.nn.functional.interpolate(x3,scale_factor = 2,mode='bilinear')
|
||||
x4 = self.aux_header4(fea)
|
||||
x4 = torch.nn.functional.interpolate(x4,scale_factor = 4,mode='bilinear')
|
||||
aux_seg = torch.cat([x2,x3,x4],dim=1)
|
||||
aux_seg = self.aux_combine(aux_seg)
|
||||
else:
|
||||
aux_seg = None
|
||||
|
||||
fea = self.pool(fea).view(-1, 1800)
|
||||
|
||||
group_cls = self.cls(fea).view(-1, *self.cls_dim)
|
||||
|
||||
if self.use_aux:
|
||||
return group_cls, aux_seg
|
||||
|
||||
return group_cls
|
||||
|
||||
|
||||
def initialize_weights(*models):
|
||||
for model in models:
|
||||
real_init_weights(model)
|
||||
def real_init_weights(m):
|
||||
|
||||
if isinstance(m, list):
|
||||
for mini_m in m:
|
||||
real_init_weights(mini_m)
|
||||
else:
|
||||
if isinstance(m, torch.nn.Conv2d):
|
||||
torch.nn.init.kaiming_normal_(m.weight, nonlinearity='relu')
|
||||
if m.bias is not None:
|
||||
torch.nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, torch.nn.Linear):
|
||||
m.weight.data.normal_(0.0, std=0.01)
|
||||
elif isinstance(m, torch.nn.BatchNorm2d):
|
||||
torch.nn.init.constant_(m.weight, 1)
|
||||
torch.nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m,torch.nn.Module):
|
||||
for mini_m in m.children():
|
||||
real_init_weights(mini_m)
|
||||
else:
|
||||
print('unkonwn module', m)
|
||||
296
algorithms/lane_ufld/code/UFLD/model/vovnet.py
Normal file
296
algorithms/lane_ufld/code/UFLD/model/vovnet.py
Normal file
@@ -0,0 +1,296 @@
|
||||
# VoVNet backbone (OSA + eSE), adapted from vovnet-detectron2 (no detectron2 dependency).
|
||||
# Reference: BK2/archive/vovnet-detectron2-master/vovnet/vovnet.py
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
VoVNet19_slim_dw_eSE = {
|
||||
'stem': [64, 64, 64],
|
||||
'stage_conv_ch': [64, 80, 96, 112],
|
||||
'stage_out_ch': [112, 256, 384, 512],
|
||||
'layer_per_block': 3,
|
||||
'block_per_stage': [1, 1, 1, 1],
|
||||
'eSE': True,
|
||||
'dw': True,
|
||||
}
|
||||
|
||||
VoVNet19_dw_eSE = {
|
||||
'stem': [64, 64, 64],
|
||||
'stage_conv_ch': [128, 160, 192, 224],
|
||||
'stage_out_ch': [256, 512, 768, 1024],
|
||||
'layer_per_block': 3,
|
||||
'block_per_stage': [1, 1, 1, 1],
|
||||
'eSE': True,
|
||||
'dw': True,
|
||||
}
|
||||
|
||||
VoVNet19_slim_eSE = {
|
||||
'stem': [64, 64, 128],
|
||||
'stage_conv_ch': [64, 80, 96, 112],
|
||||
'stage_out_ch': [112, 256, 384, 512],
|
||||
'layer_per_block': 3,
|
||||
'block_per_stage': [1, 1, 1, 1],
|
||||
'eSE': True,
|
||||
'dw': False,
|
||||
}
|
||||
|
||||
VoVNet19_eSE = {
|
||||
'stem': [64, 64, 128],
|
||||
'stage_conv_ch': [128, 160, 192, 224],
|
||||
'stage_out_ch': [256, 512, 768, 1024],
|
||||
'layer_per_block': 3,
|
||||
'block_per_stage': [1, 1, 1, 1],
|
||||
'eSE': True,
|
||||
'dw': False,
|
||||
}
|
||||
|
||||
VoVNet39_eSE = {
|
||||
'stem': [64, 64, 128],
|
||||
'stage_conv_ch': [128, 160, 192, 224],
|
||||
'stage_out_ch': [256, 512, 768, 1024],
|
||||
'layer_per_block': 5,
|
||||
'block_per_stage': [1, 1, 2, 2],
|
||||
'eSE': True,
|
||||
'dw': False,
|
||||
}
|
||||
|
||||
VoVNet57_eSE = {
|
||||
'stem': [64, 64, 128],
|
||||
'stage_conv_ch': [128, 160, 192, 224],
|
||||
'stage_out_ch': [256, 512, 768, 1024],
|
||||
'layer_per_block': 5,
|
||||
'block_per_stage': [1, 1, 4, 3],
|
||||
'eSE': True,
|
||||
'dw': False,
|
||||
}
|
||||
|
||||
VoVNet99_eSE = {
|
||||
'stem': [64, 64, 128],
|
||||
'stage_conv_ch': [128, 160, 192, 224],
|
||||
'stage_out_ch': [256, 512, 768, 1024],
|
||||
'layer_per_block': 5,
|
||||
'block_per_stage': [1, 3, 9, 3],
|
||||
'eSE': True,
|
||||
'dw': False,
|
||||
}
|
||||
|
||||
STAGE_SPECS = {
|
||||
'V-19-slim-dw-eSE': VoVNet19_slim_dw_eSE,
|
||||
'V-19-dw-eSE': VoVNet19_dw_eSE,
|
||||
'V-19-slim-eSE': VoVNet19_slim_eSE,
|
||||
'V-19-eSE': VoVNet19_eSE,
|
||||
'V-39-eSE': VoVNet39_eSE,
|
||||
'V-57-eSE': VoVNet57_eSE,
|
||||
'V-99-eSE': VoVNet99_eSE,
|
||||
}
|
||||
|
||||
# Short names used in UFLD configs (backbone='vov19slim', ...)
|
||||
VOVNET_ALIASES = {
|
||||
'vov19slim_dw': 'V-19-slim-dw-eSE',
|
||||
'vov19_dw': 'V-19-dw-eSE',
|
||||
'vov19slim': 'V-19-slim-eSE',
|
||||
'vov19': 'V-19-eSE',
|
||||
'vov39': 'V-39-eSE',
|
||||
'vov57': 'V-57-eSE',
|
||||
'vov99': 'V-99-eSE',
|
||||
}
|
||||
|
||||
|
||||
def _bn(ch):
|
||||
return nn.BatchNorm2d(ch)
|
||||
|
||||
|
||||
def dw_conv3x3(in_channels, out_channels, module_name, postfix, stride=1, kernel_size=3, padding=1):
|
||||
return [
|
||||
(f'{module_name}_{postfix}/dw', nn.Conv2d(
|
||||
in_channels, out_channels, kernel_size=kernel_size, stride=stride,
|
||||
padding=padding, groups=out_channels, bias=False)),
|
||||
(f'{module_name}_{postfix}/pw', nn.Conv2d(
|
||||
in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False)),
|
||||
(f'{module_name}_{postfix}/bn', _bn(out_channels)),
|
||||
(f'{module_name}_{postfix}/relu', nn.ReLU(inplace=True)),
|
||||
]
|
||||
|
||||
|
||||
def conv3x3(in_channels, out_channels, module_name, postfix, stride=1, groups=1,
|
||||
kernel_size=3, padding=1):
|
||||
return [
|
||||
(f'{module_name}_{postfix}/conv', nn.Conv2d(
|
||||
in_channels, out_channels, kernel_size=kernel_size, stride=stride,
|
||||
padding=padding, groups=groups, bias=False)),
|
||||
(f'{module_name}_{postfix}/bn', _bn(out_channels)),
|
||||
(f'{module_name}_{postfix}/relu', nn.ReLU(inplace=True)),
|
||||
]
|
||||
|
||||
|
||||
def conv1x1(in_channels, out_channels, module_name, postfix, stride=1, groups=1,
|
||||
kernel_size=1, padding=0):
|
||||
return [
|
||||
(f'{module_name}_{postfix}/conv', nn.Conv2d(
|
||||
in_channels, out_channels, kernel_size=kernel_size, stride=stride,
|
||||
padding=padding, groups=groups, bias=False)),
|
||||
(f'{module_name}_{postfix}/bn', _bn(out_channels)),
|
||||
(f'{module_name}_{postfix}/relu', nn.ReLU(inplace=True)),
|
||||
]
|
||||
|
||||
|
||||
class Hsigmoid(nn.Module):
|
||||
def __init__(self, inplace=True):
|
||||
super().__init__()
|
||||
self.inplace = inplace
|
||||
|
||||
def forward(self, x):
|
||||
return F.relu6(x + 3.0, inplace=self.inplace) / 6.0
|
||||
|
||||
|
||||
class eSEModule(nn.Module):
|
||||
def __init__(self, channel):
|
||||
super().__init__()
|
||||
self.avg_pool = nn.AdaptiveAvgPool2d(1)
|
||||
self.fc = nn.Conv2d(channel, channel, kernel_size=1, padding=0)
|
||||
self.hsigmoid = Hsigmoid()
|
||||
|
||||
def forward(self, x):
|
||||
return x * self.hsigmoid(self.fc(self.avg_pool(x)))
|
||||
|
||||
|
||||
class _OSA_module(nn.Module):
|
||||
def __init__(self, in_ch, stage_ch, concat_ch, layer_per_block, module_name,
|
||||
identity=False, depthwise=False):
|
||||
super().__init__()
|
||||
self.identity = identity
|
||||
self.depthwise = depthwise
|
||||
self.is_reduced = False
|
||||
self.layers = nn.ModuleList()
|
||||
in_channel = in_ch
|
||||
if self.depthwise and in_channel != stage_ch:
|
||||
self.is_reduced = True
|
||||
self.conv_reduction = nn.Sequential(
|
||||
OrderedDict(conv1x1(in_channel, stage_ch, f'{module_name}_reduction', '0')))
|
||||
for i in range(layer_per_block):
|
||||
if self.depthwise:
|
||||
self.layers.append(nn.Sequential(OrderedDict(
|
||||
dw_conv3x3(stage_ch, stage_ch, module_name, str(i)))))
|
||||
else:
|
||||
self.layers.append(nn.Sequential(OrderedDict(
|
||||
conv3x3(in_channel, stage_ch, module_name, str(i)))))
|
||||
in_channel = stage_ch
|
||||
|
||||
in_channel = in_ch + layer_per_block * stage_ch
|
||||
self.concat = nn.Sequential(OrderedDict(
|
||||
conv1x1(in_channel, concat_ch, module_name, 'concat')))
|
||||
self.ese = eSEModule(concat_ch)
|
||||
|
||||
def forward(self, x):
|
||||
identity_feat = x
|
||||
output = [x]
|
||||
if self.depthwise and self.is_reduced:
|
||||
x = self.conv_reduction(x)
|
||||
for layer in self.layers:
|
||||
x = layer(x)
|
||||
output.append(x)
|
||||
x = torch.cat(output, dim=1)
|
||||
xt = self.ese(self.concat(x))
|
||||
if self.identity:
|
||||
xt = xt + identity_feat
|
||||
return xt
|
||||
|
||||
|
||||
class _OSA_stage(nn.Sequential):
|
||||
def __init__(self, in_ch, stage_ch, concat_ch, block_per_stage, layer_per_block,
|
||||
stage_num, depthwise=False):
|
||||
super().__init__()
|
||||
if stage_num != 2:
|
||||
self.add_module('Pooling', nn.MaxPool2d(kernel_size=3, stride=2, ceil_mode=True))
|
||||
module_name = f'OSA{stage_num}_1'
|
||||
self.add_module(module_name, _OSA_module(
|
||||
in_ch, stage_ch, concat_ch, layer_per_block, module_name, depthwise=depthwise))
|
||||
for i in range(block_per_stage - 1):
|
||||
module_name = f'OSA{stage_num}_{i + 2}'
|
||||
self.add_module(module_name, _OSA_module(
|
||||
concat_ch, stage_ch, concat_ch, layer_per_block, module_name,
|
||||
identity=True, depthwise=depthwise))
|
||||
|
||||
|
||||
class VoVNetBody(nn.Module):
|
||||
"""VoVNet stages; forward returns (stage3, stage4, stage5) at strides 8/16/32."""
|
||||
|
||||
def __init__(self, variant='V-19-slim-eSE', input_ch=3):
|
||||
super().__init__()
|
||||
if variant in VOVNET_ALIASES:
|
||||
variant = VOVNET_ALIASES[variant]
|
||||
if variant not in STAGE_SPECS:
|
||||
raise KeyError(f'Unknown VoVNet variant {variant!r}, choose from {list(STAGE_SPECS)}')
|
||||
self.variant = variant
|
||||
spec = STAGE_SPECS[variant]
|
||||
|
||||
stem_ch = spec['stem']
|
||||
config_stage_ch = spec['stage_conv_ch']
|
||||
config_concat_ch = spec['stage_out_ch']
|
||||
block_per_stage = spec['block_per_stage']
|
||||
layer_per_block = spec['layer_per_block']
|
||||
depthwise = spec['dw']
|
||||
|
||||
conv_type = dw_conv3x3 if depthwise else conv3x3
|
||||
stem = conv3x3(input_ch, stem_ch[0], 'stem', '1', stride=2)
|
||||
stem += conv_type(stem_ch[0], stem_ch[1], 'stem', '2', stride=1)
|
||||
stem += conv_type(stem_ch[1], stem_ch[2], 'stem', '3', stride=2)
|
||||
self.stem = nn.Sequential(OrderedDict(stem))
|
||||
|
||||
in_ch_list = [stem_ch[2]] + config_concat_ch[:-1]
|
||||
self.stage2 = _OSA_stage(
|
||||
in_ch_list[0], config_stage_ch[0], config_concat_ch[0],
|
||||
block_per_stage[0], layer_per_block, 2, depthwise=depthwise)
|
||||
self.stage3 = _OSA_stage(
|
||||
in_ch_list[1], config_stage_ch[1], config_concat_ch[1],
|
||||
block_per_stage[1], layer_per_block, 3, depthwise=depthwise)
|
||||
self.stage4 = _OSA_stage(
|
||||
in_ch_list[2], config_stage_ch[2], config_concat_ch[2],
|
||||
block_per_stage[2], layer_per_block, 4, depthwise=depthwise)
|
||||
self.stage5 = _OSA_stage(
|
||||
in_ch_list[3], config_stage_ch[3], config_concat_ch[3],
|
||||
block_per_stage[3], layer_per_block, 5, depthwise=depthwise)
|
||||
|
||||
self.out_channels = {
|
||||
'c3': config_concat_ch[1],
|
||||
'c4': config_concat_ch[2],
|
||||
'c5': config_concat_ch[3],
|
||||
}
|
||||
self._initialize_weights()
|
||||
|
||||
def _initialize_weights(self):
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.kaiming_normal_(m.weight)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.stem(x)
|
||||
x = self.stage2(x)
|
||||
c3 = self.stage3(x)
|
||||
c4 = self.stage4(c3)
|
||||
c5 = self.stage5(c4)
|
||||
return c3, c4, c5
|
||||
|
||||
|
||||
class vovnet(nn.Module):
|
||||
"""UFLD-compatible wrapper (same interface as resnet: x2, x3, x4)."""
|
||||
|
||||
def __init__(self, variant='vov19slim', pretrained=False):
|
||||
super().__init__()
|
||||
if pretrained:
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'VoVNet has no torchvision pretrained weights in UFLD; '
|
||||
'train from scratch or load a custom checkpoint.',
|
||||
UserWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = variant if variant in VOVNET_ALIASES else variant
|
||||
self.body = VoVNetBody(key)
|
||||
self.variant = self.body.variant
|
||||
|
||||
def forward(self, x):
|
||||
return self.body(x)
|
||||
Reference in New Issue
Block a user