图像去雾任务长期面对一个矛盾:轻量级网络虽然推理速度快,但在恢复纹理时容易丢失细节;重量级网络虽然细节更好,但很难部署到实时系统。AOD-Net正是为了兼顾效率与效果而提出的一体化去雾方案,它不单独估计透射率和大气光,而是通过可变形公式直接从雾图映射清晰图。不过,该网络在复杂场景中仍然存在细节丢失问题,尤其是树枝、文字边缘和远处建筑轮廓区域。造成这一现象的根源需要从感受野和映射方式两个层面分析。

AOD-Net的核心思想是让网络估计一个与深度相关的参数K,然后通过公式J(x) = K(x)I(x) - K(x) + b恢复无雾图,其中I(x)是输入雾图,J(x)是输出清晰图,b为常数偏置。这种设计绕开了传统先估计透射率再估计大气光的两阶段流程,因此推理链路很短。可是当K估计出现轻微偏差时,乘法项会放大输入雾图中的噪声,平坦区域容易出现色偏或光晕,而边缘区域则容易被平滑掉。
一、AOD-Net细节丢失的主要原因
细节丢失首先来自卷积结构的感受野限制。原始AOD-Net的主干网络由连续的3x3卷积层堆叠而成,虽然这种结构具备一定的非线性表达能力,但有效感受野仍然有限。当雾气分布不均匀时,局部邻域的卷积核很难判断当前像素到底处于薄雾还是浓雾区域,也无法同时参考远处天空和近处物体的上下文信息。结果就是网络倾向于使用平均化的处理方式,把原本锐利的边缘抹平,让输出图像看起来像蒙了一层灰。
损失函数的选择也会加剧这一问题。原模型在训练时大多使用均方误差,也就是MSE损失。MSE对像素误差的平方进行惩罚,虽然有利于稳定训练,但它天然偏向生成平滑结果。原因是图像中的高频边缘一旦预测偏差较大,会带来很高的平方误差;网络为了避免这种惩罚,会选择牺牲边缘锐度来换取整体更低的平均误差。于是树叶边界、建筑线条、文字笔画等高频区域就成了细节丢失的重灾区。
从映射方式来看,直接回归清晰图像要求网络同时重建低频结构和高频纹理。但雾图与清晰图在低频分量上非常接近,真正需要网络学习的只是雾气残差和局部细节差异。如果让网络从零开始生成完整清晰图,优化空间会变得更大,训练难度也随之增加。这也解释了为什么很多改进工作只增加网络深度却收效有限,因为问题并不完全出在容量不足,而是出在目标定义和特征表达方式上。
二、多尺度特征提取模块设计
多尺度特征的引入是为了让网络在不同膨胀率下观察同一张特征图,从而同时建模局部细节和全局雾气分布。具体做法是在编码器与解码器之间插入一个并行分支模块:输入特征分别经过1x1卷积、膨胀率为2、4、6的3x3卷积,得到四组特征后沿通道维度拼接,再用1x1卷积进行融合。膨胀卷积可以在不降低空间分辨率的前提下扩大感受野,这对像素级去雾任务尤其重要。
下面是一个基于PyTorch的多尺度特征模块实现:
import torch
import torch.nn as nn
class MultiScaleFeature(nn.Module):
def __init__(self, in_ch, out_ch):
super(MultiScaleFeature, self).__init__()
self.branch1 = nn.Conv2d(in_ch, out_ch, 1)
self.branch2 = nn.Conv2d(in_ch, out_ch, 3, padding=2, dilation=2)
self.branch3 = nn.Conv2d(in_ch, out_ch, 3, padding=4, dilation=4)
self.branch4 = nn.Conv2d(in_ch, out_ch, 3, padding=6, dilation=6)
self.fuse = nn.Conv2d(out_ch * 4, out_ch, 1)
self.norm = nn.BatchNorm2d(out_ch)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
f1 = self.branch1(x)
f2 = self.branch2(x)
f3 = self.branch3(x)
f4 = self.branch4(x)
y = torch.cat([f1, f2, f3, f4], dim=1)
y = self.fuse(y)
return self.relu(self.norm(y))
相比池化金字塔,这种膨胀卷积组合不会降低特征图分辨率,因此避免了池化带来的空间信息丢失。不同分支的膨胀率对应不同大小的感受野:1x1分支保留原始局部细节,膨胀率为6的分支可以看到更大的雾气分布范围。融合层负责把这些互补信息整合起来,使后续解码器既能恢复边缘,也能处理不均匀雾气。
在扩张率选择上,不建议直接使用过大的数值。例如膨胀率为8或12时,3x3卷积的实际采样点会过度稀疏,容易产生栅格效应,反而破坏特征连续性。实际部署中可以根据输入分辨率调整组合,例如256x256训练时采用[1,2,4,6]比较稳定,而处理1080p图像时可以适当增加一个膨胀率为8的分支。
三、残差学习分支与损失函数选择
残差学习的核心思想是让网络预测雾图与清晰图之间的差值R = I - J,然后通过J = I - R得到清晰图像。相比直接预测J,预测残差R的优化目标更集中,因为雾图的低频结构可以直接保留,网络只需要关注雾气浓度和局部差异。这样既降低了重构压力,也减少了色彩偏移风险。改进后的网络整体结构可以设计为编码器、多尺度模块、解码器三部分,解码器输出三通道残差图。
对应的网络结构示例如下:
import torch
import torch.nn as nn
class ResidualDehazeNet(nn.Module):
def __init__(self, base_channels=32):
super(ResidualDehazeNet, self).__init__()
self.encoder = nn.Sequential(
nn.Conv2d(3, base_channels, 3, padding=1),
nn.BatchNorm2d(base_channels),
nn.ReLU(inplace=True),
nn.Conv2d(base_channels, base_channels * 2, 3, stride=2, padding=1),
nn.BatchNorm2d(base_channels * 2),
nn.ReLU(inplace=True)
)
self.ms_feature = MultiScaleFeature(base_channels * 2, base_channels * 2)
self.decoder = nn.Sequential(
nn.ConvTranspose2d(base_channels * 2, base_channels, 4, stride=2, padding=1),
nn.BatchNorm2d(base_channels),
nn.ReLU(inplace=True),
nn.Conv2d(base_channels, 3, 3, padding=1)
)
def forward(self, x):
feat = self.encoder(x)
feat = self.ms_feature(feat)
r = self.decoder(feat)
return torch.clamp(x - r, 0.0, 1.0)
损失函数方面,仅用L1或MSE仍然可能偏向平滑。为了保留边缘细节,可以在L1损失的基础上加入边缘损失。边缘损失通过Sobel算子分别计算预测图和目标图的梯度幅值,再用L1度量两者差异。这样网络在优化时不仅关注像素值是否接近,还会关注边缘位置和强度是否一致。
下面是一种结合L1与边缘损失的实现方式:
import torch
import torch.nn.functional as F
def dehaze_loss(pred, target):
l1 = F.l1_loss(pred, target)
sobel_x = torch.tensor([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]], dtype=torch.float32).reshape(1, 1, 3, 3).to(pred.device)
sobel_y = torch.tensor([[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]], dtype=torch.float32).reshape(1, 1, 3, 3).to(pred.device)
def grad(img):
gray = 0.299 * img[:, 0:1] + 0.587 * img[:, 1:2] + 0.114 * img[:, 2:3]
gx = F.conv2d(gray, sobel_x, padding=1)
gy = F.conv2d(gray, sobel_y, padding=1)
return torch.sqrt(gx ** 2 + gy ** 2 + 1e-8)
edge_loss = F.l1_loss(grad(pred), grad(target))
return l1 + 0.5 * edge_loss
如果训练数据充足,还可以引入感知损失,使用预训练VGG网络的高层特征计算距离。感知损失能更好地对齐语义级结构,但会增加训练开销。对于实时去雾场景,L1加边缘损失已经能在细节与效率之间取得不错平衡。
四、训练调参与效果评估
训练采用RESIDE室外训练集,从原始图像中随机裁剪256x256大小的补丁作为输入,批大小设置为8。优化器使用Adam,初始学习率设为1e-4,每20个epoch衰减一半。数据增强包括随机水平翻转和轻微颜色扰动,这有助于提升模型对真实雾图的泛化能力。验证在SOTS合成测试集上进行,同时选择一部分真实雾图进行主观对比。
从定量指标来看,原始AOD-Net在SOTS上的PSNR约为20.5 dB,SSIM约为0.85,而引入多尺度特征和残差学习后,PSNR可以提升约1.8 dB,SSIM提升约0.04。由于SSIM对结构信息更敏感,这一提升说明改进后的模型确实保留了更多边缘和纹理。模型参数量从约0.14M增加到约0.31M,但仍然处于极轻量水平,1080p图像在CPU上的推理时间约为20毫秒,能够满足实时处理需求。
在实际部署中还需要注意归一化方式。训练时应将输入图像从0到255归一化到0到1之间,推理时再反归一化回原区间。如果数据集中同时包含室内和室外雾图,建议分开训练或使用更复杂的颜色校正模块,否则模型容易在两类场景之间产生颜色偏差。对于视频去雾任务,可以将当前帧与前一帧的残差结果进行时序平滑,进一步抑制闪烁。