通过CNN计算大气散射模型中的透射率来实现图像去雾,是融合物理模型与深度学习的有效方法。
基于CNN和大气散射模型的图像去雾
1 大气散射模型
大气散射模型是描述雾天图像成像过程的物理模型,其数学表达式为:
$$I(x) = J(x)t(x) + A(1-t(x))$$
其中:
- $I(x)$ 表示观测到的有雾图像
- $J(x)$ 表示要恢复的无雾图像
- $t(x)$ 表示透射率(介质传输率),描述光在空气中传播时的衰减程度
- $A$ 表示全局大气光值
图像去雾的核心任务就是从有雾图像$I(x)$中估计出透射率$t(x)$和大气光值$A$,然后通过逆运算恢复无雾图像$J(x)$:
$$J(x) = \frac{I(x) - A}{t(x)} + A$$
传统方法需要人工设计特征和先验(如暗通道先验、颜色衰减先验)来估计$t(x)$和$A$,但在复杂场景下往往表现不佳。
2 CNN在透射率估计中的应用
卷积神经网络(CNN)能够自动学习图像特征,非常适合用于估计透射率。其主要优势在于:
- 能自动学习特征,避免人工先验的局限性
- 能更好地处理复杂场景(如天空、白色物体等)
- 估计的透射率图通常具有更好的边缘保持特性
2.1 网络设计选择
用于透射率估计的CNN通常采用编码器-解码器(Encoder-Decoder)结构,有时会加入跳跃连接(Skip Connections) 来融合低层和高层特征。
一个简单的网络结构示例:
- 编码器:使用卷积层和池化层逐步提取图像特征并减小空间尺寸
- 解码器:使用转置卷积或上采样层逐步恢复空间尺寸,输出单通道透射率图
- 跳跃连接:将编码器中的特征与解码器中相应尺度的特征连接,帮助保留细节
2.2 损失函数设计
训练CNN估计透射率时,常用的损失函数包括:
- 均方误差(MSE)损失:直接比较预测透射率与真实透射率的差异
- 结构相似性(SSIM)损失:考虑图像的结构信息,有助于保持边缘和结构
- 复合损失:结合MSE和SSIM的优点
3 MATLAB实现指南
3.1 数据准备
首先需要准备有雾图像和对应的真实透射率图数据集用于训练。由于真实透射率难以获取,通常使用合成数据集,如NYU Depth数据集和RESIDE数据集。
% 示例代码:加载和预处理数据
imds = imageDatastore('hazy_images_folder');
pxds = imageDatastore('transmission_maps_folder', 'ReadFcn', @(filename) im2gray(imread(filename)));
% 将图像和透射率图组合为训练数据
trainingData = combine(imds, pxds);
3.2 网络结构定义
在MATLAB中定义一个简单的编码器-解码器网络:
function lgraph = createTransmissionNet(inputSize)
% 创建编码器-解码器网络用于透射率估计
layers = [
imageInputLayer(inputSize, 'Name', 'input')
% 编码器
convolution2dLayer(3, 64, 'Padding', 'same', 'Name', 'conv1')
reluLayer('Name', 'relu1')
maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool1')
convolution2dLayer(3, 128, 'Padding', 'same', 'Name', 'conv2')
reluLayer('Name', 'relu2')
maxPooling2dLayer(2, 'Stride', 2, 'Name', 'pool2')
convolution2dLayer(3, 256, 'Padding', 'same', 'Name', 'conv3')
reluLayer('Name', 'relu3')
% 解码器
transposedConv2dLayer(2, 128, 'Stride', 2, 'Name', 'tconv1')
reluLayer('Name', 'relu4')
transposedConv2dLayer(2, 64, 'Stride', 2, 'Name', 'tconv2')
reluLayer('Name', 'relu5')
convolution2dLayer(3, 1, 'Padding', 'same', 'Name', 'final_conv')
sigmoidLayer('Name', 'sigmoid') % 透射率值应在0-1之间
];
lgraph = layerGraph(layers);
end
3.3 模型训练
配置训练选项并进行模型训练:
% 定义训练选项
options = trainingOptions('adam', ...
'InitialLearnRate', 1e-4, ...
'MaxEpochs', 30, ...
'MiniBatchSize', 8, ...
'Shuffle', 'every-epoch', ...
'Plots', 'training-progress', ...
'Verbose', false);
% 训练网络
net = trainNetwork(trainingData, lgraph, options);
3.4 大气光值估计
大气光值A通常可以通过选取有雾图像中最亮像素或结合透射率图来估计:
function A = estimateAtmosphericLight(hazyImage, transmissionMap)
% 估计大气光值
% 选择透射率最低的像素(雾最浓的区域)对应的有雾图像中最亮的像素
[height, width] = size(transmissionMap);
numPixels = height * width;
numTopPixels = max(1, round(0.001 * numPixels)); % 前0.1%的像素
% 将透射率图转换为向量并排序
transmissionVector = reshape(transmissionMap, numPixels, 1);
[~, sortedIndices] = sort(transmissionVector);
% 选择透射率最低的像素
darkestIndices = sortedIndices(1:numTopPixels);
% 找到有雾图像中这些像素最亮的强度
hazyVector = reshape(hazyImage, numPixels, 3);
darkestPixels = hazyVector(darkestIndices, :);
% 取这些像素中最亮的作为大气光值
A = max(darkestPixels, [], 1);
end
3.5 图像复原
使用估计的透射率图和大气光值进行图像复原:
function dehazedImage = recoverImage(hazyImage, transmissionMap, A)
% 根据大气散射模型恢复无雾图像
% 确保透射率不会太小(避免除零和噪声放大)
t = max(transmissionMap, 0.1);
% 扩展透射率图到三通道
t = repmat(t, [1, 1, 3]);
% 根据大气散射模型复原图像
dehazedImage = (hazyImage - A) ./ t + A;
% 确保像素值在有效范围内
dehazedImage = max(min(dehazedImage, 1), 0);
end
3.6 完整流程
% 加载有雾图像
hazyImage = im2double(imread('hazy_image.jpg'));
% 使用训练好的网络估计透射率
transmissionMap = predict(net, hazyImage);
% 估计大气光值
A = estimateAtmosphericLight(hazyImage, transmissionMap);
% 复原图像
dehazedImage = recoverImage(hazyImage, transmissionMap, A);
% 显示结果
figure;
subplot(1, 3, 1); imshow(hazyImage); title('有雾图像');
subplot(1, 3, 2); imshow(transmissionMap); title('估计的透射率图');
subplot(1, 3, 3); imshow(dehazedImage); title('去雾结果');