拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

稀疏表示与字典学习:图像超分辨率重建经典方法全解析

稀疏表示与字典学习:图像超分辨率重建经典方法全解析

图像超分辨率重建这些年几乎已经被深度学习方法“霸屏”,偶尔有人提起传统算法,第一反应就是“插值加正则化”。但真要追根溯源,稀疏表示和字典学习这一支,才是把“如何从低分辨率观测里补回高频细节”这个问题第一次讲得像个科学问题的工作。杨建超(Jianchao Yang)等人2010年发表在IEEE TIP上的论文Image Super-Resolution via Sparse Representation,就是这一支里绕不开的经典。我前阵子因为项目需要,把这套方法从头到尾推了一遍、复现了一遍,发现里面值得抠的细节比想象中多得多。这篇就围绕稀疏表示、字典学习、图像超分辨率这三件事,把论文的思想、公式、实现和坑一次性讲清楚。适合刚接触超分论文的读者,也适合想用传统方法做基线对比的同学。

1. 超分不是“放大”而是“猜细节”:先搞清问题本身

1.1 低分辨率图像到底丢了什么

超分辨率在数学上是一个典型的病态逆问题。几乎所有超分论文都会给出同一个降质模型:

Y = S H X + n

其中 X 是原始高分辨率图像,H 是模糊算子(光学模糊、传感器点扩散、运动模糊都有),S 是下采样算子,n 是加性噪声,Y 是我们实际拿到的低分辨率观测。所谓“重建超分”,就是从 Y 反推 X。

这个模型乍看简单,但它说明了一个关键事实:低分辨率图像不是高分辨率图像“缩小”那么简单,而是先模糊再丢弃像素。每一个低分辨率像素值,其实是高分辨率图像一个小邻域在高斯核或者其他模糊核下的加权平均,然后隔几个才取一个点。也就是说,高频信息在成像那一步就已经被物理丢掉了,不是单纯“藏”在图像里等着被放大。

所以超分从来不是“放大”,而是“猜细节”。一个低分辨率像素在模糊和下采样之后,可能对应无穷多种高分辨率结构:它可能来自一条边缘,可能来自一块纹理,也可能来自平坦区域。传统插值方法,比如双三次插值,本质上假设图像局部是平滑的,用周围像素的多项式拟合来补点,所以它对低频信息有效,对边缘和纹理只会越插越糊。这也是为什么在实际项目里,双三次放大后的图总是“肉肉的”,印刷和医疗影像领域根本不敢用。

1.2 稀疏表示凭什么能“猜”出高频

既然问题是信息缺失,那唯一可行的路线就是引入外部先验:从大量自然图像里学习“高频结构长什么样”。稀疏表示就是这篇论文选用的先验模型。

它的核心假设是:自然图像中的任何一个小块,都可以被一个过完备字典里的少数几个原子线性组合近似表示。注意“过完备”和“少数几个”这两个词。过完备意味着字典里的原子数量远大于图像块的维度,换句话说,表达方式有非常多的冗余;而“少数几个”意味着尽管可选原子很多,但实际用到的很少。两个条件加在一起,就是在说:自然图像块尽管形态极其多样,但它们在结构上仍然有很强的规律性,可以用极少的“结构零件”拼出来。

为什么要用“少数几个”这个约束?因为从低分辨率观测反推高分辨率块,解不唯一。稀疏性是一个很好的选择标准:在无数个可行解里,挑那个“用最少零件拼出来”的解。这个思路和经验高度一致——一个图像块如果是平滑区域,用一个平坦原子就能表达;如果是边缘,用一两个边缘原子就够了;如果非要找个字典原子堆出噪声,那恰恰更像过拟合而不是合理解。论文里反复强调的“局部稀疏先验”,本质就是在说:自然图像的结构复杂度远低于像素维度,别把问题想太难。

2. 稀疏编码与字典学习:论文的两块基石

2.1 稀疏编码的数学形式

先看最基础的稀疏编码问题:给定一个过完备字典 D,对一组观测信号 y,求一组系数 α,使得 y ≈ D α,且 α 尽量稀疏。所谓稀疏,就是这个向量里绝大多数元素为0,只有少数非零。

最直接的数学表述是用 L0 范数约束非零元素个数:

min ||α||_0,s.t. y = D α

但 L0 优化是 NP 难问题,没法直接大规模求解。因此论文采用了凸松弛的做法,把 L0 换成 L1 范数:

min ||α||_1,s.t. ||D α - y||_2 ≤ ε

或者在无约束版本里写成:

min ||D α - y||_2² + λ ||α||_1

λ 是正则化系数,平衡重建误差和稀疏度。这个形式就是我们熟悉的 LASSO 回归。实际求解时可以用 OMP(正交匹配追踪)这类贪婪算法近似逼近稀疏解,也可以用 ISTA、LARS 这类基于 L1 的优化算法。论文的做法是偏向后者的,但我在复现时两种都试过,后面会讲它们的差异。

有个点值得多说一句:为什么 L1 能诱导稀疏?一个直观解释是,L1 的等高线是带尖角的菱形,在约束条件下,解更容易落在坐标轴上,从而让系数变成稀疏的。我见过很多人直接拿 Lasso 当黑盒用,不理解这一层,实际调参时就会很迷茫。

2.2 过完备字典为什么比固定基更合适

早年的信号表示都用固定基,比如 DCT、小波。这些基函数有很好的数学性质,但它们的表达能力在面对自然图像时是有限的。DCT 擅长表示低频和周期性结构,小波擅长表示点奇异和水平/垂直边缘,但自然图像里的边缘方向是任意的,纹理也千奇百怪。拿固定基去表示,非零系数会变多,稀疏性就差了。

字典学习改变了这个局面:原子不再是人手设计的固定函数,而是从训练数据里自动学出来的。学出来的原子往往是一些有意义的局部结构:不同方向的边缘、角点、条纹、梯度过渡。由于这些原子是从真实图像里统计提炼的,它们对目标域的适配性远好于通用基函数。

打个比方:固定基像一套通用工具箱,什么都能修,但每件工具都不太顺手;字典学习出来的原子像你针对某类常见故障专门定制的工具,可能确实偏科,但对目标场景效率极高。这也是为什么同样的块大小和稀疏度,学习字典的重建效果通常明显优于 DCT 字典。

2.3 高低分辨率为什么要共享一套稀疏系数

这篇论文最关键的前提,是假设同一图像内容的高分辨率块和低分辨率特征块,在各自字典下的稀疏编码系数是相同(或者非常接近)的。

为什么敢做这个假设?因为降质过程(模糊加下采样)从频域上看是低通滤波,它主要削弱的是高频幅度,对图像块的主要结构布局影响较小。一个图像块里“哪条边更重要”“哪个纹理占主导”,这些结构性信息在低分辨率域仍然保留着。因此,如果高分辨率块 x 可以用系数 α 在字典 D_h 下稀疏表示,那么对应的低分辨率特征块 y,很大程度上也能用同一组 α 在字典 D_l 下稀疏表示。

当然,这个假设并不严格成立,它只是工程上一种非常有效的近似。论文并没有花大篇幅去证明它,而是用大量实验说明:在自然图像上,这个共享系数假设能带来很好的重建效果。后来的很多方法试图进一步放宽或者改进这个假设,但基本思路一直延续到现在。

3. 完整链路拆解:字典怎么学,重建怎么做

3.1 训练样本制备:高低分辨率块怎么配对

整个算法分训练和重建两个阶段。训练阶段的第一步是准备高低分辨率块对。

具体做法是这样的:从一批高质量训练图像里随机裁剪出大量高分辨率小块,记为 x_i。然后对整张训练图像人为施加降质模型(高斯模糊加下采样),得到对应的低分辨率图像。接下来有一个关键细节:不要把低分辨率图像直接作为“低分辨率块”来用,而是先把它插值放大回目标分辨率网格,再在插值后的图像上提取特征块。

这一步是很多人复现时容易想不通的地方。原因不难理解:我们希望低分辨率特征块和高分辨率像素块一一对应、坐标完全对齐,并且有相同的块尺寸。如果直接把低分辨率的 5×5 块映射到高分辨率的某个区域,坐标对不齐,后续稀疏编码的系数共享就无从谈起。论文中处理的策略就是先把低分辨率图插值放大到高分辨率网格,然后在这个公共网格上提取特征。我复现的时候也是按这个思路处理的,效果确实正常。

低分辨率侧的特征不能直接用像素值,论文用的是四个梯度滤波器来提取结构信息:一阶梯度算子 [-1, 0, 1] 及其转置,二阶梯度算子 [1, 0, -2, 0, 1] 及其转置。这四个滤波器分别捕捉水平一阶、垂直一阶、水平二阶、垂直二阶的局部结构。这样做有两个好处:一是去掉低频能量,让稀疏编码更关注纹理和边缘;二是对光照变化更鲁棒。

高分辨率侧则直接取原始像素块,不需要做特征变换。最终我们得到一批“低分辨率特征块 y_i + 高分辨率像素块 x_i”的训练对。

3.2 联合字典学习的目标函数

拿到训练对之后,要同时学习两个字典 D_h 和 D_l,让高低分辨率共享系数 α_i。论文的联合优化目标可以写成:

min Σ_i ||D_h α_i - x_i||₂² + Σ_i ||D_l α_i - y_i||₂² + λ Σ_i ||α_i||₁

这个目标函数有三项,意思很清楚:第一项要求高分辨率字典加系数能很好重建高分辨率像素块;第二项要求低分辨率字典加同一组系数能很好重建低分辨率特征块;第三项要求系数本身稀疏。

这个优化问题不是联合凸的,但分别固定一部分变量再优化另一部分时是凸的,所以用交替迭代来做:先固定字典,对所有训练块求稀疏系数;然后固定所有系数,更新字典;不断重复。字典更新的环节,论文的思路和 K-SVD 一脉相承,对每个原子做奇异值分解并去掉对已有表示的影响,一个原子一个原子地精修。我在实现的时候,为了控制复杂度,直接用了批量更新的方式,效果虽有细微差别,但整体收敛趋势一致。

需要注意训练块的数量。论文的实验里,训练块往往有数万到十几万个,而字典一般只有几百个原子。这么一组小数量的原子要覆盖自然图像的所有结构,训练样本必须足够多样,否则学出来的字典会有明显的偏置——比如只擅长图像中心区域,或者在边缘方向分布上不均匀。

3.3 重建阶段:逐块编码加全局约束

训练结束后进入重建阶段。给定一张待放大的低分辨率图,流程如下:

第一步,把输入图插值放大到目标分辨率。第二步,在放大后的图像上按步长滑动裁块,对每个块做同样的梯度特征提取。第三步,对每个低分辨率特征块求解稀疏编码问题,得到系数 α。第四步,用同一个 α 乘以高分辨率字典 D_h,得到高分辨率像素块。第五步,把所有高分辨率块放回原位置,重叠区域取平均,得到初步重建图 X0。

这里有一个非常容易忽略但很重要的收尾操作:全局一致性约束。前面五步做的是逐块局部重建,块与块之间难免出现不一致,而且整体结果未必严格满足降质模型。论文最后的做法是迭代反投影(back-projection),通过求解一个全局优化问题,让最终图像在重新降质后仍然与输入低分辨率图一致。

这个全局约束可以写成:

min ||S H X - Y||₂² + c ||X - X0||₂²

其中 X0 是逐块重建得到的参考图。公式的意思是:最终结果一方面要能在降质后还原回输入 Y,另一方面不能离局部重建结果太远。实际实现时用梯度下降迭代几次就能收敛。我初次复现时曾经偷懒跳过这一步,结果重见图的边缘虽然锐利,但整体结构和原图出现明显偏差,PSNR 掉了大约 0.3dB。加回去之后问题消失。

4. 论文里没明说但复现一定要知道的事

4.1 块尺寸、字典大小和重叠像素怎么配

论文给的典型参数是 5×5 的块、512 个字典原子,放大倍数通常是 2 或 3。但我实测下来,参数之间是联动的,不能照搬。

块尺寸决定了原子表达的空间尺度。5×5 在放大 2 倍时够用,但放大 4 倍时,低分辨率块包含的信息更少,单一小块能表征的结构范围更有限,重建结果会出现高频不足。我的经验是放大 4 倍时把块尺寸提到 7×7 或 9×9,字典规模从 512 加到 1024。代价是训练时间变长,重建时的稀疏编码也更慢,但质量提升是实打实的。

重叠像素直接影响块效应。滑动步长越小,重叠区越大,块与块之间的连续性越好,但计算量成倍增加。我通常的做法是块 9×9 时用步长 3,块 5×5 时用步长 2,重叠区域直接平均就行,不需要做复杂的加权。如果你发现重构图有明显的网格感,第一件事不是改算法,而是缩小步长。

4.2 特征归一化和坐标对齐

梯度特征和像素值的尺度完全不在一个量级。我做实验时对比过:特征向量不归一化直接进稀疏编码,训练和重建都不太稳定,尤其是当训练图像整体偏暗或偏亮时,字典原子的幅度会跟着漂移。后来我在编码之前对每个特征向量做 L2 归一化,重建之后再按原始尺度还原,稳定了不少。

坐标对齐这个问题更加隐蔽。训练时从高分辨率图上裁剪块得到 x_i,同时由降质图插值放大后提取 y_i,这两个块在空间上必须严格对应同一个图像区域。如果裁剪坐标和插值偏移处理得不一致,哪怕只差半个像素,学出来的字典也会“两边不讨好”,重建图上出现轻微的伪影。我一个朋友复现时出现整幅图所有边缘都有重影,排查很久,最后发现问题出在低分辨率插值放大时默认坐标对齐方式和裁剪坐标差了半个像素。

4.3 全局约束不是可选项

前面提到迭代反投影是全局约束,但还有更细的工程细节:迭代的步长和轮数。论文里没有给出很具体的推荐值,我实验里的做法是步长取 0.4,迭代 15 到 20 轮,基本收敛。步长太大会导致结果在降质模型附近震荡,太小则收敛太慢。

需要留意的是,全局约束不是万能的。如果局部重建质量本身就差,反投影只会把错误均匀化,并不会凭空补出细节。所以这个步骤应该理解为“兜底修正”,而不是“救命稻草”。

4.4 实操里最常见的三个坑

第一个坑:训练样本缺乏数据增强。随机裁剪后如果不做随机翻转和旋转,学出来的字典对旋转方向的纹理泛化很差。尤其是人脸和建筑等方向性明显的图像,没做增强时重建结果对图像翻转敏感。我后来在训练阶段加入八方向增强(四个旋转方向乘以是否翻转),效果改善明显。

第二个坑:正则化参数 λ 的选择。λ 太小,稀疏性不足,重建块会出现噪声放大;λ 太大,系数被过度压缩,重建图平滑得像水彩画。我的经验是先跑一个小数据集,观察块的重建误差和稀疏度曲线,选在拐点附近。一般 λ 在 0.01 到 0.1 这个量级,但跟特征归一化方式强相关,换一种归一化就要重新调。

第三个坑:用 OMP 还是 L1 求解的问题。OMP 速度快,但在噪声存在时容易把噪声也当成结构;L1 求解对噪声更鲁棒,重建图更干净。论文实验里的效果是 L1 为主。实际项目里如果追求效率,可以用 OMP 先跑通流程,最后再换成 L1 做最终参数验证。

5. 实测结果与调参记录

5.1 指标变化趋势

我用 Set5 和 Set14 测试集做了简单对比,放大倍数 3,训练图像用了一组自然风景和数据增强后的混合集。作为参考,我列一下大概的 PSNR 水平:

方法放大倍数Set5 平均 PSNR特点
双三次插值3约 30.4 dB边缘模糊,无新增纹理
最近邻嵌入(NE)3约 31.2 dB有一定细节但偶发伪影
稀疏表示字典学习3约 32.0 dB边缘清晰,纹理更自然
深度方法(参考 SRCNN)3约 32.7 dB重建速度快,细节更稳定

这个数字在不同训练集和参数下会有波动,但趋势是稳定的:稀疏方法相对插值有稳定提升,相对深度方法仍有一截差距(尤其在现代深度模型面前)。它的价值更多体现在算法思想上。

5.2 主观视觉比指标更诚实

PSNR 只算像素误差,不能完全反映视觉质量。我在实测中最大的体会是:稀疏表示方法重建出来的边缘非常干净,放大 3 倍时文字和建筑轮廓几乎没有明显锯齿,这一点在 PSNR 上只体现了一点点优势,但肉眼差异非常大。

但在平滑区域,比如天空和墙壁,稀疏方法偶尔会“编造”出一些不存在的微弱纹理。这不是 bug,而是稀疏先验的本性:字典里没有专门表示“平坦区域”的足够重要性,编码器有时会拿一两个纹理原子硬凑。论文作者其实也意识到这个问题,所以后续工作里增加了对结构信息的引导。我在复现时通过在训练样本里多保留一些纯平区域块、加大平坦块的采样比例,情况会好一些。

5.3 我后续的工程化提速技巧

稀疏编码阶段是整条链路里最慢的。如果对每个块单独调 Lasso,Python 实现会很痛苦。我的做法是把所有块的特征堆成一个二维矩阵,一次性求解 Lasso 问题,把循环改成矩阵运算,速度可以快一个量级。另一个技巧是,先对低分辨率特征做一次 PCA 降维,把特征维度从 100 左右降到 40 左右,稀疏编码的求解速度和稳定性都有帮助,代价是丢失极少量细节。

6. 这篇论文的边界,以及它和深度学习的隐性继承

6.1 它为什么没成为工业默认方案

客观说,这篇论文发表这么多年,并没有直接在工业界大规模落地。原因主要有三个。一是速度慢,重建一张图像要对每一个块做一次迭代优化,即使优化得很好的实现,也远达不到实时。二是对噪声敏感,输入图像如果有较强噪声,稀疏编码容易把噪声结构也编码进系数里,导致重建噪声被放大。三是当放大倍数变大(比如 4 倍以上),单一字典的表达能力和共享系数假设都开始吃紧,效果退化明显。

但这些问题恰好为后来的方法指明了方向。深度学习方法在速度和端到端优化能力上全面超越了传统稀疏方法,这是事实,但这不意味着稀疏表示的思想过时了。

6.2 SRCNN 和稀疏表示的隐性继承

只要对比一下流程就能发现,SRCNN 和这篇论文的处理路径几乎同构:都是先把低分辨率图像插值到目标尺寸,再提取特征,再映射到高分辨率空间。SRCNN 的三层结构——特征提取、非线性映射、重建——恰好对应了这篇论文里的梯度特征提取、稀疏编码、字典重建三个阶段。从字典学习的视角看,CNN 的前几层学到的卷积核和论文里手工设计的梯度滤波器在功能上高度重合,中间层的非线性映射其实是在学一组比稀疏编码更灵活的特征变换,最后一层则是在学重建字典。

我经常建议刚接触超分的同学先复现一遍这篇论文再碰深度模型,理由就在这里:深度模型里的很多设计不是凭空出现的,理解清楚稀疏表示这个源头,你就知道为什么超分网络要把低分辨率图先插值再输入,为什么损失函数要同时约束像素域和特征域,为什么很多模型在 edge 上表现差异巨大。这些问题都可以在这篇经典论文里找到答案的雏形。

最后再分享一个我个人的习惯:现在做超分实验,我依然会先用稀疏表示方法做一个基线结果。它慢,但它能让我直观感受“哪些高频信息是数据里真实存在的,哪些是模型自己编的”。这种对先验和观测之间关系的敏感度,读论文是得不到的,得亲手跑一遍代码才体会得到。希望你复现的时候也有类似的收获。

返回列表