ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于CNN的图像去噪项目:DnCNN与四种传统算法在Set12上的PSNR/SSIM对比

基于CNN的图像去噪项目:DnCNN与四种传统算法在Set12上的PSNR/SSIM对比

简介:这份资源是面向深度学习与图像处理方向学习者、课程大作业与毕业设计需求者的完整项目源码,围绕卷积神经网络图像去噪算法展开研究。项目以DnCNN为核心,同时实现均值滤波、中值滤波、非局部均值NLM与BM3D四种传统算法作为对照,并采用PSNR与SSIM两项指标量化评估去噪效果,五种算法分目录组织,进入对应目录即可运行,便于横向对比实验结论。压缩包共177个文件,以m脚本、mat数据、png图像及多平台mexw64、mexa64等编译文件为主,另含少量txt与md说明,整体约80.31MB,结构清晰、下载即用。目前已有691人学习关注,适合需要完整实验方案、对照算法实现与评价指标代码的读者参考,也可作为大作业与论文实验的可靠起点。

1. 从一份能跑通的图像去噪源码包说起:CNN 与四种传统算法同台对比

很多人做图像去噪大作业时,最头疼的不是写不出代码,而是找不到一份能直接跑、还能横向对比的完整工程。这份基于卷积神经网络的图像去噪项目源码,恰好解决了这个痛点——它把深度学习方法 DnCNN 和四种传统算法(均值滤波、中值滤波、非局部均值 NLM、三维块匹配 BM3D)放在同一个框架下,统一在 Set12 数据集上做对比实验,用 PSNR 和 SSIM 两个指标量化效果。你拿到手后不需要从零搭环境,进入对应目录运行脚本就能看到去噪前后的图像对比。适合正在做深度学习课程设计、需要快速复现 baseline 的本科生和研究生,也适合想理解传统方法与 CNN 方法差异的从业者。整个项目用 MATLAB 实现,目录结构清晰,每个算法独立存放,互不干扰。

2. 五种去噪算法的目录结构与运行入口:先搞清楚每个文件干什么

2.1 目录组织与文件职责划分

拿到压缩包解压后,你会看到五种算法分别放在五个不同的目录中。这种组织方式的好处是依赖隔离——BM3D 需要额外的变换域工具箱,DnCNN 需要深度学习工具箱,分开存放可以避免路径冲突。从项目正文给出的文件列表来看,核心文件包括:

文件名所属算法作用
BM3D.mBM3D基础三维块匹配去噪主函数
BM3DDEB.mBM3D去噪增强块(Deblocking)变体
BM3DDEB_init.mBM3DDEB 变体的初始化参数脚本
BM3DSHARP.mBM3D锐化增强版本,适合纹理丰富图像
CBM3D.mBM3D彩色图像三维块匹配去噪
VBM3D.mBM3D视频序列三维块匹配去噪
CVBM3D.mBM3D彩色视频三维块匹配去噪
BM3D_CFA.mBM3DCFA 格式(Bayer 阵列)图像去噪
Demo_test_DnCNN3.mDnCNN深度卷积网络去噪演示脚本
Demo_IDDBM3D.mBM3DIDD-BM3D 迭代去噪演示

均值滤波、中值滤波和 NLM 直接调用 MATLAB 自带函数,没有额外文件。BM3D 和 DnCNN 的代码在原版基础上做了小幅修改,主要是适配 Set12 数据集的读取路径和输出格式。

2.2 运行入口与最小验证流程

先确认 MATLAB 版本不低于 R2018b,深度学习工具箱需要单独安装。验证流程按以下步骤走:

% 步骤1:设置工作目录到对应算法文件夹 cd('path_to/BM3D'); % 切换到 BM3D 目录 % 步骤2:加载 Set12 数据集中的一张测试图 img = imread('Set12/01.png'); % 01.png 是标准测试图 if size(img,3) == 3 img = rgb2gray(img); % 统一转灰度处理 end img = double(img); % 步骤3:添加高斯白噪声,sigma=25 是常用测试强度 sigma = 25; noisy_img = img + sigma * randn(size(img)); % 步骤4:调用 BM3D 去噪 denoised_img = BM3D(noisy_img, sigma); % 步骤5:计算 PSNR 和 SSIM psnr_val = psnr(denoised_img, img); ssim_val = ssim(denoised_img, img); fprintf('PSNR: %.2f dB, SSIM: %.4f\n', psnr_val, ssim_val);

这段代码的逻辑很直接:读图、加噪、去噪、评估。sigma参数控制噪声强度,常见取值是 15、25、35、50。BM3D函数的第二个参数必须和加噪时的 sigma 一致,否则去噪效果会明显下降——这是新手最容易翻车的地方。DnCNN 的运行方式类似,但需要先加载预训练模型:

% DnCNN 去噪示例 cd('path_to/DnCNN'); load('model/DnCNN_sigma25.mat'); % 加载对应噪声水平的模型 denoised_img = DnCNN_denoise(noisy_img, net);

注意 DnCNN 的模型是和噪声水平绑定的,sigma=25 的模型不能直接用于 sigma=50 的场景。如果要用不同噪声水平,需要重新训练或找对应模型。

3. PSNR 与 SSIM 评价指标:怎么算、怎么看、怎么不踩坑

3.1 PSNR 的计算原理与参数含义

峰值信噪比 PSNR 衡量的是去噪后图像与干净图像之间的像素级差异。计算公式是:

PSNR = 10 * log10(MAX^2 / MSE)

其中 MAX 是像素最大值,8 位图像取 255;MSE 是均方误差。PSNR 越大,说明去噪后图像和原图越接近,去噪效果越好。但 PSNR 有个已知局限:它只看像素差异,不考虑人眼视觉特性。有时候 PSNR 很高的图像看起来反而模糊,这就是为什么还需要 SSIM。

在 MATLAB 中计算 PSNR 时要注意数据类型。如果图像是uint8格式,psnr函数会自动按 255 计算;如果转成了double且范围在 [0,1],则需要指定峰值:

% uint8 格式直接算 psnr_val = psnr(denoised_uint8, clean_uint8); % double 格式需要指定峰值 psnr_val = psnr(denoised_double, clean_double, 1.0);

很多同学在这里踩坑:去噪函数返回的是 double,原图是 uint8,直接相减会得到错误结果。统一转成 double 再做计算,或者统一用 uint8,不要混用。

3.2 SSIM 的取值解读与边界情况

结构相似性 SSIM 从亮度、对比度、结构三个维度比较两幅图像,取值 0 到 1,越接近 1 表示结构越相似。SSIM 对模糊和结构失真更敏感,和主观视觉质量的相关性比 PSNR 更好。但 SSIM 也有边界情况:当图像本身纹理极少(比如纯色区域),SSIM 会异常偏高,这时候不能只看 SSIM 就下结论。

% SSIM 计算,注意指定窗口大小 ssim_val = ssim(denoised_img, clean_img, 'Window', fspecial('gaussian', 11, 1.5));

默认的 SSIM 使用 11x11 高斯窗口,标准差 1.5。对于小尺寸图像(小于 11x11),需要调小窗口,否则会报错。Set12 数据集里的图像尺寸都是 256x256 或 512x512,用默认参数没问题。

3.3 五种算法的指标对比与选型参考

在 sigma=25 的高斯噪声下,五种算法在 Set12 上的典型表现大致如下:

算法平均 PSNR (dB)平均 SSIM运行速度适用场景
均值滤波26.50.72极快快速预览,对细节要求低
中值滤波27.80.78快椒盐噪声,边缘保持一般
NLM29.30.84中等纹理图像,参数需调
BM3D31.20.89慢高质量去噪,计算量大
DnCNN31.80.91快(GPU)有训练数据,泛化好

这张表是选型的核心依据。如果你只是做课程设计对比,BM3D 和 DnCNN 是必须保留的;如果追求极致速度,均值滤波可以作为下界参考。注意 DnCNN 的速度优势依赖 GPU,用 CPU 跑会慢很多。

4. 避坑与排查:五个让去噪结果翻车的常见问题

4.1 现象:PSNR 计算结果为负数或异常低

原因:图像数据类型不匹配。去噪函数返回 double 类型且范围在 [0,1],而原图是 uint8 范围 [0,255],直接调用psnr会按 255 计算,导致 MSE 巨大。

解决:统一数据类型。要么都转 uint8,要么都转 double 并指定峰值。

% 统一转 uint8 denoised_uint8 = im2uint8(denoised_double); psnr_val = psnr(denoised_uint8, clean_uint8);

4.2 现象:BM3D 运行报错“未定义函数或变量”

原因:BM3D 依赖多个子函数和变换域工具箱,如果只复制了BM3D.m而没复制同目录下的辅助文件,就会报错。

解决:确保整个 BM3D 目录完整,不要单独移动某个文件。检查 MATLAB 路径是否包含所有子目录:

addpath(genpath('path_to/BM3D'));

4.3 现象:DnCNN 去噪结果全黑或全白

原因:预训练模型的输入归一化方式与测试图像不匹配。DnCNN 训练时输入是 [0,1] 范围的 double,如果直接喂 uint8 图像,输出会饱和。

解决:在送入网络前做归一化:

input_img = double(noisy_img) / 255; output_img = predict(net, input_img); denoised_img = output_img * 255;

4.4 现象:NLM 去噪后图像过度平滑,细节丢失

原因:NLM 的滤波参数DegreeOfSmoothing设置过大。默认值是按噪声方差自动估计的,但如果手动指定了过大的值,会过度平滑。

解决:让 MATLAB 自动估计,或根据噪声水平调整:

% 自动估计平滑参数 denoised = imnlmfilt(noisy_img); % 手动指定,sigma 越大值越大 denoised = imnlmfilt(noisy_img, 'DegreeOfSmoothing', 0.01);

4.5 现象:Set12 数据集读取失败,提示文件不存在

原因:项目中的路径是相对路径,如果当前工作目录不是项目根目录,就会找不到文件。

解决:用绝对路径或先切换到项目根目录:

project_root = 'path_to_project'; cd(project_root); addpath(genpath(project_root));

5. 进阶技巧:用批处理脚本一次性跑完五种算法并自动生成对比报告

单张图跑通之后,下一步是把 Set12 全部 12 张图跑一遍,自动记录 PSNR 和 SSIM,生成对比表格。手动一张张跑太慢,写个批处理脚本是常规做法。

% batch_denoise.m % 批量运行五种算法,输出对比结果 clear; clc; project_root = pwd; addpath(genpath(project_root)); sigma = 25; algorithms = {'Mean', 'Median', 'NLM', 'BM3D', 'DnCNN'}; num_imgs = 12; results = zeros(num_imgs, length(algorithms), 2); % PSNR 和 SSIM for idx = 1:num_imgs % 读取干净图像 img_path = fullfile(project_root, 'Set12', sprintf('%02d.png', idx)); clean = imread(img_path); if size(clean, 3) == 3 clean = rgb2gray(clean); end clean = double(clean); % 添加高斯噪声 rng(42); % 固定随机种子,保证可复现 noisy = clean + sigma * randn(size(clean)); % 均值滤波 denoised_mean = imfilter(noisy, fspecial('average', 3)); results(idx, 1, 1) = psnr(denoised_mean, clean, 255); results(idx, 1, 2) = ssim(denoised_mean, clean, 'DynamicRange', 255); % 中值滤波 denoised_median = medfilt2(noisy, [3 3]); results(idx, 2, 1) = psnr(denoised_median, clean, 255); results(idx, 2, 2) = ssim(denoised_median, clean, 'DynamicRange', 255); % NLM denoised_nlm = imnlmfilt(noisy); results(idx, 3, 1) = psnr(denoised_nlm, clean, 255); results(idx, 3, 2) = ssim(denoised_nlm, clean, 'DynamicRange', 255); % BM3D denoised_bm3d = BM3D(noisy, sigma); results(idx, 4, 1) = psnr(denoised_bm3d, clean, 255); results(idx, 4, 2) = ssim(denoised_bm3d, clean, 'DynamicRange', 255); % DnCNN load(fullfile(project_root, 'DnCNN', 'model', 'DnCNN_sigma25.mat')); denoised_dncnn = predict(net, single(noisy/255)) * 255; results(idx, 5, 1) = psnr(denoised_dncnn, clean, 255); results(idx, 5, 2) = ssim(denoised_dncnn, clean, 'DynamicRange', 255); fprintf('Image %02d done.\n', idx); end % 输出平均结果 fprintf('\n===== Average Results (sigma=%d) =====\n', sigma); fprintf('%-10s %-12s %-12s\n', 'Algorithm', 'PSNR(dB)', 'SSIM'); for a = 1:length(algorithms) avg_psnr = mean(results(:, a, 1)); avg_ssim = mean(results(:, a, 2)); fprintf('%-10s %-12.2f %-12.4f\n', algorithms{a}, avg_psnr, avg_ssim); end % 保存结果到 mat 文件 save('comparison_results.mat', 'results', 'algorithms', 'sigma');

这段脚本的关键点有三个。第一,rng(42)固定随机种子,保证每次加噪结果一致,否则 PSNR 会有小幅波动,对比不公平。第二,psnr和ssim都显式指定了DynamicRange为 255,因为输入是 double 但范围是 [0,255],不指定会按 1.0 计算导致结果错误。第三,DnCNN 的predict输入需要single类型,MATLAB 的深度学习工具箱对数据类型比较敏感,double 会报错。

跑完这个脚本,你会得到一张完整的对比表,直接放进大作业报告里就是现成的实验数据。如果想进一步分析,可以把results矩阵导出成 CSV,用 Python 画柱状图对比:

import scipy.io as sio import matplotlib.pyplot as plt import numpy as np data = sio.loadmat('comparison_results.mat') results = data['results'] algorithms = ['Mean', 'Median', 'NLM', 'BM3D', 'DnCNN'] avg_psnr = np.mean(results[:, :, 0], axis=0) avg_ssim = np.mean(results[:, :, 1], axis=0) fig, axes = plt.subplots(1, 2, figsize=(12, 5)) axes[0].bar(algorithms, avg_psnr, color='steelblue') axes[0].set_ylabel('PSNR (dB)') axes[0].set_title('Average PSNR on Set12') axes[1].bar(algorithms, avg_ssim, color='coral') axes[1].set_ylabel('SSIM') axes[1].set_title('Average SSIM on Set12') plt.tight_layout() plt.savefig('comparison_chart.png', dpi=150) plt.show()

从那以后我每次跑对比实验都强制走一遍固定随机种子、统一数据类型、显式指定 DynamicRange 这三步,再也没出现过指标异常的问题。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进