ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

BM3D图像去噪算法详解:从核心原理到Matlab实战

BM3D图像去噪算法详解:从核心原理到Matlab实战 简介BM3DBlock Matching and 3D filtering是图像去噪领域公认效果优异的算法之一尤其擅长处理高斯噪声。这套MATLAB代码将算法封装为可直接调用的函数适合图像处理研究者、算法工程师以及需要快速获得高质量去噪效果的MATLAB用户。压缩包共79个文件大小仅3.51MB以面向不同平台的mex预编译可执行文件为主并包含m源文件、README说明和辅助脚本用户可按操作系统直接选用对应版本省去手动编译的麻烦。资源提供BM3D、CBM3D、VBM3D等入口覆盖灰度图、彩色图与视频序列去噪还实现了硬阈值、维纳滤波、彩色噪声处理等多种变体功能较完整。目前已有298人学习下载既可以作为入门工具快速体验经典算法也为后续改进和二次开发提供了良好基础。1. BM3D凭什么被称为去噪界的地板流图像去噪这个方向每年新论文几百篇但你要是问一个做了十年图像处理的人最稳的算法是哪个多半会得到一个名字BM3D。我从研一开始用Matlab跑图像算法试过中值滤波、NLM、小波阈值、DnCNN兜兜转转一圈下来凡是要拿实验结果去比较、去写论文、去做工程基线最后还是绕回BM3D。BM3D全称是Block-Matching and 3D Filtering块匹配与三维滤波。它是2007年由K. Dabov等人提出的核心思想一句话概括把相似的小图像块找出来叠成一个三维数组然后在三维空间里做联合滤波最后再放回原图。这个思路在当年是颠覆性的——传统去噪算法要么在空间域做局部滤波要么在变换域做全局阈值而BM3D把空间域的相似性和变换域的稀疏性结合起来效果直接上一个台阶。它的厉害之处在于在Matlab里用官方提供的bm3d_demo.m跑一遍无论你拿什么测试图Cameraman、Barbara、Lena都行PSNR通常能比NLM高出2到3个dB。而PSNR这个东西每提升0.5个dB人眼都能感受到明显差异2到3个dB基本就是画质从糊到清晰的跨越。这篇分享不打算抄论文也不打算做翻译而是从我在Matlab里实际把BM3D跑通的经历出发把环境准备、代码逻辑、踩坑记录、参数调优一次讲透。如果你正准备用Matlab做图像去噪、写大作业、做毕设或者只是想给老旧的图像处理流程换一个更硬的算法这篇文章应该能帮你省掉不少我自己当年没少走弯路。2. BM3D算法的核心原理拆解两步走每一步都有讲究2.1 第一步基础估计Step One——先粗去噪在真正上手Matlab代码之前我建议先花五分钟把算法骨架搞清楚。否则你看着别人的代码满屏都是g_r、g_r_h、sigma这些变量云里雾里。BM3D的第一阶段叫基础估计目标是把带噪图像初步去一遍噪得到一个质量还行、但不够完美的底图。具体动作分三步块匹配Block Matching在图像中取一个参考块比如8x8的小方块然后在周围的搜索区域里找到若干与此参考块最相似的块度量方式通常是欧氏距离。这一步和NLM很像但BM3D把相似块当成一个整体来用。分组Grouping把找到的相似块堆叠成一个三维矩阵。这个三维的尺寸是块大小 × 块大小 × 相似块的个数。协同硬阈值滤波Collaborative Hard-Thresholding对这个三维矩阵做三维变换通常先在二维空间做DCT或Bior1.5小波变换再在第三维做哈达玛变换然后在变换域里把小于阈值的小系数直接置零再反变换回空间域。这样做的逻辑是噪声在变换域里表现为大量零散的小系数而真实信号的能量会集中在少数大系数上把小的砍掉噪声就被去掉了。基础估计的结果虽然还有残留噪声和振铃效应但已经保留了大部分细节它为第二步提供了更好的相似块匹配依据。2.2 第二步最终估计Step Two——用维纳滤波精修第二阶段叫最终估计核心是把基础估计当作导引图oracle利用它来加权原始带噪图实现精度更高的去噪。这一步用的是维纳滤波Wiener Filter而不是硬阈值。为什么用维纳因为维纳滤波可以看作是最小均方误差意义下的最优线性滤波。简单类比硬阈值是一刀切小于阈值的系数全咔嚓掉大于的保留原值而维纳滤波是按比例缩放对每个变换系数乘以一个介于0和1之间的权重。噪声大的系数乘一个小权重噪声小的系数乘一个接近1的权重这样在去除噪声和保留细节之间取得更精细的平衡。最终估计的流程是用基础估计的结果重新做块匹配和分组。因为基础估计已经比原图干净找出来的相似块会更准确。把基础估计的三维组和带噪图像的三维组分别做三维变换。用基础估计的变换系数的能量计算维纳滤波的收缩系数Wiener ≥ (基础估计系数)^2 / ((基础估计系数)^2 sigma^2)。将此系数乘以带噪三维组的变换系数再反变换回空间域。由于同一个像素会被多个重叠的块覆盖最后通过加权平均叠加时根据窗口函数赋权回归到完整的去噪图。说白了第一阶段是在找对相似的伙伴第二阶段是利用这些伙伴大家一起帮各自把噪声分清楚。两步递进效果叠加。2.3 为什么BM3D比传统方法好一个直观类比如果你觉得上面这些概念有点抽象我打一个比方。想象你有一张合照上面有很多噪点拍摄的时候大家都没看镜头各有各的表情。传统中值滤波的思路是把每个像素附近的一圈像素取中值相当于用邻居的平均脸去替换每一个人的脸结果就是脸糊成一团五官不清晰。NLM的思路是发现照片里有很多相似的区域比如重复的纹理、相似的背景把相似区域的像素加权平均相当于让相似表情的几个人互相借鉴细节保护比中值好但毕竟每个区域还是各自为战。BM3D的思路则是把照片里所有长得像的脸都叠在一起组成一个多脸合一的立体模型然后在这个三维模型里同时去掉每张脸上的噪点再把人脸放回去。由于每张脸共享信息噪声被联合抑制而每张脸独有的细节又被保留下来。这就是三维协同滤波的威力所在。3. Matlab环境准备与源码获取跑通前的三个小坑3.1 首先确认你的Matlab版本和工具箱BM3D的官方Matlab代码本身不依赖什么冷门工具箱核心用到的函数包括imfilter、dct2、idct2、fft2等这些都属于基础图像处理和信号处理范畴。我自己在R2016b、R2020b、R2023b三套环境里都跑通过一般来说R2016之后的版本都没有问题。唯一需要注意的坑是部分第三方优化版本会用到padarray、blockproc或者并行计算工具箱Parallel Computing Toolbox的parfor。如果报Undefined function parfor之类的错误说明你的Matlab没有并行计算工具箱或者需要在代码里把parfor改成for。我遇到过几个师兄在教务版Matlab上跑BM3D结果卡在这一步折腾了半天其实是工具箱缺失。建议下载代码后先搜一下有没有parfor有的话在命令行执行matlabpool或者parpool测试一下并行池是否能正常开启。不能开就直接改for反正结果一致只是慢一些。3.2 官方源码与第三方实现的选择目前能拿到的BM3D Matlab代码主要有三类作者官方发布的BM3D包包含BM3D.m、bm3d_demo.m、demo.m等支持灰度图和彩色图支持不同噪声水平的参数预设。这个是最标准的实现论文里的实验结果基本都能复现。第三方优化版例如某些研究者在GitHub上发布的加速版、GPU版代码风格更现代但有时为了性能牺牲了一些可读性。教学精简版一些课程网站上会挂出只包含核心步骤的简化版本适合初学者阅读但去噪效果和原版有一定差距。我的建议是第一步先跑官方原版。因为你要对照论文理解参数、复现PSNR数据直接用原版最省心。等跑通之后再去看简化版和加速版做对比实验会更有意义。3.3 文件结构与依赖关系官方包解压后一般包含这些文件bm3d_demo.m演示脚本直接运行它会生成一张带噪测试图、去噪结果图和PSNR、SSIM数值。BM3D.m主函数封装了两步去噪流程输入是带噪图和噪声标准差σ输出是去噪图。demo.m另一个演示入口展示了不同噪声水平下的效果对比。function_*系列文件如function_LPA_filter.m、function_BLS_GSM.m等这些是辅助函数不要随便改也不要把它们从Maltab路径里移走。运行demo之前建议把整个文件夹添加进Matlab路径。用addpath(genpath(你的路径))一次性解决别只把主函数放在当前目录否则运行BM3D.m时它会找不到function_系列文件直接报错。4. 核心代码逐段解读BM3D主函数到底做了什么4.1 主函数结构总览打开BM3D.m你会发现它的参数列表大概是这样的function [y_est, y_est_basic, PSNR_IN, PSNR_EST, SSIM_EST] BM3D(y, z, sigma, profile, print_to_screen)其中y原始无噪图像用来计算PSNRz带噪图像就是你要去噪的输入sigma噪声标准差也就是你加噪时的标准差如果是真实噪声需要先估计profile预设参数档位常用np噪声快运行速度快效果稍差和vn噪声慢更精细效果最好输出y_est是最终去噪图像y_est_basic是第一步基础估计的结果PSNR和SSIM是评估指标函数内部结构非常清晰注释也写得不错。核心逻辑是% 第一步基础估计 [ y_est_basic, ... ] bm3d_thr_grouping(...); % 第二步最终估计 [ y_est, ... ] bm3d_wiener_grouping(...);每一大步内部又分为几个阶段。看到这里你可能会想这代码太长了读不下来。没关系你不需要一行行读关键是知道哪些参数要调、哪些函数在干什么。4.2 噪声标准差sigma的设置逻辑sigma是整个算法中最重要的参数没有之一。它的物理含义是噪声的标准差假设噪声是加性高斯白噪声AWGN即z y n其中n ~ N(0, sigma^2)。如果sigma设置偏小算法会认为噪声不严重对应的阈值和收缩系数偏保守结果就是去噪不彻底画面里还有很多噪点。如果sigma设置偏大算法会认为噪声很严重会狠下手去砍变换系数结果是细节被当噪声一起去掉了图像变得过于平滑像蒙了一层磨砂玻璃。在仿真实验中通常的做法是sigma 25; z y sigma * randn(size(y));如果你是拿真实照片去噪sigma需要通过噪声估计方法获得。一种常见做法是拿图像平滑区域的像素值标准差作为估计值或者用噪声水平估计算法如K-SVD论文里提到的方法。不知道sigma就随便填一个数结果大概率不理想。4.3 profile参数怎么选官方实现里提供了几档预设参数profile值对应参数档特点适用场景npNormal Profile速度快效果一般大批量处理、实时性要求高vnVariance Normal Profile速度稍慢效果更好追求高质量去噪结果lcLow Complexity Profile复杂度低速度最快大图处理或计算资源有限high高精度档效果最好耗时最长论文主图、对画质要求苛刻我自己做过对比在512x512的灰度图上vn档通常要跑10到30秒np档大概4到8秒。PSNR差距大约0.1到0.3dB人眼几乎无差别。所以如果不是特别在意性能直接np就好。4.4 输出指标怎么看官方demo最后会打印这么一段话Image: monarch.png, sigma: 25, PSNR_IN: 20.15dB, PSNR_EST: 29.36dB, SSIM_EST: 0.892PSNR_IN是带噪图像的峰值信噪比也就是你输入的底子PSNR_EST是去噪之后的峰值信噪比中间的差值就是算法帮你赚回来的dB数。SSIM则是结构相似度范围0到1越接近1说明结构保留越好。如果你在自己实验里跑出来的PSNR和论文公布的数据对不上先检查两件事一是是否用了同一张测试图二是sigma和profile是否一致。BM3D论文里公布的结果基本都是vn档跑出来的你用np跑自然会低零点几个dB。5. 实战踩坑记录从报错到跑通的完整排查链路5.1 报错Undefined function or variable bm3d_thr_grouping这是我见过最多的报错。原因几乎都是路径没添加完整。你只把BM3D.m放到了当前目录但bm3d_thr_grouping.m在子文件夹里。解决办法addpath(genpath(C:\你的目录\BM3D-master));然后再运行demo。如果还报错用which bm3d_thr_grouping查看Matlab是否找到了这个文件。找不到就说明路径还是不对需要用cd进入代码目录去运行而不是在别的目录下直接调用。5.2 报错Error using zeros, Size vector must be a row vector with real elements这个报错一般出现在function_LPA_filter.m或function_stdEstimation.m里。原因通常是你传入的sigma是复数或者非数值类型或者你给BM3D传参时顺序搞错了。我遇到过一位同学他把BM3D的参数顺序理解反了把带噪图当成原始图传进去把原始图当成带噪图传进去导致算法内部计算尺寸时出错。建议在调用前打印一下几个输入变量的size和class确认都是double类型、尺寸一致。5.3 跑得很慢甚至跑大图直接卡死BM3D的复杂度确实不低。块匹配阶段需要遍历全图找相似块协同滤波阶段又涉及三维变换在512x512图上跑vn档还能接受一旦换成1024x1024或者更大尺寸的图耗时可能翻好几倍内存占用也很吓人。如果遇到这种情况我的建议是改用lc档复杂度显著下降。检查是否有parfor有就打开并行池。如果图像尺寸大于1000x1000可以试着自己写一个分块策略将大图切成若干有重叠区域的块分别跑BM3D再融合。注意重叠区域最好在8像素以上否则拼接处会有接缝。或者直接降采样处理反正在很多工程场景下尺寸缩小一半对最终效果影响不大。5.4 输出图像整体偏暗或偏亮这个问题通常不是BM3D本身的问题而是图像数据类型转换导致。当你用imread读图后得到的是uint8类型范围0~255而BM3D内部处理时假设数据范围是0~255的double。如果你把图像归一化到0~1之间再传入而sigma仍是旧的25那算法内部计算阈值时就完全错乱了。正确的做法是y im2double(imread(cameraman.tif)) * 255; % 转成0~255的double z y sigma * randn(size(y));或者你保持0~1范围也行但此时sigma也要对应缩小到0~1范围比如噪声方差0.01这样才匹配。6. 参数不调好效果差一倍sigma、块大小和搜索窗口的经验值6.1 sigma不同取值下的表现差异我用Cameraman图做过一组实验固定用np档只改变sigma结果如下sigma设置值真实噪声sigmaPSNRdB视觉效果152527.8噪点残存明显细节还在252529.2干净细节保留好352528.1过于平滑纹理丢失可以看到sigma偏差10个点PSNR就掉1个多dB。所以sigma尽量估准别拍脑袋。6.2 块大小N和搜索窗口N2的选择逻辑官方代码里块大小N默认是8搜索窗口N2默认是16。这两个数字的含义分别是N每个相似块的大小8x8是经验值既能保留细节又能提供足够多的频域信息。取4则块太小频域分辨率不足取16则计算量大幅上升而且对局部结构变化不敏感。N2搜索区域大小。16意味着在每个参考块周围16x16的范围内搜索相似块。如果搜索范围太小找不到足够的相似块协同滤波效果变差太大则容易匹配到不相似的块引入伪纹理。如果你处理的是纹理丰富的图像如Barbara、Baboon可以试试把N2增大到32效果会有细微改善但耗时也会成倍增长。6.3 相似块最大数量Ns的经验值匹配相似块时BM3D会限制最多使用多少个相似块官方默认Ns16。这个值决定了三维数组第三维的厚度。Ns越大协同滤波越充分但计算量也越大而且超过一定数量后新加入的块与参考块的相似度变低反而稀释了联合去噪效果。我在实际测试中感觉Ns从16调到32PSNR大约提升0.1到0.2dB但耗时翻倍Ns从32调到64提升几乎可以忽略。所以默认值16到32之间足矣别贪多。6.4 搜索步长Nstep和滑动步长Nstep2搜索步长Nstep是在块匹配阶段每隔几个像素取一个参考块滑动步长Nstep2是在第二阶段执行维纳滤波时每隔几个像素取一个块。默认搜索步长是3这意味着参考块的位置不是每个像素都取而是隔3个像素取一次这样既有足够的重叠覆盖又控制计算量。如果你的图像细节极其丰富可以考虑把Nstep调小到2让块的覆盖更密如果只是想要一个基准结果默认3就够了。至于Nstep2我发现它比Nstep对最终质量的影响更小基本上不用去动。7. 用BM3D做对比实验算PSNR/SSIM的保姆级教程7.1 加载数据并加噪的标准写法做算法对比实验的时候最怕的就是数据加载方式不统一导致结果没法比较。我自己有一套固定写法每次都用这个模板简单不容易错clear; clc; close all; % 读取原图 y im2double(imread(cameraman.tif)) * 255; % 设置噪声水平 sigma 25; % 固定随机种子保证可复现 rng(2024); % 添加高斯白噪声 z y sigma * randn(size(y)); % 转回uint8显示用 z_uint8 uint8(z);注意rng(2024)的作用是固定随机种子这样你每次加噪得到的结果都一样方便对照实验。很多新手忽略这一点导致每次加噪都不一样结果自然没法稳定对比。7.2 调用BM3D并计算PSNR/SSIM的常用代码% 去噪 [denoised, denoised_basic] BM3D(y, z, sigma, np); % 计算PSNR注意范围也是在0~255 psnr_in psnr(z, y, 255); psnr_out psnr(denoised, y, 255); % 计算SSIM推荐用0~1范围计算更规范 ssim_out ssim(denoised / 255, y / 255); % 打印结果 fprintf(Noisy PSNR: %.2f dB\n, psnr_in); fprintf(Denoised PSNR: %.2f dB\n, psnr_out); fprintf(Denoised SSIM: %.4f\n, ssim_out);这里有一个细节Matlab的psnr函数如果输入是uint80~255时第三个参数可以省略如果输入是double但范围0~255第三个参数要写255否则默认按数据范围计算峰值结果可能不对。ssim函数则推荐在0~1范围内计算和论文里的数值更接近。7.3 对比多个算法的通用流程如果你要做BM3D和其他算法的对比建议维护一个统一的结构算法名带噪PSNR去噪PSNR去噪SSIM运行时间中值滤波20.1424.300.700.1sNLM20.1427.100.833sBM3D20.1429.200.918s每次实验保证同一张原图、同一个randn种子、同一个噪声sigma、同一台机器跑。这样结果才有说服力。我见过太多论文里对比结果五花八门就是因为有人用不同噪声种子、不同图像、甚至不同尺寸去跑得出来的差距毫无意义。8. 进阶从灰度图到彩色图从静态图到视频8.1 彩色图去噪的两种思路BM3D的官方代码里其实支持彩色图像但它的处理方式非常简单粗暴把RGB三个通道分别用同一个BM3D流程去噪最后再合起来。这种方法忽略了通道之间的相关性所以R、G、B通道的噪声是独立处理的容易出现色彩偏移或伪彩色。另一种更优的做法是转换到YCbCr或Lab色彩空间只对亮度通道Y或L做BM3D色度通道Cb、Cr或a、b使用较轻的滤波。因为人眼对亮度细节的敏感度远高于色彩细节亮度通道用强力去噪色彩通道轻处理可以在保证色彩准确的前提下大幅去噪。8.2 视频去噪用三维块匹配的升级版VBM3D/VBM4D如果你处理的是视频不要一帧一帧单独跑BM3D那样时间维度上的信息全浪费了。可以找一下VBM3DVideo BM3D它把块匹配从二维扩展到三维——除了空间上的相似块还在时间方向上匹配前后帧的块叠在一起协同滤波。VBM4D更进一步加入了光流估计效果更好但复杂度也更高。在Matlab里搜VBM3D能找到作者的官方代码调用方式与BM3D类似只是输入变成视频帧序列。我自己用VBM3D做带噪视频的实测PSNR提升比单帧BM3D高约1到2dB运动区域细节保留也更完整。8.3 和DnCNN、FFDNet的取舍建议近几年的深度学习方法DnCNN、FFDNet、DRUNet在某些数据集上已经超过了BM3D但BM3D依然没有被淘汰。原因很简单它是一个无监督的经典算法不需要训练、不需要GPU、不依赖训练集分布任何一张图拿来就能跑而且可解释性强。深度学习算法一旦遇到与训练集分布差异大的图像比如医学影像、遥感图效果可能断崖式下跌BM3D反而稳如老狗。所以在工程上我的选择策略是数据分布稳定且算力充足时用深度模型数据来源杂、要快速出结果、或需要严格的物理可解释性时BM3D永远是那个最可靠的兜底方案。我在实际跑BM3D的这几年里最大的心得就是这个算法不像某些深度模型那样调参全靠玄学它的每一步都有明确的物理和数学含义参数调优也基本有规律可循。只要把sigma搞准默认参数几乎不会让你失望。最后一件事如果你在写大作业或论文引用BM3D时记得引用原始文献Dabov et al., Image restoration by sparse 3D transform-domain collaborative filtering, SPIE 2008评审老师看到这个引用基本就知道你不是随便拼凑的算法实现了。本文还有配套的精品资源点击获取
返回列表