2020-03-29 15:54:03 +02:00
# This file is not used by the tracking application and currently outdated
2020-01-01 13:55:01 +01:00
import torch
import torch . nn as nn
class DSConv2d ( nn . Module ) :
def __init__ ( self , in_planes , out_planes , kernels_per_layer = 4 , groups = 1 ) :
super ( DSConv2d , self ) . __init__ ( )
self . conv = nn . Sequential (
nn . Conv2d ( in_planes , in_planes * kernels_per_layer , kernel_size = 3 , padding = 1 , groups = in_planes ) ,
nn . Conv2d ( in_planes * kernels_per_layer , out_planes , kernel_size = 1 , groups = groups )
)
def forward ( self , x ) :
x = self . conv ( x )
return x
class UNetUp ( nn . Module ) :
def __init__ ( self , in_channels , residual_in_channels , out_channels , size ) :
super ( UNetUp , self ) . __init__ ( )
self . up = nn . Upsample ( size = size , mode = ' bilinear ' , align_corners = True )
self . conv = DSConv2d ( in_channels + residual_in_channels , out_channels , 1 , 1 )
def forward ( self , x1 , x2 ) :
x1 = self . up ( x1 )
x = torch . cat ( [ x2 , x1 ] , dim = 1 )
x = self . conv ( x )
return x
# Copy of torchvision ShuffleNetV2 for type annotation
def channel_shuffle ( x , groups : int ) :
batchsize , num_channels , height , width = x . data . size ( )
channels_per_group = num_channels / / groups
# reshape
x = x . view ( batchsize , groups ,
channels_per_group , height , width )
x = torch . transpose ( x , 1 , 2 ) . contiguous ( )
# flatten
x = x . view ( batchsize , - 1 , height , width )
return x
class InvertedResidual ( nn . Module ) :
def __init__ ( self , inp , oup , stride ) :
super ( InvertedResidual , self ) . __init__ ( )
if not ( 1 < = stride < = 3 ) :
raise ValueError ( ' illegal stride value ' )
self . stride = stride
branch_features = oup / / 2
assert ( self . stride != 1 ) or ( inp == branch_features << 1 )
self . branch1 = nn . Sequential (
self . depthwise_conv ( inp , inp , kernel_size = 3 , stride = self . stride , padding = 1 ) ,
nn . BatchNorm2d ( inp ) ,
nn . Conv2d ( inp , branch_features , kernel_size = 1 , stride = 1 , padding = 0 , bias = False ) ,
nn . BatchNorm2d ( branch_features ) ,
nn . ReLU ( inplace = True ) ,
)
self . branch2 = nn . Sequential (
nn . Conv2d ( inp if ( self . stride > 1 ) else branch_features ,
branch_features , kernel_size = 1 , stride = 1 , padding = 0 , bias = False ) ,
nn . BatchNorm2d ( branch_features ) ,
nn . ReLU ( inplace = True ) ,
self . depthwise_conv ( branch_features , branch_features , kernel_size = 3 , stride = self . stride , padding = 1 ) ,
nn . BatchNorm2d ( branch_features ) ,
nn . Conv2d ( branch_features , branch_features , kernel_size = 1 , stride = 1 , padding = 0 , bias = False ) ,
nn . BatchNorm2d ( branch_features ) ,
nn . ReLU ( inplace = True ) ,
)
@staticmethod
def depthwise_conv ( i , o , kernel_size , stride = 1 , padding = 0 , bias = False ) :
return nn . Conv2d ( i , o , kernel_size , stride , padding , bias = bias , groups = i )
def forward ( self , x ) :
if self . stride == 1 :
x1 , x2 = x . chunk ( 2 , dim = 1 )
out = torch . cat ( ( x1 , self . branch2 ( x2 ) ) , dim = 1 )
else :
out = torch . cat ( ( self . branch1 ( x ) , self . branch2 ( x ) ) , dim = 1 )
out = channel_shuffle ( out , 2 )
return out
class ShuffleNetV2 ( nn . Module ) :
def __init__ ( self , stages_repeats , stages_out_channels , num_classes = 1000 ) :
super ( ShuffleNetV2 , self ) . __init__ ( )
if len ( stages_repeats ) != 3 :
raise ValueError ( ' expected stages_repeats as list of 3 positive ints ' )
if len ( stages_out_channels ) != 5 :
raise ValueError ( ' expected stages_out_channels as list of 5 positive ints ' )
self . _stage_out_channels = stages_out_channels
input_channels = 3
output_channels = self . _stage_out_channels [ 0 ]
self . conv1 = nn . Sequential (
nn . Conv2d ( input_channels , output_channels , 3 , 2 , 1 , bias = False ) ,
nn . BatchNorm2d ( output_channels ) ,
nn . ReLU ( inplace = True ) ,
)
input_channels = output_channels
self . maxpool = nn . MaxPool2d ( kernel_size = 3 , stride = 2 , padding = 1 )
stage_names = [ ' stage {} ' . format ( i ) for i in [ 2 , 3 , 4 ] ]
for name , repeats , output_channels in zip (
stage_names , stages_repeats , self . _stage_out_channels [ 1 : ] ) :
seq = [ InvertedResidual ( input_channels , output_channels , 2 ) ]
for i in range ( repeats - 1 ) :
seq . append ( InvertedResidual ( output_channels , output_channels , 1 ) )
setattr ( self , name , nn . Sequential ( * seq ) )
input_channels = output_channels
output_channels = self . _stage_out_channels [ - 1 ]
self . conv5 = nn . Sequential (
nn . Conv2d ( input_channels , output_channels , 1 , 1 , 0 , bias = False ) ,
nn . BatchNorm2d ( output_channels ) ,
nn . ReLU ( inplace = True ) ,
)
self . fc = nn . Linear ( output_channels , num_classes )
def forward ( self , x ) :
x = self . conv1 ( x )
x = self . maxpool ( x )
x = self . stage2 ( x )
x = self . stage3 ( x )
x = self . stage4 ( x )
x = self . conv5 ( x )
x = x . mean ( [ 2 , 3 ] ) # globalpool
x = self . fc ( x )
return x
# Facial landmark detection model
class OpenSeeNetSNV2 ( ShuffleNetV2 ) :
def __init__ ( self ) :
super ( OpenSeeNetSNV2 , self ) . __init__ ( [ 4 , 8 , 4 ] , [ 24 , 116 , 232 , 464 , 1024 ] )
self . up1 = UNetUp ( 1024 , 232 , 256 , ( 14 , 14 ) )
self . up2 = UNetUp ( 256 , 116 , 198 * 1 , ( 28 , 28 ) )
self . group = DSConv2d ( 198 * 1 , 198 , kernels_per_layer = 4 , groups = 3 )
def _forward_impl ( self , x ) :
x = self . conv1 ( x )
x = self . maxpool ( x )
x = self . stage2 ( x )
r2 = x
x = self . stage3 ( x )
r3 = x
x = self . stage4 ( x )
x = self . conv5 ( x )
x = self . up1 ( x , r3 )
x = self . up2 ( x , r2 )
x = self . group ( x )
return x
def forward ( self , x ) :
return self . _forward_impl ( x )
# Adaptive Wing Loss with offset layers and emphasis for eyes and eyebrows with 66 landmark points
def AdapWingLoss ( pre_hm , gt_hm ) :
# pre_hm = pre_hm.to('cpu')
# gt_hm = gt_hm.to('cpu')
theta = 0.5
alpha = 2.1
w = 14
e = 1
A = w * ( 1 / ( 1 + torch . pow ( theta / e , alpha - gt_hm ) ) ) * ( alpha - gt_hm ) * torch . pow ( theta / e , alpha - gt_hm - 1 ) * ( 1 / e )
C = ( theta * A - w * torch . log ( 1 + torch . pow ( theta / e , alpha - gt_hm ) ) )
batch_size = gt_hm . size ( ) [ 0 ]
hm_num = gt_hm . size ( ) [ 1 ]
mask = torch . zeros_like ( gt_hm )
# W = 10
kernel = cv2 . getStructuringElement ( cv2 . MORPH_RECT , ( 3 , 3 ) )
first = True
first_mask = None
for i in range ( batch_size ) :
img_list = [ ]
for j in range ( hm_num / / 3 ) :
hm = np . round ( gt_hm [ i ] [ j ] . cpu ( ) . numpy ( ) * 255 )
img_list . append ( hm )
img_merge = cv2 . merge ( img_list )
img_dilate = cv2 . morphologyEx ( img_merge , cv2 . MORPH_DILATE , kernel )
img_dilate [ img_dilate < 51 ] = 1 # 0*W+1
img_dilate [ img_dilate > = 51 ] = 11 # 1*W+1
img_dilate = np . array ( img_dilate , dtype = np . int )
img_dilate = img_dilate . transpose ( 2 , 0 , 1 )
dilated = torch . from_numpy ( img_dilate ) . float ( )
if first :
first_mask = dilated
first = False
dilated [ 17 : 27 ] * = 1.2
dilated [ 17 ] * = 1.3
dilated [ 18 ] * = 1.4
dilated [ 25 ] * = 1.4
dilated [ 26 ] * = 1.3
dilated [ 36 : 47 ] * = 2.5
mask [ i ] = torch . cat ( [ dilated , dilated , dilated ] , 0 )
diff_hm = torch . abs ( gt_hm - pre_hm )
AWingLoss = A * diff_hm - C
idx = diff_hm < theta
AWingLoss [ idx ] = w * torch . log ( 1 + torch . pow ( diff_hm / e , alpha - gt_hm ) ) [ idx ]
AWingLoss * = mask
sum_loss = torch . sum ( AWingLoss )
all_pixel = torch . sum ( mask )
mean_loss = sum_loss / all_pixel
return first_mask . detach ( ) , mean_loss
# The fast model runs on grayscale 112x112 using the ShuffleNet V2 0.5x configuration with input_channels=1. It was trained using AWL, but without the additional weighting for certain features
.