|
import torch |
|
import torch.nn as nn |
|
from WT import DWT, IWT |
|
|
|
|
|
def conv3x3(in_chn, out_chn, bias=True): |
|
layer = nn.Conv2d(in_chn, out_chn, kernel_size=3, stride=1, padding=1, bias=bias) |
|
return layer |
|
|
|
def conv(in_channels, out_channels, kernel_size, bias=False, stride=1): |
|
return nn.Conv2d( |
|
in_channels, out_channels, kernel_size, |
|
padding=(kernel_size // 2), bias=bias, stride=stride) |
|
|
|
def bili_resize(factor): |
|
return nn.Upsample(scale_factor=factor, mode='bilinear', align_corners=False) |
|
|
|
|
|
class UNetConvBlock(nn.Module): |
|
def __init__(self, in_size, out_size, downsample): |
|
super(UNetConvBlock, self).__init__() |
|
self.downsample = downsample |
|
self.body = [HWB(n_feat=in_size, o_feat=in_size, kernel_size=3, reduction=16, bias=False, act=nn.PReLU())] |
|
self.body = nn.Sequential(*self.body) |
|
|
|
if downsample: |
|
self.downsample = PS_down(out_size, out_size, downscale=2) |
|
|
|
self.tail = nn.Conv2d(in_size, out_size, kernel_size=1) |
|
|
|
def forward(self, x): |
|
out = self.body(x) |
|
out = self.tail(out) |
|
if self.downsample: |
|
out_down = self.downsample(out) |
|
return out_down, out |
|
else: |
|
return out |
|
|
|
class UNetUpBlock(nn.Module): |
|
def __init__(self, in_size, out_size): |
|
super(UNetUpBlock, self).__init__() |
|
self.up = PS_up(in_size, out_size, upscale=2) |
|
self.conv_block = UNetConvBlock(in_size, out_size, downsample=False) |
|
|
|
def forward(self, x, bridge): |
|
up = self.up(x) |
|
out = torch.cat([up, bridge], dim=1) |
|
out = self.conv_block(out) |
|
return out |
|
|
|
|
|
class PS_down(nn.Module): |
|
def __init__(self, in_size, out_size, downscale): |
|
super(PS_down, self).__init__() |
|
self.UnPS = nn.PixelUnshuffle(downscale) |
|
self.conv1 = nn.Conv2d((downscale**2) * in_size, out_size, 1, 1, 0) |
|
|
|
def forward(self, x): |
|
x = self.UnPS(x) |
|
x = self.conv1(x) |
|
return x |
|
|
|
class PS_up(nn.Module): |
|
def __init__(self, in_size, out_size, upscale): |
|
super(PS_up, self).__init__() |
|
|
|
self.PS = nn.PixelShuffle(upscale) |
|
self.conv1 = nn.Conv2d(in_size//(upscale**2), out_size, 1, 1, 0) |
|
|
|
def forward(self, x): |
|
x = self.PS(x) |
|
x = self.conv1(x) |
|
return x |
|
|
|
|
|
class SKFF(nn.Module): |
|
def __init__(self, in_channels, height=3, reduction=8, bias=False): |
|
super(SKFF, self).__init__() |
|
|
|
self.height = height |
|
d = max(int(in_channels / reduction), 4) |
|
|
|
self.avg_pool = nn.AdaptiveAvgPool2d(1) |
|
self.conv_du = nn.Sequential(nn.Conv2d(in_channels, d, 1, padding=0, bias=bias), nn.PReLU()) |
|
|
|
self.fcs = nn.ModuleList([]) |
|
for i in range(self.height): |
|
self.fcs.append(nn.Conv2d(d, in_channels, kernel_size=1, stride=1, bias=bias)) |
|
|
|
self.softmax = nn.Softmax(dim=1) |
|
|
|
def forward(self, inp_feats): |
|
batch_size, n_feats, H, W = inp_feats[1].shape |
|
|
|
inp_feats = torch.cat(inp_feats, dim=1) |
|
inp_feats = inp_feats.view(batch_size, self.height, n_feats, inp_feats.shape[2], inp_feats.shape[3]) |
|
|
|
feats_U = torch.sum(inp_feats, dim=1) |
|
feats_S = self.avg_pool(feats_U) |
|
feats_Z = self.conv_du(feats_S) |
|
|
|
attention_vectors = [fc(feats_Z) for fc in self.fcs] |
|
attention_vectors = torch.cat(attention_vectors, dim=1) |
|
attention_vectors = attention_vectors.view(batch_size, self.height, n_feats, 1, 1) |
|
|
|
attention_vectors = self.softmax(attention_vectors) |
|
feats_V = torch.sum(inp_feats * attention_vectors, dim=1) |
|
|
|
return feats_V |
|
|
|
|
|
|
|
|
|
class SALayer(nn.Module): |
|
def __init__(self, kernel_size=5, bias=False): |
|
super(SALayer, self).__init__() |
|
self.conv_du = nn.Sequential( |
|
nn.Conv2d(2, 1, kernel_size=kernel_size, stride=1, padding=(kernel_size - 1) // 2, bias=bias), |
|
nn.Sigmoid() |
|
) |
|
|
|
def forward(self, x): |
|
|
|
max_pool, _ = torch.max(x, dim=1, keepdim=True) |
|
avg_pool = torch.mean(x, 1, keepdim=True) |
|
channel_pool = torch.cat([max_pool, avg_pool], dim=1) |
|
y = self.conv_du(channel_pool) |
|
|
|
return x * y |
|
|
|
|
|
|
|
class CALayer(nn.Module): |
|
def __init__(self, channel, reduction=16, bias=False): |
|
super(CALayer, self).__init__() |
|
|
|
self.avg_pool = nn.AdaptiveAvgPool2d(1) |
|
|
|
self.conv_du = nn.Sequential( |
|
nn.Conv2d(channel, channel // reduction, 1, padding=0, bias=bias), |
|
nn.ReLU(inplace=True), |
|
nn.Conv2d(channel // reduction, channel, 1, padding=0, bias=bias), |
|
nn.Sigmoid() |
|
) |
|
|
|
def forward(self, x): |
|
y = self.avg_pool(x) |
|
y = self.conv_du(y) |
|
return x * y |
|
|
|
|
|
|
|
class HWB(nn.Module): |
|
def __init__(self, n_feat, o_feat, kernel_size, reduction, bias, act): |
|
super(HWB, self).__init__() |
|
self.dwt = DWT() |
|
self.iwt = IWT() |
|
|
|
modules_body = \ |
|
[ |
|
conv(n_feat*2, n_feat, kernel_size, bias=bias), |
|
act, |
|
conv(n_feat, n_feat*2, kernel_size, bias=bias) |
|
] |
|
self.body = nn.Sequential(*modules_body) |
|
|
|
self.WSA = SALayer() |
|
self.WCA = CALayer(n_feat*2, reduction, bias=bias) |
|
|
|
self.conv1x1 = nn.Conv2d(n_feat*4, n_feat*2, kernel_size=1, bias=bias) |
|
self.conv3x3 = nn.Conv2d(n_feat, o_feat, kernel_size=3, padding=1, bias=bias) |
|
self.activate = act |
|
self.conv1x1_final = nn.Conv2d(n_feat, o_feat, kernel_size=1, bias=bias) |
|
|
|
def forward(self, x): |
|
residual = x |
|
|
|
|
|
wavelet_path_in, identity_path = torch.chunk(x, 2, dim=1) |
|
|
|
|
|
x_dwt = self.dwt(wavelet_path_in) |
|
res = self.body(x_dwt) |
|
branch_sa = self.WSA(res) |
|
branch_ca = self.WCA(res) |
|
res = torch.cat([branch_sa, branch_ca], dim=1) |
|
res = self.conv1x1(res) + x_dwt |
|
wavelet_path = self.iwt(res) |
|
|
|
out = torch.cat([wavelet_path, identity_path], dim=1) |
|
out = self.activate(self.conv3x3(out)) |
|
out += self.conv1x1_final(residual) |
|
|
|
return out |
|
|
|
|
|
|
|
|
|
class HWMNet(nn.Module): |
|
def __init__(self, in_chn=3, wf=64, depth=4): |
|
super(HWMNet, self).__init__() |
|
self.depth = depth |
|
self.down_path = nn.ModuleList() |
|
self.bili_down = bili_resize(0.5) |
|
self.conv_01 = nn.Conv2d(in_chn, wf, 3, 1, 1) |
|
|
|
|
|
prev_channels = 0 |
|
for i in range(depth): |
|
downsample = True if (i + 1) < depth else False |
|
self.down_path.append(UNetConvBlock(prev_channels + wf, (2 ** i) * wf, downsample)) |
|
prev_channels = (2 ** i) * wf |
|
|
|
|
|
self.up_path = nn.ModuleList() |
|
self.skip_conv = nn.ModuleList() |
|
self.conv_up = nn.ModuleList() |
|
self.bottom_conv = nn.Conv2d(prev_channels, wf, 3, 1, 1) |
|
self.bottom_up = bili_resize(2 ** (depth-1)) |
|
|
|
for i in reversed(range(depth - 1)): |
|
self.up_path.append(UNetUpBlock(prev_channels, (2 ** i) * wf)) |
|
self.skip_conv.append(nn.Conv2d((2 ** i) * wf, (2 ** i) * wf, 3, 1, 1)) |
|
self.conv_up.append(nn.Sequential(*[bili_resize(2 ** i), nn.Conv2d((2 ** i) * wf, wf, 3, 1, 1)])) |
|
prev_channels = (2 ** i) * wf |
|
|
|
self.final_ff = SKFF(in_channels=wf, height=depth) |
|
self.last = conv3x3(prev_channels, in_chn, bias=True) |
|
|
|
def forward(self, x): |
|
img = x |
|
scale_img = img |
|
|
|
|
|
x1 = self.conv_01(img) |
|
encs = [] |
|
|
|
|
|
for i, down in enumerate(self.down_path): |
|
if i == 0: |
|
x1, x1_up = down(x1) |
|
encs.append(x1_up) |
|
elif (i + 1) < self.depth: |
|
scale_img = self.bili_down(scale_img) |
|
left_bar = self.conv_01(scale_img) |
|
x1 = torch.cat([x1, left_bar], dim=1) |
|
x1, x1_up = down(x1) |
|
encs.append(x1_up) |
|
else: |
|
scale_img = self.bili_down(scale_img) |
|
left_bar = self.conv_01(scale_img) |
|
x1 = torch.cat([x1, left_bar], dim=1) |
|
x1 = down(x1) |
|
|
|
|
|
ms_result = [self.bottom_up(self.bottom_conv(x1))] |
|
for i, up in enumerate(self.up_path): |
|
x1 = up(x1, self.skip_conv[i](encs[-i - 1])) |
|
ms_result.append(self.conv_up[i](x1)) |
|
|
|
msff_result = self.final_ff(ms_result) |
|
|
|
|
|
out_1 = self.last(msff_result) + img |
|
|
|
return out_1 |
|
|
|
if __name__ == "__main__": |
|
input = torch.ones(1, 3, 400, 592, dtype=torch.float, requires_grad=False).cuda() |
|
|
|
model = HWMNet(in_chn=3, wf=96, depth=4).cuda() |
|
out = model(input) |
|
flops, params = profile(model, inputs=(input,)) |
|
|
|
|
|
|
|
|
|
|
|
print('input shape:', input.shape) |
|
print('parameters:', params/1e6) |
|
print('flops', flops/1e9) |
|
print('output shape', out.shape) |
|
|