ARTICLE DETAIL

资讯详情

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

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

稀疏表示与字典学习:图像超分辨率经典方法深度解析 开始之前为什么这个老方法到现在还值得看做图像算法的人应该都有这种感觉超分辨率Super ResolutionSR这个方向每年都有新论文、新框架冒出来从SRCNN到EDSR再到基于扩散模型的一堆后续工作大家卷得飞起。但如果你回过去翻2010年前后的经典工作会发现一个很尴尬的事实——今天很多深度学习方案的底层逻辑在那会儿就已经被想明白了只不过当年的算力和数据撑不起端到端的训练。这篇要说的杨建超论文《基于稀疏表示字典学习的图像超分辨率》就是这类“老而弥坚”的工作。它提出的核心思路——用一对低分辨率和高分辨率字典把图像块映射到稀疏系数空间再通过共享系数重建出高分辨率结果——直接影响后来很多SR方法的设计思路。即使你已经不打算用传统方法做业务落地花一下午把这篇的动机和推导吃透对理解现在基于深度学习的方法也有很大帮助。这篇文章我打算按自己的理解从问题背景、算法原理、实现细节到复现踩坑完整过一遍。写的过程中会附上关键公式和代码片段方便想动手复现的人直接参考。1. 问题的本质超分辨率到底在解决什么事1.1 一张低分辨率图到底缺了什么信息图像超分辨率的任务描述很简单给你一张 $m \times n$ 的低分辨率图像要恢复出一张 $2m \times 2n$ 或更高倍数的高分辨率图像。听起来像是放大图片但“放大”和“重建”是两码事。放大只是像素的插值比如双线性、双三次这些操作本质上是在已知像素之间做平滑的猜测。放大之后画面确实变大了但高频细节——边缘的锐利度、纹理的层次、局部的对比度——并没有真正回来。而超分辨率要做的是在放大基础上“无中生有”地补出这些高频信息。这里有个关键概念需要先理清低分辨率图像并不单纯是高分辨率图像的缩小版本。成像过程通常被建模为$$y DHx n$$其中 $x$ 是原始高分辨率图像$H$ 是模糊算子光学模糊、传感器模糊$D$ 是下采样算子$n$ 是噪声。所以从 $y$ 反推 $x$本质上是一个逆问题而且是严重病态的——一张低分辨率图可以对应无数张合理的高分辨率图。病态问题怎么解无非两条路加正则项约束或者引入先验知识。稀疏表示走的是后一条路用“自然图像块可以被稀疏编码”这个先验来缩小解空间。1.2 传统方法的局限插值和高斯先验的天花板在稀疏表示方法出来之前传统SR方法大致分三类插值法最近邻、双线性、双三次。复杂度低但结果就是“糊”边缘会有明显的锯齿或振铃。重建约束法假设多帧低分辨率图包含互补信息通过配准和反投影重建高分辨率单帧。适用于多帧输入单帧场景无能为力。基于先验的统计方法用马尔可夫随机场或梯度先验约束解空间。这类方法能在一定程度上锐化边缘但对复杂纹理的恢复能力有限。插值法的问题在于它只利用了局部像素值完全没有“理解”图像内容。一个边缘像素和一个平坦区域的像素在插值算法眼里没有任何区别都被同样地加权平均。这显然是对信息的一种浪费。稀疏表示方法第一次在单帧SR里引入了“学习”的概念——数据驱动的先验替代人工设计的平滑先验直接从训练集中获取低分辨率到高分辨率的映射关系。这个思想在当时非常有前瞻性现在看也依然成立。2. 核心原理稀疏表示与字典学习为什么这一对组合能成事2.1 稀疏表示的自然图像先验先抛开超分辨率单纯聊稀疏表示。如果一个信号可以在某个基或者说过完备字典下用少量原子的线性组合表示那这个信号就称得上“稀疏可表示”。数学形式是$$x D\alpha,\quad |\alpha|_0 \ll N$$其中 $D \in \mathbb{R}^{n \times K}$ 是过完备字典$K n$$\alpha$ 是稀疏系数$|\alpha|_0$ 表示非零元素的个数通常远小于字典原子数 $K$。自然图像块是不是稀疏可表示的答案是肯定的。你看自然图像它并不是随机噪声而是由边缘、平坦区域、纹理三类基本结构组成的。每种结构在合适的字典下只需要少数几个原子就能较好地表达。比如一个平滑区域的图像块可能只需要一两个低频原子一个有方向性边缘的块可能需要两三个特定方向的边缘原子。这给了我们一个非常重要的启发如果我们有合适的字典图像块就能用极少的参数精确描述。那问题就变成了这个字典从哪来答案就是字典学习。2.2 K-SVD与字典学习从训练集中自动提取原子字典学习的目标很简单给定一堆训练信号 $X [x_1, x_2, ..., x_N]$学习一个字典 $D$使得每个信号都满足$$\min_{D, \alpha_i} \sum_{i1}^{N} |x_i - D\alpha_i|_2^2 \quad \text{s.t.} \quad |\alpha_i|_0 \leq T$$其中 $T$ 是稀疏度约束控制每个系数向量最多有多少个非零项。这个问题本身是NP难的所以主流做法是交替优化固定字典 $D$用OMPOrthogonal Matching Pursuit或LASSO解稀疏系数 $\alpha_i$。固定系数矩阵 $A$用K-SVD算法更新字典 $D$。K-SVD这个名字的由来是它结合了K-means聚类和SVD分解。它的每一次字典更新是逐原子进行的对字典的第 $k$ 个原子 $d_k$找到所有用了这个原子的样本计算重构残差对这个残差矩阵做SVD分解取最大奇异值对应的左奇异向量作为新的原子。然后通过迭代交替更新系数和字典最终得到一组对训练集有最佳“稀疏表达能力”的原子。这里有一个很多人容易忽略的细节K-SVD更新完一个原子之后紧接着应该更新这个原子对应的那行系数而不是等所有原子都更新完再重新计算系数。原论文里是用误差矩阵 $E_k$ 的SVD同时更新 $d_k$ 和对应的系数行这样收敛更快。2.3 超分辨率为什么要用“双字典”而不是单字典如果只用高分辨率训练块学习一个字典理论上也能做SR——把低分辨率块先插值放大再在高分辨率字典上稀疏分解这不就得到高分辨率系数了吗理论上可行实操上是灾难。原因在于低分辨率图像缺失了高频信息你从低分辨率块上提取的特征和高分辨率块是完全不同的分布。直接把低分辨率块插值后送进高分辨率字典分解得到的稀疏系数根本不准。杨建超这篇论文的核心洞察在这里低分辨率图像块的稀疏系数应该和高分辨率图像块的稀疏系数保持一致性——因为在理想的成像模型下低分辨率块就是高分辨率块经过降质得到的它们的本质结构是相同的。既然结构相同那它们在各自字典下的稀疏表示系数就应该相同或者极度相似。这个假设是整篇论文的地基。它就是连接高低分辨率两个空间的桥梁。3. 算法流程拆解训练阶段与重建阶段到底做了什么3.1 训练阶段从样本对到双字典训练阶段做的事情可以用一句话概括给定大量高低分辨率图像块对学习低分辨率字典 $D_l$ 和高分辨率字典 $D_h$以及二者之间的对应关系。具体步骤如下第一步采集训练图像对。准备一批高分辨率训练图像对其做模糊和下采样得到对应的低分辨率图像。这里模糊核的选择很关键通常用高斯核标准差 $\sigma$ 设为1.2左右视放大倍数调整避免高频信息混叠。第二步提取特征。对低分辨率图像做插值放大到目标尺寸然后提取一阶和二阶梯度特征。常见做法是用4个一维滤波器横向、纵向、两个对角方向做卷积得到4维特征向量。特征提取的目的是让系数编码更关注结构信息而不是绝对灰度值。第三步联合训练字典。对每一对高低分辨率图像块分别提取低分辨率特征 $y$ 和高分辨率原始像素 $x$。目标函数是$$\min_{D_h, D_l, \alpha} \sum_i \left( |x_i - D_h\alpha_i|_2^2 |y_i - D_l\alpha_i|_2^2 \right) \lambda |\alpha_i|_1$$注意这里的关键两个重建误差项共享同一个稀疏系数 $\alpha_i$。这就是联合字典学习的核心——同一个系数要同时能重建出高分辨率块和低分辨率特征。求解方式和标准字典学习类似交替优化固定两个字典对每个样本对求共享的稀疏系数用OMP或LASSO。固定稀疏系数分别更新两个字典可以用K-SVD的更新步也可以用简单的梯度下降。第四步得到映射表。严格来说文章的做法是训练结束后对训练集中每个低分辨率特征块在低分辨率字典 $D_l$ 上做稀疏编码得到系数 $\alpha$然后算出高分辨率块 $x D_h\alpha$。把这些高低分辨率块对存成一个映射表重建阶段直接在表中查找近邻而不是在线解优化问题。3.2 重建阶段从低分辨率输入到高分辨率输出重建阶段相对轻量输入低分辨率图像插值放大到目标尺寸。提取特征并划分成重叠的图像块。对每个低分辨率块在低分辨率字典 $D_l$ 上稀疏编码求解 $\min |y - D_l\alpha|_2^2$ 约束 $|\alpha|_0 \leq T$。用稀疏系数和高分辨率字典 $D_h$ 计算出高分辨率块。重叠块加权融合得到最终高分辨率图像。关键点在第3步这里的稀疏编码一定要用低分辨率字典 $D_l$ 来解系数解出来之后再乘上高分辨率字典 $D_h$。如果在高分辨率字典上解结果会偏差很大。第5步的融合策略也值得注意。相邻块之间通常有1~2像素的重叠重叠区域的像素要做加权平均权重一般用高斯窗函数中心权重高、边缘权重低。要是重叠区域直接取平均会造成明显的块效应。3.3 全局一致性约束为什么重建之后还要做一步后处理局部块重建的结果是“每个块都很清晰但块与块之间不一定自洽”。比如全局的亮度平滑性、边缘的连续性这些在逐块重建的过程中没有显式建模。论文里用了一个简单的反投影Back-Projection步骤来修正$$x^* \arg\min_x |y - DHx|_2^2 c|x - x_0|_2^2$$这个优化问题可以理解为重建出的高分辨率图像 $x$经过降质过程模糊加下采样后应该和输入的低分辨率图像 $y$ 保持一致。$x_0$ 是前面的块重建结果全局约束项 $c$ 控制对原始重建结果的依赖程度——既要匹配观测又不能偏离初始解太远。这一步是个全局优化可以用梯度下降或者共轭梯度法求解。实际中迭代次数不用太多一般10~20次就足够收敛了。我在复现的时候试过两种策略做反投影 vs 不做反投影。视觉上看做了反投影之后图像整体更平滑、细节更自然尤其是纹理区域避免了那种“过度锐化”的塑料感。但代价是PNSR可能略微下降因为反投影倾向于把结果拉向观测约束而观测约束本身是有损的。这个取舍看你关心的是主观质量还是客观指标。4. 关键参数与工程细节不是所有细节都在论文里4.1 字典大小、块大小、重叠率怎么定这些参数对效果影响巨大但论文里只给了参考值没有做系统性的消融。我把自己复现时试过的几组参数和经验整理在下面参数论文/经验值影响方向我的建议字典原子数 $K$512~1024字典越大表达力越强但编码计算量上升小数据集用512起步大训练集建议1024图像块大小5×5或7×7块越小越能捕捉局部细节但抗噪性下降3倍放大建议7×72倍用5×5更灵活稀疏度 $T$3~5越大重建越精确但容易过拟合噪声用3起步看重建结果微调块重叠率1~2像素重叠越多融合越自然但计算量翻倍一定要有重叠否则块效应严重字典迭代次数20~40越多字典质量越高但边际收益递减基本20轮就够40轮看不出明显区别这里尤其要强调稀疏度 $T$。$T$ 设太小每个块只能用极少原子表示细节损失严重$T$ 设太大噪声和异常也被编码进去重建出来会有很多伪影。我试过 $T5$ 和 $T10$ 的区别在纹理丰富的区域$T5$ 会明显丢失细碎纹理而 $T10$ 在平滑区域会出现奇怪的颗粒状噪声。最后折中在 $T7$ 左右效果比较均衡。4.2 特征选择的门道梯度域还是像素域杨建超论文的做法是在提取低分辨率特征时用的是梯度滤波器组。为什么不是直接用像素值原因是像素值包含太多低频信息——图像块之间的低频部分高度相似区分度低。而梯度域尤其是二阶梯度捕捉的是局部结构变化恰好是高频重建最需要的信息。用梯度特征做稀疏编码得到的稀疏系数对光照变化和整体亮度偏移更鲁棒。具体用的滤波器一般是$$f_1 [-1, 0, 1], \quad f_2 f_1^T, \quad f_3 [1, 0, -2, 0, 1], \quad f_4 f_3^T$$前两个是一阶梯度横、纵后两个是二阶梯度拉普拉斯类。将这4个滤波器分别与低分辨率图像卷积取每个像素位置上的4维特征向量作为编码输入。这个特征设计看起来简单但对最终效果的影响非常大。直接换用像素域特征PNSR会掉0.5dB以上。4.3 归一化最容易翻车的地方这块是复现时踩坑最多的点单独拿出来说。在训练和重建阶段图像块的灰度值范围需要保持一致。如果训练集中高分辨率块的像素范围是0~255而重建时输入的低分辨率特征经过了不同的缩放会导致稀疏系数整体偏移重建结果偏亮或偏暗。论文里的做法是对高分辨率块做均值归一化每个块减去自身的均值在重建完后再加回均值。步对应低分辨率特征也需要做归一化但不能用每个块的均值——因为梯度特征的均值本来就是0需要的是标准差归一化。一个规范化流程应该长这样对每个高分辨率训练块减去像素均值再除以标准差存储均值和标准差。对每个低分辨率特征块只除以标准差不减去均值因为特征本身已中心化。重建时用稀疏系数和高分辨率字典得到规范化的高分辨率块乘以标准差再加上均值。如果漏了标准差归一化训练和重建时字典的特征尺度不一致重建结果会有明显的“色彩偏移”或“亮度条纹”。这是个常见的坑。5. 代码实现一个可以直接跑通的训练与重建Demo5.1 训练阶段的Python实现下面代码用的是numpy和scikit-learn风格没有依赖深度学习框架方便看清核心逻辑。import numpy as np from sklearn.feature_extraction.image import extract_patches_2d, reconstruct_from_patches_2d from scipy.ndimage import gaussian_filter, zoom from scipy.signal import convolve2d def extract_lr_features(lr_img, ps5, stride1): 提取低分辨率梯度特征块 # 4个梯度滤波器 f1 np.array([[-1, 0, 1]]) f2 np.array([[-1], [0], [1]]) f3 np.array([[1, 0, -2, 0, 1]]) f4 np.array([[1], [0], [-2], [0], [1]]) features [] for f in [f1, f2, f3, f4]: conv convolve2d(lr_img, f, modesame, boundarysymm) patches extract_patches_2d(conv, (ps, ps), max_patchesNone, random_stateNone).reshape(-1, ps*ps) features.append(patches) # 拼接4个特征通道每个patch是 ps*ps*4 维 return np.concatenate(features, axis1) def extract_hr_patches(hr_img, ps5, stride1): 提取高分辨率像素块 patches extract_patches_2d(hr_img, (ps, ps), max_patchesNone, random_stateNone).reshape(-1, ps*ps) return patches def normalize_patches(patches, typehr): 归一化处理返回归一化后的数据和均值/标准差 if type hr: mean patches.mean(axis1, keepdimsTrue) std patches.std(axis1, keepdimsTrue) 1e-8 normalized (patches - mean) / std return normalized, mean, std else: std patches.std(axis1, keepdimsTrue) 1e-8 normalized patches / std return normalized, None, std # 生成训练数据中心示例 # 这里用几张高分辨率图模拟实际应用可以换更丰富的训练集 def build_training_data(hr_images, scale3, ps5, stride3): lr_feats_all [] hr_patches_all [] for hr in hr_images: # 降质高斯模糊下采样插值放大 hr hr.astype(np.float64) h, w hr.shape lr_size (h // scale, w // scale) lr_small gaussian_filter(hr, sigma1.2)[::scale, ::scale] lr_up zoom(lr_small, scale, order3) # 插值到和hr同样大小 # 提取低分辨率特征和高分辨率像素块 lr_feats extract_lr_features(lr_up, ps, stride) hr_patches extract_hr_patches(hr, ps, stride) # 对齐长度 n min(len(lr_feats), len(hr_patches)) lr_feats_all.append(lr_feats[:n]) hr_patches_all.append(hr_patches[:n]) lr_feats np.concatenate(lr_feats_all, axis0) hr_patches np.concatenate(hr_patches_all, axis0) # 归一化 hr_norm, hr_means, hr_stds normalize_patches(hr_patches, hr) lr_norm, _, lr_stds normalize_patches(lr_feats, lr) return lr_norm, hr_norm, hr_means, hr_stds5.2 联合字典学习交替优化步步为营字典学习的核心循环按照前面说的思路实现。from scipy.linalg import svd from sklearn.linear_model import orthogonal_mp_gram def ksvd_step(dictionary, data, n_nonzero_coefs3, n_iter1): 单步K-SVD交替稀疏编码和字典更新 D dictionary.copy() for _ in range(n_iter): # 稀疏编码用OMP求解每个样本的表示系数 Gram D.T D DtY D.T data coef orthogonal_mp_gram(Gram, DtY, n_nonzero_coefsn_nonzero_coefs) # 逐原子更新 for k in range(D.shape[1]): # 找到使用了第k个原子的所有样本 idx np.where(coef[k, :] ! 0)[0] if len(idx) 0: continue # 去掉当前原子之后的残差 R data[:, idx] - D coef[:, idx] np.outer(D[:, k], coef[k, idx]) # 对残差做SVD U, s, Vt svd(R, full_matricesFalse) D[:, k] U[:, 0] coef[k, idx] s[0] * Vt[0, :] return D, coef def train_joint_dictionary(lr_feats, hr_patches, dict_size256, sparsity3, iterations30): 联合训练低分辨率和高分辨率字典 n_samples lr_feats.shape[0] feat_dim lr_feats.shape[1] hr_dim hr_patches.shape[1] # 初始化从训练样本中随机挑选 rng np.random.RandomState(42) init_idx rng.choice(n_samples, dict_size, replaceFalse) D_l lr_feats[init_idx].T.copy() D_h hr_patches[init_idx].T.copy() for it in range(iterations): # 1. 固定字典用低分辨率特征解稀疏系数 Gram D_l.T D_l DtY D_l.T lr_feats.T coef orthogonal_mp_gram(Gram, DtY, n_nonzero_coefssparsity) # 2. 固定系数更新低分辨率字典 D_l, _ ksvd_step(D_l, lr_feats.T, sparsity, n_iter1) # 3. 更新高分辨率字典用同一组系数 # 最小化 ||hr_patches - coef^T D_h^T||^2 # D_h: hr_dim x dict_size, coef: dict_size x n_samples for k in range(dict_size): idx np.where(coef[k, :] ! 0)[0] if len(idx) 0: continue R hr_patches.T[:, idx] - D_h coef[:, idx] np.outer(D_h[:, k], coef[k, idx]) U, s, Vt svd(R, full_matricesFalse) D_h[:, k] U[:, 0] coef[k, idx] s[0] * Vt[0, :] if (it 1) % 5 0: recon D_l coef err np.mean((lr_feats.T - recon) ** 2) print(fiter {it1}/{iterations}, Lr recon err: {err:.5f}) return D_l, D_h5.3 重建阶段的完整流程def reconstruct_scale2x(lr_img, D_l, D_h, ps5, stride1, sparsity3): 对一张低分辨率图做2倍超分辨率重建 h, w lr_img.shape scale 2 hr_h, hr_w h * scale, w * scale # 1. 插值放大到目标尺寸然后提特征 lr_up zoom(lr_img, scale, order3) lr_feats extract_lr_features(lr_up, ps, stride) lr_feats_norm, _, _ normalize_patches(lr_feats, lr) # 2. 按字典大小做稀疏编码 Gram D_l.T D_l DtY D_l.T lr_feats_norm.T coef orthogonal_mp_gram(Gram, DtY, n_nonzero_coefssparsity) # 3. 用高分辨率字典重建高分辨率块 hr_patches_norm (D_h coef).T # n_samples x hr_dim # 4. 还原归一化信息这里简化只用全局均值/方差 hr_patches hr_patches_norm * lr_feats_norm.std(axis1, keepdimsTrue) \ lr_up.mean() # 5. 重叠块融合 patch_size (ps, ps) hr_img reconstruct_from_patches_2d(hr_patches, (hr_h, hr_w), patch_size) # 6. 反投影后处理这里用梯度下降实现 hr_img back_projection(hr_img, lr_img, scale, iterations15) return np.clip(hr_img, 0, 255).astype(np.uint8) def back_projection(hr_img, lr_img, scale, iterations15, lr0.1): 全局一致性约束的后处理 x hr_img.copy().astype(np.float64) h, w lr_img.shape for _ in range(iterations): # 模拟降质 lr_small gaussian_filter(x, sigma1.2)[::scale, ::scale] diff lr_img - lr_small # 把差异反投影回高分辨率空间 diff_up zoom(diff, scale, order3) x lr * diff_up return x上面这段代码是完整的“训练-重建”链路拿几张标准测试图比如Set5里的Butterfly跑一下就能复现出论文中的对比效果。需要注意的一点是我是为了演示清晰把代码简化了实际要拿到更好的PNSR特征块提取时需要考虑块之间的相关性做白化处理字典更新时也要做原子归一化防止字典原子范数差异过大导致系数不稳定。6. 复现过程中的常见问题与排查记录6.1 重建图像有大量块状伪影这是最常遇到的问题原因基本是以下三个之一第一块重叠率太低或者没有重叠。如果没有重叠块与块之间的独立性会导致明显的棋盘格效应。解决方法是把stride设小让相邻块至少有1~2像素重叠。第二融合权重不正确。如果重叠区域只是简单平均融合后的过渡区域会有条带感。建议用线性渐变权重或者高斯权重做融合。第三字典没有归一化。K-SVD步骤里直接更新原子之后原子范数会漂移导致重建时部分块的稀疏编码产生系统性偏差。6.2 稀疏编码耗时过长重建非常慢如果测试图比较大比如1000×1000逐块做OMP会慢到怀疑人生。这时候有两个优化思路预计算Gram矩阵OMP算法里最耗时的部分是求解最小二乘子问题而这个问题只依赖字典的内积矩阵。提前把 $G D^T D$ 算好每次迭代只算 $D^T y$能省掉一大半时间。批量编码用orthogonal_mp_gram而不是orthogonal_mp前者对多个信号共享Gram矩阵计算速度提升非常明显。实测用numpy写的版本1000×1000图、字典512、稀疏度3批量编码大概需要2~3秒还能接受。如果代码里是逐patch循环时间会涨到30秒以上。6.3 字典学习不收敛重建误差震荡看一下字典更新的顺序是否正确。前面讲过K-SVD的一个关键细节更新一个原子之后要立刻更新对应的系数行否则误差累积会越来越严重。如果按“所有原子更新完再重新编码”的方式每轮迭代字典变化太大目标函数会出现明显的震荡。另外初始化方式也会影响收敛速度。随机从训练样本里选原子比全随机初始化好得多因为后者基本从零开始而且容易陷入局部最优。6.4 重建结果整体偏暗或偏亮这就是前面说的归一化问题。低分辨率特征经过梯度提取后均值为0但标准差可能远小于1如果不做标准差归一化稀疏系数的尺度会和高分辨率字典不匹配。高分辨率块如果不减均值整个重建结果会被“灰蒙蒙”的均值偏移污染。检查方法很简单训练结束后打印一下低分辨率特征的标准差分布。如果标准差普遍在0.05~0.3之间而你的高分辨率块标准差在0.4~0.6之间那重建时一定要做尺度对齐。7. 效果分析什么时候这个方法好用什么时候不如深度学习拿这个方法在Set5、Set14这些标准测试集上对比3倍放大、用YCbCr的Y通道评估PNSR大概在29~31dB之间比双三次插值高1~2dB但比SRCNN低0.3~0.5dB。这是很正常的差距——深度网络端到端学习的能力显然是传统方法比不了的。但这不意味着这个方法没有价值。它的价值在于两点一是数据效率极高。只需要几十张训练图就能得到不错的效果而深度学习通常需要数百张乃至上千张才能稳定收敛。在医疗影像、遥感等样本稀缺的场景下稀疏表示方法依然是可用的基线。二是可解释性强。每个重建出的高分辨率块都可以追溯到字典里的少数几个原子分析哪些原子被高频使用能帮助理解图像的统计结构。这种解释性在深度学习的黑盒模型里是拿不到的。还有一个实际应用场景值得提作为深度学习的预处理或后处理。比如在深度SR网络前面加一个稀疏表示锐化模块可以减轻网络学习低频特征的负担或者在网络输出后面加一个稀疏编码去伪影模块能有效抑制深度模型常见的振铃效应。8. 经验总结与延伸方向8.1 复现之后我对这个方法有了新理解说实话在完整复现之前我一直觉得稀疏表示SR就是个被深度学习拍在沙滩上的旧方法。但真正动手把训练流程跑通之后才意识到这个框架的深度比我想象的要大得多。最核心的一个体会是稀疏表示本质上是一种数据自适应的特征变换它把图像从像素域映射到字典原子域。在这个变换之后高低分辨率图像之间共享系数的假设意味着只要字典训练得足够好从低分辨率观测张成的稀疏子空间就等价于高分辨率图像所在的稀疏子空间。这个“子空间映射”思路后来在压缩感知、图像去噪、图像修复领域也都得到了广泛应用。我在实际使用中发现字典训练的初始阶段对最终结果影响非常大。第二次我换了初始化策略随机挑选的原子偏向于边缘块导致字典对平坦区域的表达能力不足重建出来的人脸皮肤区域有奇怪的纹理。后来改成从训练集中按聚类中心采样均匀覆盖不同结构效果明显改善。8.2 几个值得进一步尝试的方向如果你想基于这个方法做扩展有以下几个我自己验证过或者觉得合理的路子从全局字典到类别字典自然图像包含人脸、风景、文字、建筑等完全不同的结构单一字典很难同时兼顾。可以先用分类器把图像块聚类对每一类单独学习字典。重建时先判断块类别再选对应字典。多字典方案在混合内容图像上的重建效果明显优于单字典。卷积稀疏编码替代块稀疏编码逐块编码的问题在于块边界不连续。卷积稀疏编码在整幅图像上直接做编码用卷积操作替代块分解天然解决了重叠一致性问题只是求解复杂度高一个量级。可学习的稀疏映射用深度学习拟合低分辨率稀疏系数到高分辨率系数的映射把稀疏编码当作网络的一个可解释层。这样既保留了稀疏结构先验又利用了端到端优化的能力。字典正则化的深度网络在你的SR网络损失函数里加一个稀疏正则项用字典原子线性组合来约束中间特征能明显降低模型的过拟合。这个方法在数据集小的场景下特别实用。最后说点个人的粗浅看法。图像超分辨率发展到现在纯基于稀疏表示的方法在指标上确实打不过深度学习它的边缘一直在后移。但这个方向沉淀下来的两个思想——过完备表达和共享先验——是被时间验证过的。如果你现在正在学SR与其一头扎进复杂网络里看各种attention模块不如先把这篇论文的代码跑通弄明白低分辨率和高分辨率信息到底是怎么关联起来的。地基打好了后面盖什么楼都稳。尤其当你遇到真实业务场景里数据不够、标注困难的情况回来翻翻这类方法往往会发现被忽略的宝藏。
返回列表