简介面向图像处理课程设计与毕业设计场景一份MATLAB资源完整覆盖传统滤波与深度学习的图像去噪实现方案。内容既包含均值滤波、中值滤波、高斯滤波等经典算法代码也提供DnCNN卷积神经网络的网络构建、训练与预测脚本适合需要对比算法效果、快速搭建实验环境的学习者。资源包共177个文件以.m脚本、.mat数据文件、.png示例图像为主并附有mex编译文件与说明文档约80.31MB。目前已有854人学习下载。借助清晰的文件目录使用者可直接运行Demo_test_DnCNN3等示例复现DnCNN去噪流程同时结合BM3D等配套算法参考深入理解传统方法与深度学习方法在噪声抑制、边缘保留上的差异为后续研究或毕设答辩提供扎实代码支撑。1. 为什么算传统图像去噪再上DnCNNMATLAB里完整的学习路径图像去噪把这组像素当作退化观测目标是从 y x v 中还原真实信号 x。传统去噪的实现放在信号处理框架里先用空域或变换域的先验把 v 压下去再期望 x 不要被损伤太多DnCNN则把问题重新定义成一个监督回归任务训练一个 20 层卷积网络去预测噪声图 v而不是直接预测 x。二者在 MATLAB 里都能用原生工具箱函数实现不依赖任何第三方 Python 环境测试结果也可以直接横向比较。常见做法是先跑通 wiener2、medfilt2 这类内置函数把 PSNR 和 SSIM 的基线数字拿到手再接上 Deep Learning Toolbox 搭 DnCNN。你会发现一个反直觉的现象噪声强度 σ 小于 15/255 时传统方法和小网络的差距很小σ 上到 25/255 以后DnCNN 对边缘和纹理区域的保留能力明显更强。这篇文章按“传统算法 → 残差学习原理 → MATLAB 建模 → 训练评估 → 工程技巧”的顺序展开代码均在 R2023b 上验证过。适合正在做图像处理大作业、毕业设计算法对比以及想确认深度去噪到底值不值得换方案的人阅读。2. 传统去噪算法MATLAB内置算子的原理、代码与边界2.1 空间域滤波均值、中值、维纳的数学假设与MATLAB实现空间域滤波的基本假设是噪声是像素级的随机扰动而图像结构在局部具有相关性因此用邻域统计量替代中心像素值可以把噪声分量抵消掉一部分。均值滤波假设噪声均值为 0、方差为 σ²对邻域 N 个像素做算术平均后噪声方差理论上降到 σ²/N。窗口越大降噪越强但边缘与角点这些非平稳结构也会被平均掉图像变糊。中值滤波针对的是脉冲噪声也就是椒盐噪声。它取邻域排序后的中位数对离群像素不敏感所以能在不引入线性平均模糊的情况下清除孤立的亮暗点。维纳滤波更精细一点它假设局部区域服从平稳随机场先估计局部均值 μ 和局部方差 σ_loc²然后用收缩系数 σ²/(σ_loc² σ²) 把当前像素往局部均值方向拉。平坦区域局部方差小收缩系数接近 1滤波效果接近均值边缘区域局部方差大收缩系数趋近 0细节被保留。以下代码用内置函数把三条路线一次跑完img im2double(imread(cameraman.tif)); rng(0); sigma 25/255; noisy img sigma * randn(size(img)); % 均值滤波3x3 平均核边缘用replicate填充 avgFilter fspecial(average, [3 3]); denoisedAvg imfilter(noisy, avgFilter, replicate); % 中值滤波3x3 邻域中位数 denoisedMed medfilt2(noisy, [3 3]); % 维纳滤波估计局部均值与方差做MMSE收缩 denoisedWie wiener2(noisy, [3 3]);imfilter的第三个参数replicate表示边界像素按最近值复制避免滤波时在图像边缘产生黑边。medfilt2和wiener2的第二个参数都是邻域窗口窗口从[3 3]增大到[7 7]时平滑力度递增SSIM 通常先升后降说明窗口不是越大越好。实际调参时我会在噪声较明显的区域固定窗口再观察纹理边缘是否发虚而不是只看 PSNR 数字。2.2 变换域去噪wdenoise2小波阈值收缩空间域方法对局部像素直接操作变换域方法则先把图像变换到频域或小波域再对系数做收缩。小波变换的优势在于同时保留空间位置和频带信息噪声能量散布在所有细节子带幅值小真实边缘在对应位置上系数幅值大。于是可以通过阈值把小系数置零再逆变换回空间域。MATLAB 的小波工具箱把这套流程封装在wdenoise2里不需要手动拆子带% 4层小波分解sym4小波基Bayes阈值估计软阈值收缩 denoisedWav wdenoise2(noisy, 4, ... Wavelet, sym4, ... DenoisingMethod, Bayes, ... ThresholdRule, Soft, ... NoiseEstimate, LevelDependent); % 统一评价 psnrAvg psnr(denoisedAvg, img, 1); psnrMed psnr(denoisedMed, img, 1); psnrWie psnr(denoisedWie, img, 1); psnrWav psnr(denoisedWav, img, 1); fprintf(均值: %.2f dB | 中值: %.2f dB | 维纳: %.2f dB | 小波: %.2f dB\n, ... psnrAvg, psnrMed, psnrWie, psnrWav);psnr的第三个参数1表示峰值电平为 1因为im2double后像素范围是 [0,1]不写的话 MATLAB 对 double 类型默认也用 1但显式写出来更保险。DenoisingMethod选Bayes表示用贝叶斯估计阈值比固定阈值UniversalThreshold更保守适合纹理多的图ThresholdRule选Soft是在阈值处做连续收缩比硬阈值产生的振铃更少。对于 sigma 在 10-25/255 之间的高斯噪声wdenoise2的 PSNR 通常能超过中值和维纳滤波 0.5-1 dB但代价是边缘处可能出现轻微过平滑。它的极限不在实现而在小波基的固定形状——sym4 的支撑长度和消失矩是事先定死的遇到不规则纹理时表达力不够。2.3 传统方法的局限与PSNR/SSIM读数习惯把四种传统方法放一起看它们的共性是去噪能力完全由手工设计的先验决定。均值和中值没有显式的噪声模型维纳假设局部平稳小波假设系数稀疏。这些先验对理想高斯噪声有效但真实图像的噪声往往是有色噪声或者混合了传感器暗电流噪声与量化噪声手工模型很难全部覆盖。读 PSNR 要养成一个习惯0.5 dB 的提升在视觉上几乎不可见超过 1 dB 才能在边缘和纹理上看出差别。SSIM 对结构相似性更敏感同一张图降噪后如果边缘变糊但 PSNR 略升SSIM 会明确下降这能帮你判断算法是不是“以平滑换指标”。下面给出一组典型对比关系具体数值随测试集和噪声种子波动重点看相对趋势方法适用噪声边缘保留参数敏感度相对耗时均值滤波高斯差低最快中值滤波脉冲噪声中等低快维纳滤波高斯中等中窗口大小快小波收缩高斯/混合较好中阈值规则中等这也是为什么 DnCNN 出现后大家愿意转向深度方法网络不是预先指定一种先验而是从大量数据中学习噪声到残差的映射理论上可以覆盖更广的噪声分布。3. 残差学习与DnCNN20层卷积网络的数学设计和MATLAB建模3.1 模型为什么要学“噪声”而不是学“干净图像”如果直接训练一个网络 f(y)x输入带噪图像输出干净图像网络需要记住大量图像结构信息边缘方向、纹理周期、灰度分布。这些信息变化范围大不同图片之间的差异远大于噪声的差异网络必须把容量花在拟合图像先验上。DnCNN 换了一个目标估计残差 v(y)y-x也就是噪声图本身然后通过减法 x y - v(y) 重建干净图。噪声图的分布比自然图像简单得多白噪声是零均值、局部平稳、像素间近似独立的随机场。网络要拟合的目标函数更平滑优化更容易收敛。另一个关键点是批量归一化Batch Normalization。BN 层把每层输出分布拉回到零均值单位方差使深层网络的梯度不会在反向传播时消失同时残差输出集中在 0 附近预测结果天然不会偏离输入太远视觉上不会出现整体偏色或偏亮。有经验的工程师会注意到这里不需要 ImageNet 预训练权重。像 ResNet-50 那种分类模型迁移到去噪任务意义不大因为底层视觉任务要求保留像素级细节分类网络的下采样会丢失位置信息。DnCNN 的 20 层结构完全从零训练数据量需求也远小于大模型。3.2 20层结构拆解Conv-BN-ReLU模块与41×41感受野DnCNN 的网络结构可以分成三段第一段是一个卷积加 ReLU从输入图像提取浅层特征中间 17 层反复堆叠 Conv-BN-ReLU 模块每个卷积都是 3×3 大小、64 个滤波器padding 设为 same 保证特征图尺寸不变最后一段是一个 3×3 卷积输出通道数为 1对应残差图。整个网络没有池化层也没有全连接层属于全卷积结构。3×3 卷积核叠加 17 层感受野按公式 RF 2×depth 1 计算20 层时刚好是 41×41。也就是说输出某个像素的残差估计时用到的是以该像素为中心 41×41 范围内的信息。这个范围和手工去噪算法的窗口逻辑一样邻域越大上下文越多去噪越准但过大的感受野会增加计算量。40×40 或 41×41 的训练补片刚好覆盖感受野不需要更大。为什么中间层固定 64 个滤波器这是去噪性能和显存占用的折中。64 个 3×3 卷积核的参数量大约是 64×64×936864 个17 层累计不到 63 万参数训练规模和显存压力都很低。如果增加到 128 个滤波器PSNR 提升通常不超过 0.1 dB训练时间却接近翻倍。3.3 用layerGraph在MATLAB中逐层定义DnCNNMATLAB 中定义网络有两条路直接拼 layer 数组或者用 layerGraph 构建有向无环图。DnCNN 是纯链式结构用 layer 数组就行但用 layerGraph 显式声明拓扑后面如果想加跳跃连接或辅助监督改起来更方便。inputSize [40 40 1]; netLayers [ imageInputLayer(inputSize) convolution2dLayer(3, 64, Padding, same, ... WeightsInitializer, he) reluLayer() ]; for k 1:17 netLayers [netLayers; convolution2dLayer(3, 64, Padding, same, ... WeightsInitializer, he); batchNormalizationLayer(); reluLayer()]; end netLayers [netLayers; convolution2dLayer(3, 1, Padding, same, ... WeightsInitializer, he); regressionLayer()]; lgraph layerGraph(netLayers);convolution2dLayer(3, 64, Padding, same)表示 3×3 卷积核、64 个输出通道、padding 方式为 same保证输出特征图与输入尺寸一致。WeightsInitializer, he用 He 初始化给卷积核赋初值这个初始化方法针对 ReLU 激活函数的方差匹配设计能缓解深层网络梯度消失。倒数第二层卷积输出 1 个通道对应灰度图的残差而且这一层不接 ReLU因为噪声图有正有负直接输出实数更合理。regressionLayer()是关键的监督头它计算预测残差和目标残差之间的均方误差。如果误把classificationLayer放上去训练会直接报错因为去噪不是分类问题标签也不是类别索引。3.4 回归目标与损失函数MSE和残差图训练 DnCNN 的监督目标不是干净图像而是噪声图本身。假设输入带噪补片 y对应的干净补片是 x则训练时要求网络输出尽可能接近 y-x。损失函数用 MSEL (1/N) ∑ ||R(y_i) - (y_i - x_i)||²其中 R(y_i) 是网络对第 i 个补片的输出。MSE 对像素级误差一视同仁数值上等价于最大化峰值信噪比这正是去噪任务的标准评价指标。如果追求视觉效果更锐利也可以换成 MAEL1 损失在 MATLAB 中可以用maeRegressionLayer但收敛速度通常比 MSE 慢。这里有个容易混淆的点训练完成后的推理过程是 y - R(y)不是直接输出 R(y)。很多人在写测试代码时忘了这一步减法结果预测的是一张噪声图PSNR 反而比输入还低。我在实践中的检查方法是把网络输出单独显示出来如果看到的是类似噪声的均匀颗粒说明网络学习正确如果看到干净图像结构说明损失函数或数据准备出了问题。4. 在MATLAB中训练DnCNNpatch抽取、超参数与整图预测4.1 randomPatchExtractionDatastore生成成对训练样本深度学习去噪需要大量“带噪-干净”图像对。常见数据集如 BSD400 包含 400 张训练图像直接整图输入网络效率低且显存压力大标准做法是随机裁剪成 40×40 的小补片。MATLAB 的randomPatchExtractionDatastore专门干这个事它从一个或多个 imageDatastore 中随机裁剪对应位置补片并自动组织成训练需要的格式。% 读入灰度训练图像转为double便于加高斯噪声 imds imageDatastore(D:/data/BSD400/*.png, ... ReadFcn, (f) im2double(im2gray(imread(f)))); % 同一份数据既当输入又当标签裁剪40x40补片 patchDs randomPatchExtractionDatastore(imds, imds, [40 40], ... PatchesPerImage, 64); % 在变换函数中加噪声并构造残差目标 sigma 25/255; dsTrain transform(patchDs, (d) addNoise(d, sigma)); function dataOut addNoise(data, sigma) clean data.ResponseImage; noisy clean sigma * randn(size(clean), like, clean); dataOut.InputImage noisy; dataOut.ResponseImage noisy - clean; endrandomPatchExtractionDatastore的输出包含InputImage和ResponseImage两个字段前者是网络输入后者是回归目标。transform函数把原本相同的两个干净补片变成一对“带噪输入-残差标签”ResponseImage被改写成noisy-clean也就是要学习的噪声图。PatchesPerImage, 64表示每张训练图像随机裁 64 个补片400 张图一个 epoch 就是 25600 个补片这个量级足够 20 层小网络拟合。im2gray在 R2020b 及以后版本可用能把 RGB 图转成单通道灰度图。BSD400 本身是灰度图加不加这层处理都行但写上之后整个数据流程可以适配任意彩色数据集不会出现通道数不匹配的报错。4.2 trainingOptions关键超参数与常见翻车点DnCNN 在 MATLAB 中用trainNetwork训练超参数设置我习惯这样写options trainingOptions(sgdm, ... InitialLearnRate, 1e-3, ... LearnRateSchedule, piecewise, ... LearnRateDropFactor, 0.1, ... LearnRateDropPeriod, 30, ... Momentum, 0.9, ... MaxEpochs, 60, ... MiniBatchSize, 128, ... Shuffle, every-epoch, ... L2Regularization, 1e-4, ... VerboseFrequency, 10, ... ExecutionEnvironment, gpu); net trainNetwork(dsTrain, lgraph, options);学习率 1e-3 是回归任务比较稳的起点。如果训练曲线前几个迭代直接跳成 NaN最常见的原因是学习率太高或输入图像里有 NaN 像素把InitialLearnRate降到 3e-4 再试。LearnRateDropPeriod为 30 表示每 30 个 epoch 学习率乘以 0.1让网络在后期用小步长精调。MiniBatchSize 128 对应 128 张 40×40 灰度补片显存占用大约 2-3 GB如果报显存不足先降到 64。MNIST 和 CIFAR 这类小数据集上60 个 epoch 的 DnCNN 足以看到收敛趋势但真实图像数据建议 80-100 个 epoch训练过程以验证集 PSNR 不再上升作为早停依据。ExecutionEnvironment设成gpu需要 Parallel Computing Toolbox 和 CUDA 计算能力 3.5 以上的 NVIDIA 显卡CPU 也能训练只是速度会慢 5-10 倍。4.3 测试流程与多方法PSNR对比DnCNN 是全卷积网络测试时不必裁剪补片整张图直接送入predict输出是同样尺寸的残差图做一次减法即可得到去噪结果。testImg im2double(im2gray(imread(D:/data/Set12/04.png))); rng(1); noisyImg testImg sigma * randn(size(testImg), like, testImg); estNoise predict(net, noisyImg); denoisedImg noisyImg - estNoise; fprintf(PSNR 带噪: %.2f dB\n, psnr(noisyImg, testImg, 1)); fprintf(PSNR 去噪: %.2f dB\n, psnr(denoisedImg, testImg, 1)); fprintf(SSIM 去噪: %.4f\n, ssim(denoisedImg, testImg)); montage({testImg, noisyImg, denoisedImg}, Size, [1 3]);montage把原图、带噪图和去噪图并排显示适合直接观察视觉差距。用psnr对比时注意参考图必须是同一尺寸同一点位ssim还要注意中心高斯窗口的边界效应图像小于 64×64 时结果不太可靠。典型结果在 BSD68 或 Set12 上σ25 时 DnCNN 比wiener2高 1.5-2.5 dB比wdenoise2高 0.5-1 dB具体数值因训练数据量和噪声种子浮动但相对关系稳定。算法PSNRσ25dB结构保留特点wiener2 (5×5)约 26.8-27.3边缘轻微过平滑wdenoise2 sym4约 27.8-28.4纹理区有振铃DnCNN-20层约 28.8-29.3纹理和边缘均较锐利4.4 灰度与彩色的差异通道数决定网络最后一层上面所有代码假设输入是灰度图imageInputLayer的第三个维度是 1。如果要处理 RGB 彩色图有两个选择一是把三个通道分开各自用灰度网络去噪再合并二是把输入层改成[40 40 3]同时把最后一层卷积的输出通道改成 3。第二种方案能利用通道间的相关性去彩色噪声时效果更好但 PS 等训练时间也约三倍。DnCNN 原论文主要针对灰度图彩色去噪更常采用三通道变体。如果你的数据源是 JPEG 压缩噪声而不是高斯白噪声训练时加的噪声模型也要相应换成压缩伪影这部分在部署阶段再做调整。5. 部署阶段三个技巧噪声水平估计、盲去噪和大图推理5.1 用MAD估计未知σ测试真实图片时往往不知道噪声水平。常见做法是用拉普拉斯算子提取高频分量再用绝对中位差估计 σfunction sigmaEst estimateNoise(img) lap [0 -1 0; -1 4 -1; 0 -1 0]; lapImg imfilter(im2double(img), lap, replicate); madVal median(abs(lapImg(:) - median(lapImg(:)))); sigmaEst madVal / 0.6745; end0.6745 是标准正态分布的四分位距与标准差之比。算出 σ 后可以直接用它生成噪声训练数据也可以作为后续算法调参的输入。5.2 随机σ混合训练的盲DnCNN真实场景噪声强度常常未知又多变更好的做法是在训练时随机生成 σ让网络学会自适应function dataOut addRandomNoise(data, sigmaRange) clean data.ResponseImage; s sigmaRange(1) (sigmaRange(2) - sigmaRange(1)) * rand; noisy clean s * randn(size(clean), like, clean); dataOut.InputImage noisy; dataOut.ResponseImage noisy - clean; end把sigmaRange设为[0 55/255]每个补片用不同的噪声级别。训练出的模型在推理时不需要知道 σ直接对任意强度噪声做残差预测泛化能力比固定 σ 模型更好。5.3 大尺寸图像的分块推理与接缝处理全卷积网络理论上可以输入任意尺寸但 2K 以上图像整图推理会占满显存。做法是切成 512×512 的块分别预测最后拼接。为避免接缝处因缺失上下文出现差异让相邻块重叠 32 像素只取每块中心 480×480 区域拼回原图重叠部分自然过渡。对边界条件敏感的应用这个技巧比直接裁剪省一次二次滤波。三种技巧合在一起DnCNN 就可以从离线对比实验快速推向实际图像处理流程而不必为每张图手动调参。本文还有配套的精品资源点击获取
