【论文研读】SMFANet:实现高效超分辨率的轻量化网络架构
本文为研读论文《SMFANet: A Lightweight Self-Modulation Feature Aggregation Network for Efficient Image Super-Resolution》所做笔记,适合轻量化实现高效超分辨率入门
论文研读: SMFANet: A Lightweight Self-Modulation Feature Aggregation Network for Efficient Image Super-Resolution
SMFANet:核心是通过轻量化的模块设计实现高效的局部与非局部特征协同建模,以平衡图像超分辨率的性能与计算效率。
:本文仅为博主学习所记笔记,侵删。
文章目录
一、网络架构
SMFANet由浅层特征提取模块、特征调制块(FMB)和图像重建模块组成。核心创新在于自调制特征聚合模块(SMFA)和部分卷积前馈网络(PCFN):

1.SMFA模块
SMFA(self-modulation feature aggregation):联合建模局部与非局部特征。
- 高效自注意力近似分支(EASA):通过下采样和深度可分离卷积提取低频结构信息,引入方差调制机制增强全局表征。
- 低通滤波:通过8倍下采样(自适应最大池化)和深度卷积(3×3 DWConv)提取低频结构信息。
- 方差调制:计算输入特征的全局方差,作为统计描述符调制低频特征,增强非局部建模能力。
- 特征聚合:调制后的特征上采样后(激活函数:GELU)与输入逐点相乘,生成非局部特征。
- 局部细节估计分支(LDE):采用扩展的3×3深度卷积捕获高频细节,通过两层1×1卷积增强局部特征。
- 双分支输出通过元素加和融合,形成互补的特征表示。
GELU激活函数:广泛应用于 Transformer 架构。
GELU ( x ) = x ⋅ Φ ( x ) 正态分布的累积分布函数CDF : Φ ( x ) = 1 2 ( 1 + erf ( x 2 ) ) \text{GELU}(x) = x \cdot \Phi(x)\\正态分布的累积分布函数\text{CDF}:\Phi(x) = \frac{1}{2} \left( 1 + \text{erf}\left( \frac{x}{\sqrt{2}} \right) \right) GELU(x)=x⋅Φ(x)正态分布的累积分布函数CDF:Φ(x)=21(1+erf(2x))
标准正态分布的累积分布函数(CDF):概率统计中的核心概念,描述了一个标准正态随机变量 Z (均值为 0、标准差为 1)的取值小于或等于某数 x 的概率。
Φ ( x ) = P ( Z ≤ x ) = ∫ − ∞ x 1 2 π e − z 2 2 d z = 1 2 ( 1 + erf ( x 2 ) ) erf ( x ) = 2 π ∫ 0 x e − t 2 d t \Phi(x) = P(Z \leq x) = \int_{-\infty}^{x} \frac{1}{\sqrt{2\pi}} e^{-\frac{z^2}{2}} \, dz= \frac{1}{2} \left( 1 + \text{erf}\left( \frac{x}{\sqrt{2}} \right) \right)\\ \text{erf}(x) = \frac{2}{\sqrt{\pi}} \int_{0}^{x} e^{-t^2} \, dt Φ(x)=P(Z≤x)=∫−∞x2π1e−2z2dz=21(1+erf(2x))erf(x)=π2∫0xe−t2dt
标准正态分布的概率密度函数(PDF): ϕ ( x ) = d d x Φ ( x ) = 1 2 π e − x 2 2 \phi(x) = \frac{d}{dx} \Phi(x) = \frac{1}{\sqrt{2\pi}} e^{-\frac{x^2}{2}} ϕ(x)=dxdΦ(x)=2π1e−2x2近似计算: GELU ( x ) ≈ 0.5 x ( 1 + tanh ( 2 π ( x + 0.044715 x 3 ) ) ) \text{GELU}(x) \approx 0.5x \left(1 + \tanh\left( \sqrt{\frac{2}{\pi}} (x + 0.044715x^3) \right) \right) GELU(x)≈0.5x(1+tanh(π2(x+0.044715x3)))
- 平滑性:相比 ReLU(在 x=0x=0 处不可导),GELU 是处处可导的,更适合梯度下降优化。
- 概率解释:GELU 可以看作对输入进行随机正则化(stochastic regularization),类似于 Dropout 的思想。
- 非单调性:对于负的输入,GELU 不会完全截断(如 ReLU),而是给予一个较小的权重。
- 计算成本:比 ReLU 稍高,但远低于 Sigmoid 或 Tanh。
2.PCFN模块
PCFN(Partial convolution-based feed-forward network):轻量级细化SMFA输出的特征。
“Cross-channel interaction on the expanded hidden space” 指在神经网络中,通过某种操作(如 1×1 卷积)让不同通道(channel)的特征图(feature maps)之间进行信息交互,而这一操作发生在一个维度扩展后的隐空间(expanded hidden space)中。
假设 PCFN 的结构如下:
- 输入:某个隐状态(hidden state),其维度为 C×H×W(通道 × 高度 × 宽度)。
- Expansion 操作:
- 可能通过全连接层或分组卷积将通道数从 C扩展到 C′(C′>C),形成 “expanded hidden space”。
- 1×1 Convolution + GELU:
- 在扩展后的空间(C′×H×W)上应用 1×1 卷积,权重矩阵为 C′×C′′,实现跨通道交互。
- GELU 激活函数对输出进行非线性变换。
效果:
- 通过扩展隐空间,模型能学习更复杂的通道间依赖关系。
- 1×1 卷积压缩或重组通道信息(类似 SENet 中的 Squeeze-and-Excitation),而 GELU 确保信息保留的平滑性。
细化SMFA生成的特征,将SMFA输出特征按通道拆分,仅对1/4通道进行3×3卷积处理,减少冗余计算的同时保留空间上下文信息。
- 常规FFN对空间信息交互不足,PCFN引入部分卷积(仅处理1/4通道)减少冗余计算:
- 1×1卷积扩展通道 → GELU激活 → 通道拆分(1:3)。
- 对1/4通道施加3×3卷积(编码局部上下文),剩余通道保留。
- 拼接后通过1×1卷积压缩通道。
3.总结
- SMFA输出: F ρ = Conv 1 × 1 ( X l + Y d ) F_{\rho} = \text{Conv}_{1\times1}(X_l + Y_d) Fρ=Conv1×1(Xl+Yd)
- X l X_l Xl:EASA分支的非局部特征。
- Y d Y_d Yd:LDE分支的局部特征。
- PCFN输出: F ^ ρ = Conv 1 × 1 ( Concat [ ϕ ( Conv 3 × 3 ( F ρ 1 ) ) , F ρ 2 ] ) \hat{F}{\rho} = \text{Conv}{1\times1}(\text{Concat}[\phi(\text{Conv}{3\times3}(F^1_\rho)), F^2_\rho]) F^ρ=Conv1×1(Concat[ϕ(Conv3×3(Fρ1)),Fρ2])
- 仅 F ρ 1 F^1_\rho Fρ1(1/4通道)参与3×3卷积计算。
二、实验验证
1.数据集
- 训练集:DIV2K(单数据集)和DF2K(DIV2K + Flickr2K组合)。
- 测试集:Set5、Set14、B100、Urban100、Manga109(评估PSNR/SSIM,YCbCr的Y通道)。
将图像变换到YCbCr颜色空间,计算图像Y通道的峰值信噪比(Peak signal-to-noise ratio,PSNR)与结构相似度(Structural similarity index,SSIM)来评价复原图像的质量。
PSNR 通过均方误差(MSE)衡量重建图像与原始图像的差异,单位为分贝(dB),值越大表示质量越好。
PSNR = 10 ⋅ log 10 ( MAX I 2 MSE ) , MAX I 2 :图像像素最大值 \text{PSNR} = 10 \cdot \log_{10} \left( \frac{\text{MAX}_I^2}{\text{MSE}} \right),\text{MAX}_I^2:图像像素最大值 PSNR=10⋅log10(MSEMAXI2),MAXI2:图像像素最大值
- 典型值范围:20dB(差) ~ 40dB(优秀)。
import numpy as np def psnr(original, reconstructed, max_pixel=255): mse = np.mean((original - reconstructed) ** 2) return 10 * np.log10(max_pixel**2 / mse)SSIM 从亮度(Luminance)、对比度(Contrast)、结构(Structure)三方面评估图像相似性,值越接近 1 表示越相似。
SSIM ( x , y ) = [ l ( x , y ) ] α ⋅ [ c ( x , y ) ] β ⋅ [ s ( x , y ) ] γ \text{SSIM}(x, y) = \left[ l(x, y) \right]^\alpha \cdot \left[ c(x, y) \right]^\beta \cdot \left[ s(x, y) \right]^\gamma SSIM(x,y)=[l(x,y)]α⋅[c(x,y)]β⋅[s(x,y)]γl ( x , y ) = 2 μ x μ y + C 1 μ x 2 + μ y 2 + C 1 , c ( x , y ) = 2 σ x σ y + C 2 σ x 2 + σ y 2 + C 2 , s ( x , y ) = σ x y + C 3 σ x σ y + C 3 简化形式:SSIM ( x , y ) = ( 2 μ x μ y + C 1 ) ( 2 σ x y + C 2 ) ( μ x 2 + μ y 2 + C 1 ) ( σ x 2 + σ y 2 + C 2 ) l(x, y) = \frac{2\mu_x \mu_y + C_1}{\mu_x^2 + \mu_y^2 + C_1},c(x, y) = \frac{2\sigma_x \sigma_y + C_2}{\sigma_x^2 + \sigma_y^2 + C_2},s(x, y) = \frac{\sigma_{xy} + C_3}{\sigma_x \sigma_y + C_3} \\简化形式:\text{SSIM}(x, y) = \frac{(2\mu_x \mu_y + C_1)(2\sigma_{xy} + C_2)}{(\mu_x^2 + \mu_y^2 + C_1)(\sigma_x^2 + \sigma_y^2 + C_2)} l(x,y)=μx2+μy2+C12μxμy+C1,c(x,y)=σx2+σy2+C22σxσy+C2,s(x,y)=σxσy+C3σxy+C3简化形式:SSIM(x,y)=(μx2+μy2+C1)(σx2+σy2+C2)(2μxμy+C1)(2σxy+C2)
from skimage.metrics import structural_similarity as ssim ssim_score = ssim(original, reconstructed, win_size=11, data_range=255, multichannel=True)
2.实现细节
- 输入:从LR图像中随机裁剪64个大小为64×64的补丁(patches),并随机进行水平翻转和旋转。迭代1,000,000次。
- 优化器:Adam(β1=0.9, β2=0.99),初始学习率1e-3,余弦退火至1e-5。
- 模型配置:
- SMFANet:8个FMB,36通道。
- SMFANet+:12个FMB,48通道(更大参数量)。
余弦退火(Cosine Annealing):通过余弦函数动态调整学习率,使其从初始值平滑下降到最小值,形似余弦曲线的半个周期(从 0 到 π)。
η t = η min + 1 2 ( η max − η min ) ( 1 + cos ( t T π ) ) \eta_t = \eta_{\text{min}} + \frac{1}{2} (\eta_{\text{max}} - \eta_{\text{min}}) \left( 1 + \cos\left( \frac{t}{T} \pi \right) \right) ηt=ηmin+21(ηmax−ηmin)(1+cos(Ttπ))
- η m a x η_{max} ηmax:初始学习率(如 1 × 1 0 − 3 1×10^{−3} 1×10−3)。
- η m i n η_{min} ηmin:最小学习率(如 1 × 1 0 − 5 1×10^{−5} 1×10−5)。
- T T T:总退火步数(一个周期长度)。
- t t t:当前训练步数( t ∈ [ 0 , T ] t∈[0,T] t∈[0,T])。
工作流程:
- 初始阶段: t = 0 t=0 t=0 时, c o s ( 0 ) = 1 cos(0)=1 cos(0)=1,学习率为 η m a x η_{max} ηmax。
- 下降阶段:随着 t t t 增加,余弦值从 1 减小到 -1,学习率从 η m a x η_{max} ηmax 平滑降至 η m i n η_{min} ηmin。
- 周期性重启(可选):某些变体(如 SGDR)会在每个周期结束后重启学习率到 η m a x η_{max} ηmax,形成多个余弦衰减周期。
# 初始化优化器 optimizer = optim.SGD(model.parameters(), lr=1e-3) # η_max = 1e-3 # 余弦退火调度器(T=总迭代步数) scheduler = CosineAnnealingLR(optimizer, T_max=1000, eta_min=1e-5) # η_min = 1e-5
3.对比实验
定量实验,定性实验
- 与CNN(如IMDN、CARN)和轻量级Transformer(如SwinIR-light、ELAN-light)对比。
- 评估指标:PSNR、SSIM、参数量(#Params)和FLOPs,最大占用GPU内存和平均推理时间,LAM(局部归因图,扩散指数(DI)值,值大表示涉及的像素范围更广。)比较结果。
4.消融实验
消融实验(Ablation Studies)
- SMFA模块
- 并行结构(EASA分支+ LDE分支)对性能提升至关重要。移除SMFA导致PSNR显著下降(Urban100降0.65dB)。
- EASA分支:近似自注意力机制,高效建模非局部信息。替换为窗口自注意力(window-based SA)会显著增加计算成本(GPU内存×3)。
- LDE分支:捕获局部细节,移除后图像边缘出现扭曲。
- 方差调制机制
- 使用输入特征的方差(σ²(x))调制非局部特征,比替代方案(如学习参数α)更有效,PSNR提升0.06dB。逐元素相加比逐元素相乘更有效。
- PCFN模块
- 部分卷积设计(仅处理1/4通道)在保持性能的同时降低计算开销,比全卷积方案(如GDFN)节省50% GPU内存。
三、代码实现
1.SMFANet
SMFANet 是一个基于 局部-全局特征混合 的图像超分辨率网络。
代码实现:
class SMFANet(nn.Module):
def __init__(self, dim=36, n_blocks=8, ffn_scale=2, upscaling_factor=4):
"""
dim=36:特征维度,默认为36
n_blocks=8:网络中使用的 FMB 块的数量,默认为8
ffn_scale=2:FFN(前馈网络)的缩放因子,默认为2
upscaling_factor=4:上采样因子,默认为4倍超分辨率
"""
super().__init__()
self.scale = upscaling_factor # 存储上采样因子
self.to_feat = nn.Conv2d(3, dim, 3, 1, 1) # 将输入RGB图像(3通道)转换为特征空间的卷积层
self.feats = nn.Sequential(*[FMB(dim, ffn_scale) for _ in range(n_blocks)]) # 多个FMB块组成的序列
self.to_img = nn.Sequential(
nn.Conv2d(dim, 3 * upscaling_factor**2, 3, 1, 1),
nn.PixelShuffle(upscaling_factor)
) # 将特征转换回图像的后处理部分
def forward(self, x):
x = self.to_feat(x) # 将输入图像转换为特征表示
x = self.feats(x) + x # 通过FMB块序列和残差连接处理特征
x = self.to_img(x) # 将特征转换回图像空间并上采样
return x # 返回图像
代码详解:
self.to_img = nn.Sequential(
nn.Conv2d(dim, 3 * upscaling_factor**2, 3, 1, 1),
nn.PixelShuffle(upscaling_factor)
)
nn.Conv2d(dim, 3 * upscaling_factor**2, 3, 1, 1):
- 输入通道:
dim(特征维度,默认为36) - 输出通道:
3 * upscaling_factor²- 3对应RGB三个通道
upscaling_factor²是因为PixelShuffle需要足够的通道来重组
nn.PixelShuffle(upscaling_factor):实现子像素卷积(Sub-pixel Convolution)的层
- 上采样因子:
upscaling_factor(默认为4) - 将
3×4²=48个通道重组为:- 空间维度扩大4倍(高度和宽度各×4)
- 通道维度降为3(RGB)
- 工作原理:先通过卷积增加通道数至
r²×C(其中r是上采样因子,C是输出通道数),然后通过PixelShuffle将通道数据重组为空间数据- 输入张量形状:
(N, C×r², H, W) - 输出张量形状:
(N, C, H×r, W×r)
- 输入张量形状:
上采样(UpSampling):将低分辨率图像或特征图转换为高分辨率的过程。
传统上采样方法:
- 最近邻插值(Nearest Neighbor)
- 双线性插值(Bilinear)
- 双三次插值(Bicubic)
深度学习中的上采样方法:
- 转置卷积(Transposed Convolution)
- 子像素卷积(Sub-pixel Convolution, 即PixelShuffle)
- 插值+卷积组合
1.1DMlp
DMlp: Dynamic MLP
功能:动态特征变换,通过深度可分离卷积增强局部感受野。
代码实现:
class DMlp(nn.Module):
def __init__(self, dim, growth_rate=2.0):
"""dim: 输入通道数
growth_rate: 通道扩展因子"""
super().__init__()
hidden_dim = int(dim * growth_rate)
self.conv_0 = nn.Sequential(
nn.Conv2d(dim,hidden_dim,3,1,1,groups=dim), # 深度分离卷积
nn.Conv2d(hidden_dim,hidden_dim,1,1,0) # 动态权重生成
)
self.act =nn.GELU()
self.conv_1 = nn.Conv2d(hidden_dim, dim, 1, 1, 0) # 降维恢复
def forward(self, x):
x = self.conv_0(x)
x = self.act(x)
x = self.conv_1(x)
return x
代码详解:
nn.Conv2d(
in_channels=dim, # 输入通道数
out_channels=hidden_dim, # 输出通道数
kernel_size=3, # 卷积核大小 (3x3)
stride=1, # 步长
padding=1, # 边缘填充(保持分辨率)
groups=dim # 分组卷积的组数
)
groups=dim:将输入通道和输出通道均分成dim组,每组内部独立进行卷积计算。- 每组输入通道数 =
dim / dim = 1 - 每组输出通道数 =
hidden_dim / dim - 计算方式:每组使用独立的3x3卷积核处理对应的输入通道,输出结果拼接成最终特征。
- 每组输入通道数 =
- 每个通道单独进行3x3卷积,保留空间局部特征。
- 降低参数量:
dim × hidden_dim × 3 × 3→ \to →dim × (hidden_dim/dim) × 3 × 3
nn.Conv2d(hidden_dim, hidden_dim, 1, 1, 0)
- 1x1卷积混合:将动态生成的权重跨通道混合,实现全局通道交互,避免通道间孤立。
1.2PCFN
PCFN: (Partial Convolutional Feed-Forward Network)
创新点:部分通道处理(p_rate=0.25),平衡计算效率与特征融合。
代码实现:
class PCFN(nn.Module):
def __init__(self, dim, growth_rate=2.0, p_rate=0.25):
super().__init__()
hidden_dim = int(dim * growth_rate)
p_dim = int(hidden_dim * p_rate) # 只对1/4通道数进行卷积操作
self.conv_0 = nn.Conv2d(dim,hidden_dim,1,1,0) # 1x1卷积升维
self.conv_1 = nn.Conv2d(p_dim, p_dim ,3,1,1) # 仅处理p_dim个通道
self.act =nn.GELU()
self.conv_2 = nn.Conv2d(hidden_dim, dim, 1, 1, 0) # 1x1卷积降维
self.p_dim = p_dim
self.hidden_dim = hidden_dim
def forward(self, x):
if self.training: # 训练模式
x = self.act(self.conv_0(x)) # [B, hidden_dim, H, W]
x1, x2 = torch.split(x,[self.p_dim,self.hidden_dim-self.p_dim],dim=1) # 显式分割
x1 = self.act(self.conv_1(x1)) # 仅处理x1 (p_dim个通道)
x = self.conv_2(torch.cat([x1,x2], dim=1)) # 合并后降维
else: # 推理模式
x = self.act(self.conv_0(x)) # [B, hidden_dim, H, W]
x[:,:self.p_dim,:,:] = self.act(self.conv_1(x[:,:self.p_dim,:,:])) # 原地修改前p_dim个通道
x = self.conv_2(x) # 降维恢复
return x
代码详解:
x[:,:self.p_dim,:,:] = self.act(self.conv_1(x[:,:self.p_dim,:,:]))
- 输入张量:
x的形状为[B, hidden_dim, H, W]。 - 操作步骤:
- 切片选择:
x[:, :p_dim]选取前p_dim个通道。 - 3x3卷积:
self.conv_1对选中的通道进行空间卷积。 - 激活函数:
self.act(GELU)引入非线性。 - 原位写入:结果直接写回
x的前p_dim通道。
- 切片选择:
- 原地操作减少内存拷贝,提升推理速度。
训练模式和推理模式的差异:
- 训练模式:显式分割-处理-合并
- 显式
split和cat确保梯度能正确回传到所有通道,避免原地修改的梯度截断。 - 各分支张量独立可检查(如
x1和x2的值可单独打印)。 - 推理模式:原位修改
- 推理时不需反向传播,无需保存中间结果。
- 减少内存占用,加速计算。
1.3SMFA
SMFA (Spatial Modulation Feature Attention)
核心机制:
- 多尺度感知:通过下采样(
down_scale=8)捕获全局上下文。 - 方差调制:利用通道方差
x_v动态调整特征重要性。
x l = x ⋅ interpolate ( GELU ( W 1 ( α ⋅ x s + β ⋅ x v ) ) ) x_l = x \cdot \text{interpolate}(\text{GELU}(W_1(\alpha \cdot x_s + \beta \cdot x_v))) xl=x⋅interpolate(GELU(W1(α⋅xs+β⋅xv)))
代码实现:
class SMFA(nn.Module):
def __init__(self, dim=36):
super(SMFA, self).__init__()
self.linear_0 = nn.Conv2d(dim,dim*2,1,1,0) # 将输入通道扩展2倍,用于分割出x和y两条分支
self.linear_1 = nn.Conv2d(dim,dim,1,1,0) # 特征变换
self.linear_2 = nn.Conv2d(dim,dim,1,1,0) # 降维
self.lde = DMlp(dim,2) # 动态MLP分支,用于增强局部特征
self.dw_conv = nn.Conv2d(dim,dim,3,1,1,groups=dim) # 深度分离卷积,每个通道独立计算
self.gelu = nn.GELU()
self.down_scale = 8 # 下采样比率
self.alpha = nn.Parameter(torch.ones((1,dim,1,1))) # 可学习的缩放参数
self.belt = nn.Parameter(torch.zeros((1,dim,1,1))) # 可学习的偏置参数
def forward(self, f):
_,_,h,w = f.shape
y, x = self.linear_0(f).chunk(2, dim=1) # f[B,dim,H,W] → x/y [B,dim,H,W]
x_s = self.dw_conv(F.adaptive_max_pool2d(x, (h // self.down_scale, w // self.down_scale))) # 8倍下采样 + 3x3深度卷积,捕获图像宏观结构
x_v = torch.var(x, dim=(-2,-1), keepdim=True) # 通道方差,表征局部激活强度
x_l = x * F.interpolate(self.gelu(self.linear_1(x_s * self.alpha + x_v * self.belt)), size=(h,w), mode='nearest')
y_d = self.lde(y) # DMlp动态特征变换
return self.linear_2(x_l + y_d) # 逐元素相加,跨通道信息重组
代码详解:
"""
torch.chunk和torch.split
"""
y, x = self.linear_0(f).chunk(2, dim=1) # 将通道维度均分为两部分
x1, x2 = torch.split(x, [self.p_dim, self.hidden_dim-self.p_dim], dim=1) # 自定义分割
torch.chunk(tensor, chunks, dim=0):
tensor:待分割的张量。chunks:均分的块数(必须能整除对应维度大小)。dim:分割的维度(默认为0)。对于一个4D张量[B, C, H, W]。dim=0:沿batch维度分割(B)dim=1:沿通道维度分割(C)dim=2:沿高度分割(H)dim=3:沿宽度分割(W)
torch.split(tensor, split_size_or_sections, dim=0):
tensor:待分割的张量。split_size_or_sections:可以是每块的大小(整数)或各块尺寸列表。dim:分割的维度(默认为0)。
x_s = self.dw_conv(F.adaptive_max_pool2d(x, (h // self.down_scale, w // self.down_scale))) # 通过组合下采样和深度卷积,从输入特征中提取全局结构信息
F.adaptive_max_pool2d(x, (h // self.down_scale, w // self.down_scale)):将输入特征图下采样到固定尺寸 (H/8, W/8)。
- 在每个
8x8窗口内取最大值,保留局部最显著特征(如边缘、纹理),抑制噪声。 - 池化将
8x8区域压缩为1个点,后续卷积的1个点实际对应原始输入的8×3=24像素范围。
x_v = torch.var(x, dim=(-2,-1), keepdim=True)
x:输入特征图。dim=(-2,-1):指定沿最后两个维度(高度H和宽度W)计算方差。keepdim=True:保持原始维度数(否则输出会减少两维)。
x_l = x * F.interpolate(
self.gelu(
self.linear_1(x_s * self.alpha + x_v * self.belt)
),
size=(h,w),
mode='nearest'
)
x_s * self.alpha + x_v * self.belt:动态权重计算。
F.interpolate(..., size=(h,w), mode='nearest'):上采样对齐
- 将权重图从
[B,C,H/8,W/8]上采样到原始分辨率[B,C,H,W] nearest:复制最近像素值,边缘锐利。
x * F...:逐元素相乘
插值法上采样和
PixelShuffle上采样:插值法上采样更常见于注意力机制或特征调制模块中,而PixelShuffle通常作为最终的上采样输出层。
interpolate:
- 特征融合:
x_s * self.alpha + x_v * self.belt进行两种特征加权融合- 线性变换:
self.linear_1(通常是全连接层或1x1卷积)- 激活函数:GELU非线性激活
- 上采样:
F.interpolate使用最近邻插值(mode=‘nearest’)上采样到目标尺寸(h,w)- 调制操作:最终与输入x进行逐元素相乘
PixelShuffle:
- 通道扩展:卷积层将通道数扩展为
r²×C(r为上采样因子)- 像素重组:通过固定操作将通道数据重组为空间数据
- 自动完成:输出尺寸自动计算为
H×r, W×r
1.4FMB
FMB (Feature Modulation Block)
作用:通过双重归一化(F.normalize)稳定训练过程。
代码实现:
class FMB(nn.Module):
def __init__(self, dim, ffn_scale=2.0):
super().__init__()
self.smfa = SMFA(dim)
self.pcfn = PCFN(dim, ffn_scale)
def forward(self, x):
x = self.smfa(F.normalize(x)) + x # 残差连接
x = self.pcfn(F.normalize(x)) + x # 残差连接
return x
代码详解:
F.normalize(x):输入进行 Layer Normalization(沿通道维度标准化),提升注意力/卷积的敏感性。
:笔者不是很懂= =
| 作用维度 | 具体表现 |
|---|---|
| 梯度稳定 | 防止特征值量级波动导致梯度爆炸/消失,加速收敛 |
| 特征分布对齐 | 强制特征向量位于单位球面,减少内部协变量偏移(Internal Covariate Shift) |
| 多模块协同 | 确保SMFA和PCFN的输入处于相似数值范围,提升模块兼容性 |
2.评估指标
2.1PSNR
PSNR(Peak signal-to-noise ratio):峰值信噪比。通过均方误差(MSE)衡量重建图像与原始图像的差异,单位为分贝(dB),值越大表示质量越好。
PSNR = 10 ⋅ log 10 ( MAX I 2 MSE ) \text{PSNR} = 10 \cdot \log_{10} \left( \frac{\text{MAX}_I^2}{\text{MSE}} \right) PSNR=10⋅log10(MSEMAXI2)
- MAX I \text{MAX}_I MAXI:图像像素最大值,一般是 255 ∗ 255 255*255 255∗255。
- 典型值范围:20dB(差) ~ 40dB(优秀)。
代码实现:
def calculate_psnr(img, img2, crop_border, input_order='HWC', test_y_channel=False, **kwargs):
"""
img,img2 (ndarray): Images with range [0, 255].
crop_border (int): 裁剪边缘像素数(避免边界效应影响评估)
input_order (str): 输入数据的通道顺序
test_y_channel (bool): 是否仅计算YCbCr颜色空间的Y(亮度)通道,默认false
"""
assert img.shape == img2.shape, (f'Image shapes are different: {img.shape}, {img2.shape}.') # 确保图像尺寸相同
if input_order not in ['HWC', 'CHW']: # 检查合法通道顺序
raise ValueError(f'Wrong input_order {input_order}. Supported input_orders are "HWC" and "CHW"')
img = reorder_image(img, input_order=input_order) # 统一转为input_order格式
img2 = reorder_image(img2, input_order=input_order)
if crop_border != 0: # 裁剪边缘
img = img[crop_border:-crop_border, crop_border:-crop_border, ...]
img2 = img2[crop_border:-crop_border, crop_border:-crop_border, ...]
if test_y_channel: # RGB转Y通道
img = to_y_channel(img)
img2 = to_y_channel(img2)
img = img.astype(np.float64) # 提升计算精度
img2 = img2.astype(np.float64)
mse = np.mean((img - img2)**2) # 均方误差
if mse == 0:
return float('inf')
return 10. * np.log10(255. * 255. / mse)
**kwargs:用于接收任意数量的关键字参数(keyword arguments)
- 前面的双星号
**表示将传入的关键字参数打包成一个字典 - 与
*args(接收非关键字参数)不同,**kwargs专门处理具名参数
YCbCr颜色空间:将RGB颜色信息分离为亮度(Y)和色度(Cb, Cr)的颜色表示方法
- Y(Luma):亮度分量,包含图像的灰度信息
- Cb:蓝色色度分量(Blue Chrominance)
- Cr:红色色度分量(Red Chrominance)
场景 test_y_channel=False test_y_channel=True 输入图像 RGB三通道 仅Y通道(单通道) 计算复杂度 较高(计算3通道) 较低(计算1通道) 适用情况 需要全面评估色彩保真度 聚焦结构/亮度信息 典型值差异 通常比Y-PSNR低1-3dB 更接近主观感知质量
2.2SSIM
SSIM(Structural similarity index):结构相似度。从亮度(Luminance)、对比度(Contrast)、结构(Structure)三方面评估图像相似性,值越接近 1 表示越相似。
SSIM ( x , y ) = ( 2 μ x μ y + C 1 ) ( 2 σ x y + C 2 ) ( μ x 2 + μ y 2 + C 1 ) ( σ x 2 + σ y 2 + C 2 ) \text{SSIM}(x, y) = \frac{(2\mu_x \mu_y + C_1)(2\sigma_{xy} + C_2)}{(\mu_x^2 + \mu_y^2 + C_1)(\sigma_x^2 + \sigma_y^2 + C_2)} SSIM(x,y)=(μx2+μy2+C1)(σx2+σy2+C2)(2μxμy+C1)(2σxy+C2)
- μ \mu μ:均值。 σ \sigma σ:方差/协方差。 C C C:稳定常数。
代码实现:
def calculate_ssim(img, img2, crop_border, input_order='HWC', test_y_channel=False, **kwargs):
"""
...代码类似上面PSNR,此处省略...
"""
ssims = []
for i in range(img.shape[2]): # 逐通道计算
ssims.append(_ssim(img[..., i], img2[..., i]))
return np.array(ssims).mean() # 多通道平均
def _ssim(img, img2):
c1 = (0.01 * 255)**2
c2 = (0.03 * 255)**2
kernel = cv2.getGaussianKernel(11, 1.5)
window = np.outer(kernel, kernel.transpose())
mu1 = cv2.filter2D(img, -1, window)[5:-5, 5:-5] # valid mode for window size 11
mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5]
mu1_sq = mu1**2
mu2_sq = mu2**2
mu1_mu2 = mu1 * mu2
sigma1_sq = cv2.filter2D(img**2, -1, window)[5:-5, 5:-5] - mu1_sq
sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq
sigma12 = cv2.filter2D(img * img2, -1, window)[5:-5, 5:-5] - mu1_mu2
ssim_map = ((2 * mu1_mu2 + c1) * (2 * sigma12 + c2)) / ((mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2))
return ssim_map.mean()
代码详解:
kernel = cv2.getGaussianKernel(11, 1.5)
window = np.outer(kernel, kernel.transpose())
-
高斯核的创建
- 参数:
11:核大小(长度)1.5:高斯分布的标准差(σ)
- 输出:形状为
(11,1)的NumPy数组,元素和为1
- 参数:
-
生成2D高斯窗口
-
np.outer作用:计算两个向量的外积,生成2D高斯窗口 -
结果:得到11×11的对称高斯权重矩阵
-
关键特性:
- 中心权重最大,边缘权重逐渐减小
- 所有元素和≈1(保持亮度不变性)
-
mu1 = cv2.filter2D(img, -1, window)[5:-5, 5:-5]
cv2.filter2D:二维卷积函数,用于对图像进行滤波操作,这里用高斯窗口对图像做卷积(加权平均)-1:输出图像深度(-1表示与输入相同)window:高斯权重核,作为kernel卷积核
- 切片
[5:-5,5:-5]:去除边缘5像素(因11×11卷积会使边缘模糊)- 输入图像尺寸:假设为
(H,W) - 输出尺寸:
(H-10, W-10)(左右/上下各裁5像素)
- 输入图像尺寸:假设为
- 物理意义:计算每个像素邻域(11×11窗口)的高斯加权平均值,表示局部亮度(μ)
为什么做卷积是加权平均?
卷积本质是加权和,逐点相乘后相加。
加权平均的关键在于高斯核的归一化:
- 核元素和=1:通过
cv2.getGaussianKernel生成的核会自动归一化(sum(kernel)=1)- 物理意义:当核的和为1时,卷积操作相当于用权重系数对邻域像素求平均,且中心像素权重更大。
高斯加权:
- 模拟人眼视觉特性(中心敏感度高于边缘)
- 避免矩形窗口的块效应
边缘裁剪:
- 卷积边界效应会使边缘估计不准
- 保证所有计算点都有完整的11×11邻域
3.损失函数
3.1l_pix
l_pix:像素级重建损失(Pixel-wise Loss):衡量模型输出图像与真实图像在像素值上的直接差异。
Loss (MAE) : ℓ pix L1 = 1 N ∑ i = 1 N ∣ y i − y ^ i ∣ Loss (MSE) : ℓ pix L2 = 1 N ∑ i = 1 N ( y i − y ^ i ) 2 \text{Loss (MAE)}:\ell_{\text{pix}}^{\text{L1}} = \frac{1}{N} \sum_{i=1}^{N} |y_i - \hat{y}_i| \\\text{Loss (MSE)}:\ell_{\text{pix}}^{\text{L2}} = \frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)^2 Loss (MAE):ℓpixL1=N1i=1∑N∣yi−y^i∣Loss (MSE):ℓpixL2=N1i=1∑N(yi−y^i)2
- y i y_i yi:目标图像第 i i i个像素值
- y ^ i \hat{y}_i y^i:预测图像第 i i i个像素值
- N N N:图像总像素数( H × W × C H \times W \times C H×W×C)
3.2f_pix
f_pix:特征级感知损失(Feature-wise Loss):通过预训练网络(如VGG)提取图像的高层特征,比较特征图的差异,衡量视觉感知相似性(而非像素级匹配)。
ℓ f_pix = 1 C j H j W j ∥ ϕ j ( y ) − ϕ j ( y ^ ) ∥ 2 2 \ell_{\text{f\_pix}} = \frac{1}{C_j H_j W_j} \|\phi_j(y) - \phi_j(\hat{y})\|_2^2 ℓf_pix=CjHjWj1∥ϕj(y)−ϕj(y^)∥22
- ϕ j ( ⋅ ) \phi_j(\cdot) ϕj(⋅):预训练网络(如VGG16)第 j j j层的特征提取器
- C j × H j × W j C_j \times H_j \times W_j Cj×Hj×Wj:第 j j j层特征图的维度
3.3区别
| 指标 | 计算层面 | 优化目标 | 敏感性 | 典型应用场景 |
|---|---|---|---|---|
l_pix |
像素值直接比较 | 精确匹配像素颜色 | 对噪声敏感 | 超分辨率、去噪 |
f_pix |
深层特征比较 | 保持视觉语义一致性 | 对结构/纹理更敏感 | 风格迁移、图像生成 |
l_pix确保低频信息(如颜色、轮廓)准确f_pix提升高频细节(如纹理、边缘)的自然度
更多推荐
所有评论(0)