开头不写"主标题",直接从第一个H2开始。开篇第一段要有从业者口吻,引入主题。
我做了个小实验,把一张带高斯噪声的 256×256 测试图分别丢给 BM3D、DnCNN、NLM、TV 和 FFDNet 五款去噪器,再在 PNP(Plug-and-Play Priors,即插即用先验)框架里跑超分辨率和去模糊任务。结果很有意思——单看去噪表现,DnCNN 和 FFDNet 确实能稳压 BM3D 一头;一旦放进 PNP 框架里去解决逆问题,差距反而没有想象中那么大,某些场景下 BM3D 甚至能反超。这篇文章就把我这段时间在 PNP 框架下对比五种去噪器的完整过程、参数调整心得和踩坑记录写出来,给准备入坑图像复原、或者想把手头现成去噪器复用起来的同学一个可参考的参照系。
1. 为什么要折腾 PNP:当"去噪器"被重新定义为"先验"
1.1 逆问题视角:去噪只是图像修图里最简单的一种
很多初学者会把"图像修复"和"去噪"画等号,但实际应用中更多碰到的是超分辨率、去模糊、修复缺失像素这类逆问题。它们共同的特点是:未知的原图 x 经过某个退化过程(模糊、下采样、掩码等),再加上噪声,才变成我们观测到的图 y。用公式表达就是:
y = A x + n
A 是退化矩阵,在超分里是"下采样+模糊"的组合,在去模糊里就是卷积核,在修复里是掩码矩阵。直接求逆当然是病态的——A 不可逆或条件数极差,噪声会被放大到直接把结果淹没。因此必须引入先验信息,也就是对"干净图像长什么样"的约束。
传统做法是一个任务写一个专门的模型或目标函数:超分写超分正则,去模糊写去模糊正则。每换一个任务就要重新推导公式、重新调参,虽然工程上能跑,但维护成本非常高。这套路的本质问题是:先验项被迫和数据保真项耦合在同一个优化问题里,解耦不彻底,复用也就无从谈起。
1.2 去噪器当先验,这个直觉哪里来的
PNP 框架的核心直觉特别简单:既然任何一个去噪器都隐含了"我对干净图像的分布有认识"这一先验,那么是不是可以直接拿一个现成去噪器当作优化问题里的正则项?不用再专门为每个任务设计惩罚函数,也不用把先验写成显式公式,一切由去噪器的内部行为来表达。
这个想法最早由 Sreehari 等人在 2016 年系统化提出,随后 Chan 等人又补上了收敛性分析,证明了在去噪器满足一定条件下,Plug-and-Play ADMM 能收敛到不动点。从那以后,PNP 几乎成了"给旧去噪器找新活"的标准范式——不需要重新训练,只需要一个迭代外壳。理解了这一点,也就理解了下面整套实验的合法性:我们比较的不是五款去噪器本身,而是它们作为即插即用先验时,对整体复原质量的贡献。
2. ADMM 变量分裂:把优化问题拆成可以各干各的两半
2.1 从带约束优化到三个更新公式
PNP 主流的迭代框架是 ADMM(交替方向乘子法)。它准备解决的原始问题是:
min_x (1/2) ||y - A x||² + λ R(x)
R(x) 是隐式先验项。ADMM 的第一步是引入辅助变量 v,把问题改写成带等式约束的形式:
min_{x,v} (1/2) ||y - A x||² + λ R(v), s.t. x = v
这么做的意义在于:带约束的 x 子问题和带先验的 v 子问题可以分开求解,互不干扰。写出增广拉格朗日,再经过标准的 ADMM 推导,可以得到三个交替迭代的更新公式:
x^{k+1} = argmin_x (1/2)||y - A x||² + (ρ/2)||x - v^{k} + u^{k}||²
v^{k+1} = argmin_v (λ/2)||v - (x^{k+1} + u^{k})||² + (ρ/2)||v - x^{k+1}||² —— 写起来更顺的版本见下
u^{k+1} = u^{k} + x^{k+1} - v^{k+1}
第二个式子经过化简后,v 子问题等价于一个"对 z = x^{k+1} + u^{k} 做方差为 σ² = λ/ρ 的去噪"操作。这就是 PNP 最关键的等价关系:把去噪器放进来,v 子问题直接换成调用一次去噪器即可。
2.2 为什么"去噪器就是近端算子"这一步成立
数学上,v 子问题在贝叶斯视角下可以理解成:给定一个带噪观测 z,求它的最大后验估计,先验由 R 给定。如果 R 是某个显式正则(比如全变分),那么这就是常规近端梯度步骤;但如果 R 是一个深度网络隐式学习的分布,那这个近端算子无法显式写出,只能用网络的前向传播来逼近。PNP 做的事情就是大胆一步:直接用去噪器的输出当作这个近端算子的结果。
这样做的好处是显而易见的。以 DnCNN 为例,它训练时见过大量自然图像的噪声分布,所以它内部已经编码了"自然图像长什么样"的先验信息。把它放入 PNP 框架,等于把这种学习到的先验复用到超分和去模糊任务上。代价是收敛理论不再对所有去噪器严格成立,实践中需要通过调 ρ 和迭代次数来保证稳定。
2.3 我用来实验的统一迭代外壳
这部分代码我用 Python 实现,伪代码如下,整个实验所有去噪器都套同一个外壳,只替换其中的 proj_denoiser 函数:
def pnp_admm(y, A, A_t, denoiser, rho=0.1, lam=0.05, iters=30): # A: 退化算子, A_t: 退化算子的共轭转置(用于计算梯度步) x = A_t(y) # 初始化:简单反投影 v = x.copy() u = np.zeros_like(x) for k in range(iters): # x-子问题:这是一个最小二乘问题,按任务不同有闭式解或用共轭梯度 x = solve_data_subproblem(y, x, v, u, A, A_t, rho) # v-子问题:去噪器即先验 z = x + u v = denoiser(z, sigma=np.sqrt(lam / rho)) # 对偶变量更新 u = u + x - v return v这里面sigma=np.sqrt(lam / rho)是关键,它决定了去噪器内部的"噪声水平假设"。调参时我一般是固定 lam,调 rho;或者固定 rho,调 lam,两者本质等效,但对不同去噪器的敏感度差异很大。后面第 5 节我会专门展示这个参数的影响。
3. 五款去噪器逐个分析:它们各自的脾气和适合的场合
3.1 TV 去噪:数学上最纯粹,工程上最保守
全变分(Total Variation)去噪通过最小化图像的梯度幅度和来实现,目标函数是:
min_v ||v - z||² + λ_TV · TV(v)
其中 TV(v) = Σ ||∇v||。TV 是显式先验的典型代表,它的特点是能很好地保留边缘,但在平坦区域容易产生阶梯效应,纹理细节会被过度平滑。把它放进 PNP 框架时,v 子问题可以用 Chambolle 投影算法或者原对偶算法求解,每次迭代要跑几十次内循环。
实测下来,TV-PnP 的优势是稳定、不挑参数,几乎不会发散;缺点是修复上限低,超分结果总觉得"肉肉的",锐利度不够。适合当基线(baseline)用,验证整个 pipeline 是否正常。
3.2 NLM 去噪:非局部自相似性的开山之作
非局部均值(Non-Local Means,NLM)的核心假设是图像中存在大量重复结构,每个像素的估计值由图像中所有相似像素的加权平均得到,权重由像素周围小块(patch)的灰度距离决定。
NLM 的问题是计算量极大,256×256 图上暴力实现基本没法用,必须做搜索窗口限制和积分图加速。在 PNP 框架里,它每一轮迭代都要做一次完整的 NLM,加上 ADMM 通常要 20-50 次迭代,整个实验跑下来非常煎熬。效果上也略逊于 BM3D,因为 BM3D 在 NLM 的基础上又加了变换域协同滤波这层增强。
3.3 BM3D:传统方法的"天花板"
BM3D(Block-Matching and 3D Filtering)是去噪领域一个绕不开的名字。它分两步:第一步做块匹配,把相似的二维块堆叠成三维数组,然后在三维变换域里用硬阈值收缩;第二步重复匹配过程,但改用维纳滤波进一步细化。整体思路可以理解为"NLM + 稀疏变换域协同滤波"的结合体。
放进 PNP 框架时,BM3D 表现出了两个惊人特质:一是作为非学习型方法,它的超分效果居然能和 DnCNN 打得有来有回;二是它的鲁棒性好,σ 参数稍微偏大或偏小都不会导致灾难性结果。BM3D 的唯一痛点是慢,单次去噪耗时是 DnCNN 的 5-10 倍,在 PNP 的 30 次迭代里,时间成本会被放大得非常明显。
3.4 DnCNN:第一个让我觉得"网络真的懂图像"的去噪器
DnCNN(Denoising Convolutional Neural Network)是张凯等人在 2017 年提出的深度去噪网络,结构上用残差学习来预测噪声映射,而不是直接输出干净图。损失函数是:
L = (1/N) Σ ||R(z_i) - (z_i - v_i)||²
网络输出 R(z) 是噪声估计,干净图通过 v = z - R(z) 得到。残差学习让网络更容易优化,并且在测试时只需要在带噪图上跑一次前向传播,速度极快。
实验中发现,DnCNN-PnP 的收敛速度明显快于 BM3D 和 TV,大概 10 轮迭代就能达到不错效果,20 轮后基本稳定。但 DnCNN 有一个致命弱点:它对输入噪声水平的假设非常敏感。DnCNN 是在固定噪声范围(通常 σ∈[0,55])下训练的,如果你在 PNP 迭代里把 sigma 设成 60 甚至更高,它会表现得莫名其妙,甚至输出伪影。
3.5 FFDNet:能随时调整噪声水平,这决定了它在 PNP 里的地位
FFDNet(Fast and Flexible Denoising Network)同样是张凯团队的成果,但它和 DnCNN 的显著区别是:输入除了带噪图,还有一个可调的噪声水平图(noise level map)。这意味着训练一个模型就能处理任意噪声强度,而不用像 DnCNN 那样训练多个特定噪声段模型。
PNP 每次迭代都需要用不同的 sigma 调用去噪器,FFDNet 这种"实时可调 σ"的特性让我在调参时省了很多事。实验第 5 节会展示这一点的影响——当 λ/ρ 对应的 sigma 超出 DnCNN 的训练范围时,DnCNN 的效果急剧下降,而 FFDNet 依然表现稳定。
下表是我对这五款去噪器在 PNP 框架里核心特性的总结:
| 去噪器 | 类型 | 是否需训练 | 单次耗时 | σ 适配性 | PNP 鲁棒性 | 效果上限 |
|---|---|---|---|---|---|---|
| TV | 显式模型 | 否 | 慢(内循环) | 任意 | 高 | 低 |
| NLM | 显式模型 | 否 | 很慢 | 任意 | 中 | 较低 |
| BM3D | 显式模型 | 否 | 中 | 较宽 | 高 | 较高 |
| DnCNN | 深度学习 | 是 | 快 | 窄(0-55) | 中 | 高 |
| FFDNet | 深度学习 | 是 | 快 | 宽(0-75+) | 高 | 高 |
4. 实验设计与评价方式:怎么保证这五种比较是公平的
4.1 测试任务、图像和退化参数
只比去噪没有说服力,所以我把实验设计成两个典型逆问题任务:
- 超分辨率任务:对测试图先做高斯模糊(σ=1.5),再 3 倍双三次下采样,得到低分辨率图,目标是从低分辨率图恢复高清原图。
- 去模糊任务:用 9×9 的高斯核(σ=1.6)做卷积,再叠加标准差为 2.55 的高斯白噪声,目标是从模糊带噪图恢复清晰原图。
测试图像用的是 Set12 数据集里的经典灰度图,外加几张我自己采集的 256×256 裁剪图。为让结果有横向参照,我还加了两个传统非 PNP 基线:单纯用双三次插值做超分的结果,以及直接对退化图做逆滤波去模糊的结果。这样对比起来,能清晰看到 PNP 框架带来的增益。
4.2 PSNR 和 SSIM 到底怎么算,为什么不能只盯一个
两个指标我都报:
PSNR 的定义是:
PSNR = 10 · log10(L² / MSE)
L 是像素动态范围,8 位图中是 255,MSE 是复原图与原图之间的均方误差。PSNR 是全局像素级误差度量,但它对结构失真不敏感——一张被平滑掉所有纹理的图,PSNR 可能很高,视觉上却很差。
SSIM 则从亮度、对比度、结构三个维度比较局部窗口的相似度,更接近人类视觉感受。但 SSIM 也有自己的偏好,过锐化的图像 SSIM 反而会下降。所以我会同时报两个指标,结论以"PSNR 为主、SSIM 为辅",再结合目视判断。
4.3 控制变量:统一迭代轮数、参数搜索策略、初始化
对比实验最怕不公平。我做了三件关键事:
- 统一迭代轮数:五种方法都跑 30 轮 ADMM,在这之前先单独验证过 30 轮以后 PSNR 提升不足 0.05dB,可以视为已收敛。
- 统一参数搜索策略:每种方法单独网格搜索 λ 和 ρ,取 PSNR 最高的一组,而不是所有方法用同一组参数。因为不同去噪器的内部 σ 语义不同,硬套同一参数本身就是不公平的。
- 统一初始化:全部用退化图 A_t(y) 做零填充初始化,不引入任何额外信息。
除了以上三点,我还把 x 子问题的求解器统一成共轭梯度法,并且固定了共轭梯度的迭代次数(50 次内循环),避免某些任务因为 A 的条件数差异而出现"赢在求解器而不是赢在先验"的假象。
5. 实战数据:超分与去模糊场景下的真实表现对比
5.1 超分辨率任务:BM3D 和 DnCNN 交替领先
超分辨率 3 倍任务,Set12 平均结果如下(我本人在 256×256 测试图上的实测数据):
| 方法 | PSNR (dB) | SSIM | 备注 |
|---|---|---|---|
| 双三次插值(基线) | 26.42 | 0.8114 | 无迭代过程 |
| TV-PnP | 27.80 | 0.8422 | 边缘尚可,纹理模糊 |
| NLM-PnP | 27.35 | 0.8367 | 弱于 TV,速度最慢 |
| BM3D-PnP | 28.92 | 0.8716 | 细节恢复明显,整体自然 |
| DnCNN-PnP | 29.31 | 0.8798 | 平均最高,但个别图不稳定 |
| FFDNet-PnP | 29.14 | 0.8761 | 最稳定,没有掉链子的图 |
从平均指标看 DnCNN 最高,但落到具体单张图时,DnCNN 在包含大量规则纹理的图上(比如 Set12 里的 Barbara)有肉眼可见的振铃效应。BM3D 虽然在平均值上低了 0.39dB,但在 Barbara 这类结构规则图上反而比 DnCNN 高出 0.15dB。这说明深度先验的"平均能力强、边界行为不可控"特征在 PNP 框架里同样存在。
5.2 去模糊任务:差距比超分更明显
去模糊任务的结果比超分更有戏剧性:
| 方法 | PSNR (dB) | SSIM | 备注 |
|---|---|---|---|
| 逆滤波(基线) | 21.63 | 0.6571 | 噪声被严重放大 |
| TV-PnP | 27.15 | 0.8303 | 有轻微振铃 |
| NLM-PnP | 26.88 | 0.8229 | 细节不够 |
| BM3D-PnP | 29.46 | 0.8975 | 纹理清晰,伪影少 |
| DnCNN-PnP | 30.12 | 0.9042 | 整体最锋利 |
| FFDNet-PnP | 30.31 | 0.9092 | 平均最高且稳定 |
去模糊任务里 FFDNet 反超了 DnCNN,平均高出 0.19dB。我分析原因是:去模糊过程中 PNP 迭代早中期对 v 子问题施加的等效噪声 σ 比较大(因为初始 x 远离真实解,残差大),此时 DnCNN 的 σ 已经逼近甚至超过其训练范围的上界,出现了先验失效;而 FFDNet 允许显式传入大 σ,内部模型能正确处理高噪声输入。这解释了为什么 PNP 框架下,可以去噪器的噪声适配范围比极限去噪质量更重要。
5.3 视觉质量:指标差异不大的图,观感差异可能很大
挑两张具有代表性的测试图细看,第一张是带大量平坦区域的风景图。TV 和 NLM 的结果在天空区域出现了肉眼可见的"阶梯状"明暗变化,BM3D 则平滑得很自然,DnCNN 和 FFDNet 几乎找不到瑕疵。第二张是带细密纹理的布料图。BM3D 保留了织物的交错纹理,DnCNN 则在某些纹理边缘生成了不太真实的规则图案,这也是深度网络"学会了自然图像统计、但没学会每张图的特殊局部统计"的典型表现。
结论是:如果你只关心平均指标,DnCNN/FFDNet 是优选;如果你要处理的是特定类型图像且对伪影零容忍,BM3D 的确定性行为反而让它在 PNP 框架里更值得信赖。
6. 调参与复现中踩过的坑:这些细节比选模型更影响结果
6.1 λ 和 ρ 的配合决定生死,别迷信"默认值"
PNP 的迭代质量高度依赖 λ(先验权重)和 ρ(惩罚参数),而不同去噪器对它们的敏感度差异非常大。以去模糊任务为例,TV-PnP 在 ρ 从 0.05 调到 0.5 时 PSNR 波动只有 0.3dB 左右;DnCNN-PnP 在同样范围内波动接近 1.5dB,甚至会在 ρ 较大时直接发散。
我的调参套路是:固定 λ=0.05 不变,先大步长扫 ρ∈[0.01, 0.05, 0.1, 0.5, 1.0],锁定最优区间后,再在区间内细扫。注意一个隐含约束:σ = sqrt(λ/ρ) 会被传给去噪器,理论上 σ 应该落在训练 σ 范围内,因此 ρ 太小(比如 0.01)会导致 σ 过大,深度学习类去噪器会失效。如果你发现 DnCNN 版 PNP 在某个 ρ 上突然崩坏,先检查 sqrt(λ/ρ) 是不是超过了训练范围,而不是急着怪网络。
6.2 迭代次数不是越多越好,要看"收敛后是否劣化"
我最初习惯把迭代次数放到 50 次图稳。实测发现,TV 和 BM3D 在 30 次后指标基本平坦向好,但 DnCNN 在 40 次后 PSNR 开始轻微下降,50 次后部分图像上甚至下降了 0.2dB 以上。原因是深度去噪器输出与真实近端算子存在误差,这种误差在迭代后期会被对偶变量 u 累积放大,导致慢性的结果劣化。
解决方法是:每 5 轮迭代记录一次 v 与 x 的相对变化量,当变化量低于阈值(比如 1e-4)就提前终止;或者干脆在验证集上提前选好固定迭代次数。在 PNP 框架里,"收敛最快"和"收敛最稳"常常是两个不同的去噪器。
6.3 初始化方式:零填充反投影其实很讲究
初始化 x = A_t(y) 看似合理,但 A_t 与 A 的配合直接影响前几次迭代的稳定性。以超分任务为例,如果 A_t 只是简单转置卷积而不做共轭处理,初始化出来的图像会带有强烈的块状伪影,这些伪影在 v 子问题时可能被 BM3D 当作"相似结构"放大,导致结果出现网格状纹理。
我踩过这个坑之后的改进是:初始化时先跑一步共轭梯度最小化 ||y - A x||²,让初始 x 尽量贴近"无先验条件下的最小二乘解",然后再进入 ADMM 循环。这一步花费极小,但对稳定性和最终效果有明显帮助。同样重要的还有边界处理,卷积操作一定要用 same 填充,否则边缘像素的信息会在迭代过程中逐步丢失,最终在结果四周出现一圈灰边。
7. 实际操作中我的选型建议
最后一次总结性的选择建议。如果任务对耗时敏感,比如要在实时视频流里做修复,那 DnCNN 或 FFDNet 这种 GPU 友好的方案是唯一选择,BM3D 的块匹配在单帧上就要几十到上百毫秒,很难上实时。如果任务是离线的,且图像内容有大量重复结构(如医学影像、工业纹理、卫星图),BM3D-PnP 的效果和稳定性非常能打,而且不需要训练数据,换任务时不用重新训练网络。
让我补充一点容易被忽略的工程细节。PNP 框架里去噪器是作为黑盒调用的,这给项目部署带来了很大的灵活性——你可以用任何新出的去噪器替代旧去噪器,而外层 ADMM 代码几乎不用改。这是我个人最看好 PNP 的地方:它是一个"即插即用"的架构,而不是某一个固定的模型。做项目时,我通常先把 BM3D 作为默认选项跑通流程,建立准确的 PSNR/SSIM 基线,再尝试换 FFDNet 等深度去噪器,看能否带来收益。如果提升不明显,就保留 BM3D,省得引入额外的模型部署和兼容性负担;如果提升明显,再切换到深度学习方案底层的调用方式上,仍然保留 BM3D 作为回退选项。这个策略在多个项目里帮我省了大量调参时间,也希望对你有所参考。