☰
MATLAB实现DnCNN图像去噪:从传统方法到深度学习实战
2026/10/2 14:49:49 网站建设 项目流程

简介:这份资源面向高校图像处理与深度学习方向的课程设计、毕业设计及期末大作业场景,提供了一套完整的MATLAB图像去噪仿真程序,将经典去噪算法与深度卷积神经网络DnCNN整合为统一的算法验证平台,适合具备一定MATLAB基础、希望深入理解传统方法与深度学习协同机制的学习者。压缩包共213个文件,约82.47MB,以m脚本、mat数据、png图像、mexw64与mexa64等跨平台编译文件及zbak备份文件为主,覆盖算法实现、数据存储、结果可视化与多平台运行支持,无需额外配置即可直接使用。目前已有70人学习下载。项目在指导教师评审中获得97分,代码规范、运行稳定,读者可据此复现论文去噪效果,并在此基础上开展算法改进与性能对比分析,掌握图像恢复任务中的关键技术要点。

1. 从一组噪点图说起:DnCNN 去噪在 MATLAB 里到底怎么落地

手里有一批被高斯噪声污染的图像,想验证去噪算法效果,又不想把整个流程绑在 Python 生态里——这是很多做 matlab 课程设计的人真实处境。这份资源给的就是一条完整链路:传统图像去噪方法(均值、中值、维纳、BM3D 思路)与深度学习 DnCNN 的 MATLAB 实现,外加配套数据集。DnCNN 的核心思路不复杂,它把残差学习引入去噪:网络不直接预测干净图,而是预测噪声图,最后用输入减掉预测噪声得到结果。这个设计让训练更稳、收敛更快,也是它比早期直接回归干净图的 CNN 更好用的原因。资源适合三类人:课程设计需要完整可跑代码的学生、想对比传统与深度学习去噪差异的从业者、以及要在 MATLAB 里搭深度学习基线的人。下面按「资源是什么 → 怎么跑 → 坑在哪 → 怎么调」推一遍。

2. 传统去噪与 DnCNN 的选型逻辑:为什么不是二选一

2.1 传统方法在 MATLAB 里的定位

传统去噪不是被淘汰的东西,它在 MATLAB 里反而是最稳的基线。均值滤波用imfilter配合fspecial('average', k)就能跑,中值滤波一行medfilt2搞定,维纳滤波有wiener2,BM3D 虽然 MATLAB 没有官方函数,但常见做法是找第三方实现或自己按块匹配加协同滤波写。这些方法的特点是参数少、可解释、不依赖训练数据,缺点是强噪声下细节丢得厉害,边缘容易糊。

我一般会先把传统方法跑一遍,不是为了出最终结果,而是拿它们的 PSNR/SSNR 当参照。如果 DnCNN 训完连中值滤波都打不过,那基本是训练流程或数据配对出了问题,不用怀疑网络结构。这一步能省掉大量瞎调参的时间。

传统方法的参数边界要清楚:均值滤波窗口越大越平滑但越糊,中值滤波对椒盐噪声效果好但对高斯噪声一般,维纳滤波需要估计噪声功率。这些在 MATLAB 文档里都有,但实际跑的时候噪声方差未知,通常用std2在平坦区域估一下,或者直接拿已知的加噪参数反推。

2.2 DnCNN 的残差结构为什么适合去噪

DnCNN 的结构可以拆成三段:第一层Conv + ReLU,中间若干层Conv + BN + ReLU,最后一层Conv输出单通道噪声估计。关键在残差连接——输入图像和网络输出相加(实际是相减)得到干净图。这个设计对应一个先验:噪声图比干净图更容易学,因为噪声分布相对简单,网络只需要拟合噪声分量。

在 MATLAB 里搭这个结构,用 Deep Learning Toolbox 的dlnetwork或layerGraph都行。常见做法是用imageInputLayer接convolution2dLayer,中间层加batchNormalizationLayer和reluLayer,最后regressionLayer做回归。注意 MATLAB 的卷积层默认是 valid padding,要手动设'Padding', 'same'保持尺寸,否则每过一层图就小一圈,最后对不上原图。

残差学习的另一个好处是缓解梯度消失。深层网络直接回归干净图时,浅层梯度容易衰减;改成预测噪声后,网络输出接近零均值,梯度传播更顺畅。这也是为什么 DnCNN 能堆到 17 层甚至更深还不难训。

2.3 数据集怎么配对才不翻车

DnCNN 训练需要成对的干净图和噪声图。资源里的数据集通常是干净图集合,噪声在训练时动态加。常见做法是用imnoise(I, 'gaussian', 0, sigma)在线生成,sigma 可以固定也可以随机采样。如果数据集本身已经带了噪声图,那要确认噪声类型和方差是否和你的测试条件一致,否则训出来的模型换个噪声水平就崩。

配对时最容易翻车的是尺寸和通道数。MATLAB 的imread读灰度图返回二维矩阵,读彩色图返回三维,如果数据集里混了灰度和彩色,直接imds读进来会报维度不匹配。我一般会统一转灰度或统一转三通道,在预处理阶段用rgb2gray或cat(3, I, I, I)对齐。另外图像尺寸不一致时,要么裁剪到统一大小,要么用augmentedImageDatastore做批量 resize,但 resize 会改变噪声统计特性,能不用就不用。

3. 在 MATLAB 里搭 DnCNN:从数据加载到训练收敛

3.1 数据加载与在线加噪

MATLAB 里加载图像集合最顺手的是imageDatastore,配合augmentedImageDatastore做批量预处理。下面这段是常见的训练数据管道搭建方式:

% 假设干净图放在 clean/ 目录下,统一为灰度 imds = imageDatastore('clean/', 'IncludeSubfolders', true, ... 'LabelSource', 'none', 'ReadFcn', @(f) im2double(rgb2gray(imread(f)))); % 统一尺寸,避免 batch 内维度不一致 targetSize = [64 64]; augImds = augmentedImageDatastore(targetSize, imds, 'ColorPreprocessing', 'gray2gray'); % 自定义加噪读取函数,训练时动态生成噪声图 noisyImds = transform(augImds, @(data) addGaussianNoise(data, 25/255));

addGaussianNoise是自己写的辅助函数,核心就是imnoise或直接I + sigma*randn(size(I))。用transform而不是预先加噪,好处是每个 epoch 看到的噪声 realization 不同,相当于数据增强,能提升泛化。sigma 设 25/255 是 DnCNN 论文里的常用档位,对应约 10% 的噪声强度,实际课程设计里可以按需调。

注意im2double把像素值归到 [0,1],后面所有操作都在这个量纲下做。如果混用 uint8 和 double,PSNR 计算会出错,这是血泪经验。

3.2 网络定义与训练参数

网络定义用layerGraph或直接数组都行,关键是层数和通道数。DnCNN 原版是 17 层、64 通道,MATLAB 里跑这个规模在单卡上没问题,但课程设计如果机器一般,可以降到 8 层、32 通道,效果差一点但能跑完。

layers = [ imageInputLayer([64 64 1], 'Name', 'input') convolution2dLayer(3, 64, 'Padding', 'same', 'Name', 'conv1') reluLayer('Name', 'relu1') convolution2dLayer(3, 64, 'Padding', 'same', 'Name', 'conv2') batchNormalizationLayer('Name', 'bn2') reluLayer('Name', 'relu2') % ... 中间重复若干组 conv+bn+relu convolution2dLayer(3, 1, 'Padding', 'same', 'Name', 'convOut') regressionLayer('Name', 'output') ];

训练用trainNetwork,优化器选adam,初始学习率 1e-3,batch size 64 或 128。'Shuffle', 'every-epoch'要开,否则模型会记住顺序。验证集从训练集里切 10%,用'ValidationFrequency'控制评估频率。如果 loss 震荡不降,先查学习率是不是太大,再查 BN 层是不是在 batch size 太小时不稳定——batch 小于 16 时 BN 的统计量噪声很大,常见做法是换groupNormalizationLayer或直接去掉 BN。

训练轮数看数据量,几千张图跑 30 到 50 epoch 通常够。'Plots', 'training-progress'打开能实时看 loss 曲线,比事后翻日志直观。

3.3 推理与 PSNR 评估

训完模型用predict或activations拿输出,注意 DnCNN 输出的是噪声估计,干净图是输入减输出:

noisy = im2double(imread('test_noisy.png')); noisy = imresize(noisy, [64 64]); predNoise = predict(net, noisy); cleanPred = noisy - predNoise; cleanPred = min(max(cleanPred, 0), 1); % 截断到合法范围 psnrVal = psnr(cleanPred, cleanRef); ssimVal = ssim(cleanPred, cleanRef);

min(max(...))这步不能省,网络输出可能让像素值超出 [0,1],不截断的话imshow会显示异常,PSNR 也会虚高。评估时干净参考图要和预测图同尺寸同量纲,否则psnr函数会报错或给错值。

对比传统方法时,把均值、中值、维纳的结果用同样流程算 PSNR,列个表就能看出 DnCNN 在中等噪声下通常领先 1 到 3 dB,强噪声下优势更明显,但训练成本也高得多。

4. 避坑与排查:训练不收敛、PSNR 异常、显存不够

4.1 loss 不降或震荡

现象:训练几个 epoch 后 loss 卡在某个值不动,或者上下大幅震荡。原因通常是学习率过大、batch size 太小导致 BN 统计不稳、或者数据配对错了(噪声图和干净图没对齐)。解决:先把学习率降到 1e-4 试,再把 batch 提到 32 以上,最后检查transform函数里噪声图和干净图是不是同一张源图生成的。如果用了augmentedImageDatastore的随机裁剪,要确保干净图和噪声图裁的是同一区域,否则网络学的是错位映射。

4.2 PSNR 高得离谱或低得离谱

现象:测试 PSNR 超过 50 dB 或低于 15 dB。原因:超过 50 通常是量纲错了,比如干净图是 uint8 而预测图是 double,psnr按默认 peak 算会虚高;低于 15 可能是噪声图和干净图没对齐,或者推理时忘了做noisy - predNoise而是直接拿了网络输出。解决:统一用im2double归到 [0,1],推理后确认做了减法,再算指标。

4.3 显存不足或训练中断

现象:跑几个 batch 后报 out of memory。原因:图像尺寸太大、batch size 太高、或者网络通道数太多。解决:先把训练图裁到 64×64 或 48×48,batch 降到 16,通道数从 64 降到 32。MATLAB 的trainNetwork不会自动释放中间变量,长时间训练建议开'ExecutionEnvironment', 'gpu'并定期clear无用变量。如果只有 CPU,把网络缩到 6 层以内,否则一个 epoch 能跑半小时。

4.4 验证集 loss 先降后升

现象:训练 loss 持续降,验证 loss 降到某个点后反弹。原因:过拟合。解决:加数据增强(随机翻转、小角度旋转)、加 dropout 层、或者早停。MATLAB 的trainNetwork没有内置早停,常见做法是设'ValidationPatience',连续若干轮验证 loss 不降就停。另外检查训练集和验证集是不是来自同一分布,如果验证集里混了不同噪声水平的图,loss 反弹是正常的,要分开评估。

5. 进阶技巧:噪声水平自适应与模型导出

5.1 用噪声水平图做条件输入

固定 sigma 训出来的模型换个噪声强度就废,这是 DnCNN 的已知边界。进阶做法是把噪声水平作为条件输入:常见方案是加一个噪声水平图(noise level map),即每个像素位置都填当前 sigma 值,和噪声图拼成双通道输入。MATLAB 里实现就是在transform函数里多拼一个通道:

function dataOut = addNoiseWithLevel(data, sigma) I = data{1}; noisy = I + sigma * randn(size(I)); levelMap = sigma * ones(size(I)); dataOut = {cat(3, noisy, levelMap), I}; end

网络第一层改成imageInputLayer([64 64 2]),其余不变。这样训一个模型能覆盖多个 sigma 档位,推理时改 levelMap 的值就行。代价是训练数据要覆盖目标 sigma 范围,否则条件输入学不到东西。

5.2 模型导出与部署验证

MATLAB 训完的SeriesNetwork或dlnetwork可以导出成 ONNX,用exportONNXNetwork(net, 'dncnn.onnx')。导出后建议用onnxruntime或 Python 端跑一遍同样的测试图,对比 MATLAB 和 ONNX 的输出差异。常见问题是 BN 层在导出时被折叠进卷积,数值上会有微小差异,PSNR 差 0.1 dB 以内算正常,差多了要查导出配置。

如果课程设计只需要 MATLAB 环境跑,那不用导出,直接save('dncnnModel.mat', 'net')存下来,下次load就能用。注意存的时候把训练用的 sigma 和归一化参数一起存,否则复现时对不上。

5.3 一个我常走的验证习惯

每次训完模型,我不会只看最终 PSNR,而是固定三张测试图:一张平坦区域多的、一张纹理密集的、一张边缘锐利的。分别跑传统方法和 DnCNN,列个对比表。如果 DnCNN 在平坦图上领先但纹理图上反而输,说明网络过平滑了,要检查残差连接是不是没生效或者 BN 层把高频信息压掉了。这个习惯帮我抓过好几次「指标好看但图糊」的问题。

从那以后我每次训去噪模型都强制走一遍三图对比,不看单张指标就下结论。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询