超分辨率第九章-RDN

超分辨率第九章-RDN

Residual Dense Network for Image Super-Resolution(RDN)发表于2018年的CVPR

论文地址:Residual Dense Network for Image Super-Resolution

一.模型介绍

  • 残差密集块( RDB ),它不仅可以通过连续内存( CM )机制从前一个RDB中读取状态,还可以通过局部密集连接充分利用其内部的所有层。
  • 局部特征融合( LFF ),自适应地保留积累的特征。
  • 全局特征融合,以自适应地融合来自LR空间中所有RDB的分层特征。
  • 通过全局残差学习,将浅层特征和深层特征结合在一起,从而得到原始LR图像的全局稠密特征
  • pArHBpd.png
  • pArHsXt.png

二.网络结构

1.RDB模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class RDB_Conv(nn.Module):  
# 初始化方法,接收输入通道数inChannels,增长率growRate,以及可选的卷积核大小kSize(默认为3)
def __init__(self, inChannels, growRate, kSize=3):
super(RDB_Conv, self).__init__() # 调用父类的初始化方法
Cin = inChannels # 输入通道数
G = growRate # 增长率
# 构建一个卷积层序列,包含一个2D卷积层和一个ReLU激活函数
self.conv = nn.Sequential(*[
nn.Conv2d(Cin, G, kSize, padding=(kSize-1)//2, stride=1), # 2D卷积层,使用适当的填充以保持输入和输出尺寸相同
nn.ReLU() # ReLU激活函数
])

# 前向传播方法,接收输入x
def forward(self, x):
out = self.conv(x) # 通过卷积层和激活函数处理输入x
# 将处理后的输出out与原始输入x在通道维度(维度1)上拼接起来
return torch.cat((x, out), 1)

# 定义一个名为RDB的类,它继承自nn.Module,表示一个残差密集块
class RDB(nn.Module):
def __init__(self, growRate0, growRate, nConvLayers, kSize=3):
super(RDB, self).__init__() # 调用父类的初始化方法
G0 = growRate0 # 初始特征数
G = growRate # 每个RDB_Conv层的最终输出特征数
C = nConvLayers # RDB_Conv层的数量

convs = [] # 初始化一个空列表,用于存储RDB_Conv层
# 循环创建RDB_Conv层,每个层的输入通道数根据增长率和层数动态计算
for c in range(C):
convs.append(RDB_Conv(G0 + c*G, G))
self.convs = nn.Sequential(*convs) # 将RDB_Conv层序列化为一个nn.Sequential模块

# Local Feature Fusion(局部特征融合),使用一个1x1卷积层来融合所有RDB_Conv层的输出
self.LFF = nn.Conv2d(G0 + C*G, G0, 1, padding=0, stride=1)

# 前向传播方法,接收输入x
def forward(self, x):
# 通过所有RDB_Conv层处理输入x,并通过局部特征融合层LFF融合特征
return self.LFF(self.convs(x)) + x # 将融合后的特征与原始输入x相加,形成残差连接

2.RDN模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class RDN(nn.Module):  
# 初始化RDN网络,包括输入通道数、输出通道数、特征数、RDB块数、每一个RDB所含卷积层数、上采样因子
def __init__(self, in_channels, out_channels, num_features, num_blocks, num_layers, upscale_factor):
super(RDN, self).__init__() # 调用父类nn.Module的初始化方法
r = upscale_factor # 上采样因子
G0 = num_features # 初始特征数
kSize = 3 # 卷积核大小

# 将RDB块数、卷积层数、特征数分别赋值给D、C、G
self.D, C, G = [num_blocks, num_layers, num_features]

# 浅层特征提取网络
self.SFENet1 = nn.Conv2d(in_channels, G0, kSize, padding=(kSize-1)//2, stride=1) # 第一个卷积层
self.SFENet2 = nn.Conv2d(G0, G0, kSize, padding=(kSize-1)//2, stride=1) # 第二个卷积层

# 残差密集块和密集特征融合
self.RDBs = nn.ModuleList() # 创建一个模块列表来存储RDB块
for i in range(self.D):
self.RDBs.append(
RDB(growRate0 = G0, growRate = G, nConvLayers = C) # 向列表中添加RDB块
)

# 全局特征融合
self.GFF = nn.Sequential(*[
nn.Conv2d(self.D * G0, G0, 1, padding=0, stride=1), # 1x1卷积层,用于融合RDB块的输出
nn.Conv2d(G0, G0, kSize, padding=(kSize-1)//2, stride=1) # 3x3卷积层
])

# 上采样网络
if r == 2 or r == 3:
self.UPNet = nn.Sequential(*[
nn.Conv2d(G0, G * r * r, kSize, padding=(kSize-1)//2, stride=1), # 卷积层,调整通道数以匹配PixelShuffle
nn.PixelShuffle(r), # PixelShuffle层,实现上采样
nn.Conv2d(G, out_channels, kSize, padding=(kSize-1)//2, stride=1) # 输出卷积层
])
elif r == 4:
self.UPNet = nn.Sequential(*[
nn.Conv2d(G0, G * 2 * 2, kSize, padding=(kSize-1)//2, stride=1), # 第一步卷积层
nn.PixelShuffle(2), # 第一次PixelShuffle,上采样2倍
nn.Conv2d(G, G * 2 * 2, kSize, padding=(kSize-1)//2, stride=1), # 第二步卷积层
nn.PixelShuffle(2), # 第二次PixelShuffle,再次上采样2倍,总共4倍
nn.Conv2d(G, out_channels, kSize, padding=(kSize-1)//2, stride=1) # 输出卷积层
])
else:
raise ValueError("scale must be 2 or 3 or 4.") # 如果上采样因子不是2、3或4,抛出错误

# 前向传播函数
def forward(self, x):
f__1 = self.SFENet1(x) # 通过第一个浅层特征提取网络
x = self.SFENet2(f__1) # 通过第二个浅层特征提取网络

RDBs_out = [] # 创建一个列表来存储RDB块的输出
for i in range(self.D):
x = self.RDBs[i](x) # 通过每个RDB块
RDBs_out.append(x) # 将RDB块的输出添加到列表中

x = self.GFF(torch.cat(RDBs_out,1)) # 将所有RDB块的输出拼接起来,并通过全局特征融合网络
x += f__1 # 将全局特征融合的输出与第一个浅层特征提取网络的输出相加,形成残差连接

return self.UPNet(x) # 通过上采样网络,输出最终结果
-------------本文结束-------------