从SRCNN看图像超分辨率:卷积神经网络原理与工程复现

发布时间:2026/9/18 15:22:26
从SRCNN看图像超分辨率:卷积神经网络原理与工程复现 简介深度学习超分辨率方向的奠基性论文——SRCNN原理与实验细节以PDF文档形式呈现面向深度学习与人工智能初学者、计算机视觉研究者和图像处理从业者帮助理解如何用卷积神经网络实现端到端的低分辨率到高分辨率图像重建。文档基于SRCNN论文逐层拆解三个卷积层的作用将特征提取、非线性映射与图像重建对应到稀疏编码框架并给出了模型参数定义、像素级均方误差损失、峰值信噪比评价指标以及卷积核大小、卷积核数量、网络层数对复原效果影响的对比实验结论。包内共一个PDF文件整体仅三百五十三KB便于下载后随时翻阅。资源已有四百八十三人学习浏览适合快速建立超分辨率深度学习技术的理论框架并从具体实验细节中获取模型调优和训练技巧的参考。1. 从SRCNN开始理解深度学习超分辨率做图像放大的朋友都经历过这种尴尬把一张小图放大到2倍边缘全是锯齿放大到4倍纹理直接糊成一团。超分辨率Super Resolution就是专门解决这个方向的一个任务它要把低分辨率图像恢复成高分辨率图像。在SRCNN出现之前主流做法是稀疏编码这类多模块方法特征提取、字典映射、重建各自为政。SRCNN是第一个把整条流程用一个三层卷积网络端到端扛下来的模型它甚至没有池化层和全连接层只靠卷积就完成了从低分辨率输入到高分辨率输出的映射。下面我会把论文里的设计思路、训练细节和容易被忽略的复现要点拆开来讲尤其是那些在代码里看不到的实验细节。2. 稀疏编码到卷积网络SRCNN的三大模块设计SRCNN最容易被低估的一个点是它的三层卷积不是拍脑袋堆出来的而是从稀疏编码sparse coding的解题步骤里一一对应出来的。理解了这层对应关系你才知道为什么filter size和feature map数量要那样设置。2.1 为什么是稀疏编码而不是传统插值超分辨率的核心问题是低分辨率图像缺失的高频信息从哪里来。传统双三次插值只能利用邻域像素做平滑相当于猜一个低通结果。稀疏编码的做法是假设图像块可以被一个过完备字典稀疏表示于是先从低分辨率字典求出稀疏系数再用这个系数去重建高分辨率块。问题在于这个过程要分三步走低分辨率字典和高分辨率字典是分开训练的每个模块的误差会不断累积。SRCNN作者的做法是把这个三阶段流程简化成Patch extraction、Non-linear mapping、Reconstruction三个连续操作然后用卷积神经网络替代掉字典学习和迭代求解。也就是说SRCNN不是从零创造了一个新范式而是把稀疏编码的可解释性重新用CNN实现了一遍同时去掉了迭代解码器。这里要注意SRCNN在预处理阶段仍然用bicubic把低分辨率图像放大到目标尺寸再送进网络。这个bicubic没有学习能力只是把坐标对齐好让网络集中精力去修复高频细节。所以SRCNN的超分能力来自后三层卷积而不是前面的插值。我一般把bicubic这一步看成从LR坐标搬到HR坐标不改变像素信息量。2.2 Patch extraction、非线性映射与重建的等价关系论文里把三个卷积层和稀疏编码的三个步骤做了直接对应。Patch extraction从低分辨率图像里提取若干图像块每个块与一个卷积核相乘得到特征投影。如果低分辨率字典尺寸是n1那等效于用n1个f1×f1的线性滤波器作用在输入上得到n1张特征图。Non-linear mapping把低分辨率特征映射成高分辨率特征稀疏编码中这一步是字典系数的转换在SRCNN里用一层卷积完成。Reconstruction把高分辨率特征图组合成最终图像。稀疏编码中HR字典重建相当于在特征图上做线性卷积如果重建块尺寸是f3×f3则最后一层卷积核的空间尺寸就是f3×f3。这里有一个容易理解反的地方论文里并不是先提取互相重叠的patch再做平均而是直接用卷积层覆盖重叠块处理。重叠块求平均在传统方法里是为了给单独估计每个块时引入一致性约束相当于把同一像素被多个块覆盖时的估计结果调和在一起。SRCNN把这一操作隐含进了最后一层卷积。如果最后一层卷积核尺寸选得比第一层小意味着重建更依赖HR块中心部分的像素这个设计思路可以直接用来指导超参数选择。下面这段代码可以直观看到Patch extraction和卷积的关系import numpy as np def extract_patches(img, patch_size32, stride14): h, w img.shape[:2] patches [] for y in range(0, h - patch_size 1, stride): for x in range(0, w - patch_size 1, stride): patches.append(img[y:ypatch_size, x:xpatch_size]) return np.stack(patches)这里的patch_size32对应论文中训练子图像尺寸stride14对应论文中相邻子图像的步幅。步幅越小重叠越多训练样本越多但重叠区域完全一致中心像素会反复出现这相当于给局部区域施加了一致性约束。论文从91张训练图像里抽出了约24,800张32×32子图像就是在这个参数下得到的。实际上SRCNN运行时并不需要这样手动切块因为卷积天然共享权重并聚合局部信息。但理解这个切块过程才能明白为什么n1个滤波器等价于低分辨率字典里的n1个原子。如果自己复现时发现训练很慢可以先把stride调大到16减少样本量但要清楚这会降低重叠区域的一致约束PSNR通常会掉零点几个dB。2.3 端到端优化与传统多模块方法的本质区别传统稀疏编码方法中低分辨率字典和高分辨率字典分别优化每个步骤的最优解叠加起来并不是全局最优。SRCNN把三个步骤的全部权重放到参数集合P{W_1,W_2,W_3,b_1,b_2,b_3}里用均方误差MSE一个损失函数同时更新。这就是端到端的含义输入LR图像输出SR图像所有中间特征都由网络自己学习。SRCNN阶段对应的稀疏编码步骤卷积层作用Patch extraction低分辨率字典投影提取n1个LR特征图Non-linear mapping稀疏系数到HR系数映射n1维映射到n2维Reconstruction高分辨率字典重建输出HR图像训练时的核心代码比想象中短MSE的计算在PyTorch里就是一行import torch import torch.nn.functional as F loss F.mse_loss(pred, target)这里的pred是网络输出的20×20区域target是HR子图像中心的20×20区域。不要直接拿32×32的ground truth去算因为训练时卷积层没有padding输出尺寸小于输入尺寸需要用切片对齐。后面第3章会展开讲这个细节。这个设计带来的好处是三个步骤被放进同一个优化空间梯度可以同时传导到所有卷积层网络不会再出现传统方法那种模块间误差累计。但代价是网络对初始参数和学习率更敏感这就是为什么SRCNN作者在实验部分专门提醒网络变深之后反而更难收敛甚至停在坏的局部极小值。这个反直觉结论在后续很多超分网络里依然存在只不过被残差学习、归一化等手段缓解了。以常见配置为例第一层64个9×9卷积核输出1通道参数量是64×9×9645248第二层32个1×1卷积核参数量是32×64322080第三层1个5×5卷积核参数量是32×5×51801。整个模型大约8000多个参数是一个很小的网络。这么小的模型能取得当时最好的效果核心就在于它把稀疏编码的三个模块压缩到了一起。3. SRCNN网络结构与训练流程拆解SRCNN的结构非常简洁三层卷积没有池化没有全连接。但从输入到输出每一层的维度变化和训练数据对齐都有不少坑。3.1 三层卷积的参数配置与计算过程以常见复现配置为例假设输入32×32×1的灰度图第一层用64个大小为9×9的卷积核输出64张特征图由于无padding尺寸变成24×24。第二层用32个1×1卷积核把64维特征图压缩映射到32维尺寸保持24×24。第三层用1个5×5卷积核把32张特征图合成为1张输出尺寸变成20×20。层卷积核尺寸输出通道数输出空间尺寸conv19×96424×24conv21×13224×24conv35×5120×20第一层和第三层的卷积核大小并非固定论文实验里专门对比过不同size的效果常见复现一般取f19、f21、f35也可以取f23。f21意味着在特征维度上做逐像素非线性映射不改变空间尺寸这一步直接对应稀疏编码中的字典系数转换。PyTorch实现如下import torch.nn as nn class SRCNN(nn.Module): def __init__(self, num_channels1): super(SRCNN, self).__init__() self.conv1 nn.Conv2d(num_channels, 64, kernel_size9, padding0) self.conv2 nn.Conv2d(64, 32, kernel_size1, padding0) self.conv3 nn.Conv2d(32, num_channels, kernel_size5, padding0) self.relu nn.ReLU(inplaceTrue) def forward(self, x): x self.relu(self.conv1(x)) x self.relu(self.conv2(x)) x self.conv3(x) return x注意第一层和第二层后面都接ReLU第三层不接激活因为输出是像素值必须保留线性尺度。所有卷积层padding0这是为了保证训练时输出严格等于20×20与标签中心区域对齐。如果测试阶段希望输入输出尺寸一致需要临时把padding改为kernel_size // 2论文中测试阶段就是这么处理的。3.2 训练数据准备32×32子图像与24800张样本的生成论文使用的训练集是91幅图像。从每幅图像中随机裁剪32×32的子图像作为HR标签子图像之间的步幅是14。所谓子图像和patch的区别在于patch通常有重叠重建时需要平均并做后处理而sub-image被看作是独立的小图像不需要后处理。每幅图像平均能裁出两百多张子图像91幅加起来大约24,800张。为了生成对应的LR输入作者先用高斯核对HR子图像做模糊处理按尺度因子下采样再用双三次插值上采样回原始大小。这一步得到的LR子图像和HR子图像尺寸相同但高频信息已经被破坏SRCNN要学的是从这种退化后的LR图像恢复到干净HR图像的映射。实现时常见做法是先把整张HR图像下采样再上采样然后再裁剪LR和HR对应块裁块时要保持位置一致否则无法计算MSE。下面这段代码展示如何从一张HR图像生成LR/HR训练对import cv2 import numpy as np def crop_sub_images(hr_img, scale2, size32, stride14): h, w hr_img.shape[:2] # 生成退化LR先模糊再下采样再上采样 blurred cv2.GaussianBlur(hr_img, (7, 7), 1.6) lr_small blurred[::scale, ::scale] lr_img cv2.resize(lr_small, (w, h), interpolationcv2.INTER_CUBIC) hr_patches, lr_patches [], [] for y in range(0, h - size 1, stride): for x in range(0, w - size 1, stride): hr_patches.append(hr_img[y:ysize, x:xsize]) lr_patches.append(lr_img[y:ysize, x:xsize]) return np.stack(lr_patches), np.stack(hr_patches)这段代码里scale2表示放大倍率训练时每个尺度单独训练一个模型所以生成数据时就要按scale来处理。kernel_size和sigma是高斯模糊核参数论文没有给死值常见实现里用7×7、sigma1.6。生成出来的LR子图像和HR子图像尺寸都是32×32但LR更像是HR的模糊版本。把LR块直接作为网络输入HR块作为训练标签。需要注意训练时一般要把像素值除以255.0规整到0到1的浮点数范围。如果直接使用0到255的原始数值MSE量级很大梯度更新容易震荡学习率需要调到非常小才能稳住。3.3 MSE损失与反向传播的代码级说明网络前三层逐层降采样输出是20×20。计算损失时比较的对象并不是完整32×32的HR标签而是HR标签中心区域裁剪出的20×20块。这个细节在论文实验部分写得很清楚。由于卷积层没有padding输入边界像素永远无法参与输出如果强行用32×32标签去算损失数值上会出现严重的边界误差。训练循环常见写法optimizer torch.optim.SGD(model.parameters(), lr0.01, momentum0.9) criterion torch.nn.MSELoss() for epoch in range(epochs): for lr_patch, hr_patch in train_loader: lr_patch lr_patch.to(device) hr_patch hr_patch.to(device) pred model(lr_patch) # shape: [B, 1, 20, 20] target hr_patch[:, :, 6:26, 6:26] # 中心20x20 loss criterion(pred, target) optimizer.zero_grad() loss.backward() optimizer.step()lr_patch是32×32pred是20×20所以hr_patch必须切片到6:26这个位置。这里6 (32-20)//2刚好是中心区域。初始学习率0.01配合SGD动量0.9是论文时代的常见组合。SRCNN没有批归一化层所以学习率太大非常容易训练崩掉尤其是网络层数加深以后。3.4 彩色图像的YCrCb处理策略SRCNN论文的实验只处理彩色图像的亮度通道而不是RGB三通道一起做。原因是人眼对亮度变化最敏感超分辨率主要提升的是亮度细节。具体做法是把RGB转到YCrCb色彩空间只对Y通道跑SRCNNCb和Cr通道直接用双三次插值放大到目标尺寸最后把三个通道拼回彩色图像。def sr_color(image_bgr, model, scale2): ycrcb cv2.cvtColor(image_bgr, cv2.COLOR_BGR2YCrCb) y, cr, cb cv2.split(ycrcb) # 只对Y通道做bicubic放大并送入SRCNN y_up cv2.resize(y, (int(y.shape[1]*scale), int(y.shape[0]*scale)), interpolationcv2.INTER_CUBIC) y_tensor torch.from_numpy(y_up).float().unsqueeze(0).unsqueeze(0) / 255.0 with torch.no_grad(): y_sr model(y_tensor).clamp(0, 1) * 255.0 y_sr y_sr.squeeze().cpu().numpy().astype(np.uint8) # Cb、Cr通道bicubic直接放大 cr_up cv2.resize(cr, (y_up.shape[1], y_up.shape[0]), interpolationcv2.INTER_CUBIC) cb_up cv2.resize(cb, (y_up.shape[1], y_up.shape[0]), interpolationcv2.INTER_CUBIC) sr_ycrcb cv2.merge([y_sr, cr_up, cb_up]) return cv2.cvtColor(sr_ycrcb, cv2.COLOR_YCrCb2BGR)这里模型输入是bicubic放大后的Y通道输出是超分后的Y通道。Cb/Cr通道用cv2.resize直接放大。这样做除了和论文保持一致之外还有一个工程上的好处模型只处理单通道输入输出少推理更快。如果后面要接着做视频超分这种方式也很容易嵌入视频处理管线。需要注意OpenCV读入图像默认是BGR顺序转YCrCb前不要弄混通道顺序。4. 实验细节解析卷积核大小、层数与训练集尺寸的影响论文的实验部分看起来很简略却隐藏着几个直接影响复现结论的细节。只改网络结构不看这些前提PSNR可能始终比论文低一大截。4.1 Filter size与filter number对复原效果的影响论文实验首先对比了不同卷积核大小和卷积核数量。结论很直接卷积核数量越多特征向量维数越高恢复效果越好但计算量也越大卷积核尺寸越大感受野越大恢复效果也略微更好但同样会拖慢训练和推理速度。所以不存在绝对最优配置只能在效果和速度之间平衡。常见复现里第一层用64个9×9第二层用32个1×1第三层1个5×5就是基于这个平衡做出的选择。参数变化PSNR趋势速度影响Filter数量增加升高明显变慢Filter尺寸增大略升变慢网络层数加深降低变慢且难收敛上表来自论文第7.1和第7.2节的实验观察。注意“网络层数加深”这一行SRCNN论文得出的是反直觉结论模型变为更深版本后效果非但没有提升反而下降了。作者把原因解释为缺少池化和全连接网络对初始参数和学习率非常敏感训练很难收敛即使收敛也可能停在一个坏的局部极小值而且学习到的滤波器参数彼此差异不够大。4.2 为什么网络越深效果反而变差这里要展开说。现代超分辨率网络的深度通常达到几十层但SRCNN在验证网络深度时发现四层模型的PSNR不如三层。原因有两个一是没有残差连接和归一化层深层梯度信号在反向传播时容易消失学习率稍微大一点就发散小一点则收敛极慢二是超分辨率是像素级回归任务特征图的局部纹理模式在较浅层就能被捕获盲目增加层数只会放大优化难度。复现时如果你自己把网络改成4层或5层不要惊讶它的表现很可能不如原版三层。不是所有任务都遵循“越深越好”。训练集尺寸方面32×32子图像是针对scale2设置的为什么因为网络实际输出只有20×20输入尺寸再大一些也可以但32×32是作者在计算量和样本量之间取的平衡。步长14下91幅训练图能提供约24,800个样本。对scale3输入尺寸要能被3整除所以取33×33。这个细节在代码里需要处理如果直接用32×32的输入做3倍超分输出尺寸会是20×20而20不能整除3无法直接作为HR中心区域对齐。因此论文用modcrop()函数把输入尺寸调整到可被scale整除。测试时同样需要先裁剪def modcrop(img, scale): h, w img.shape[:2] h h - h % scale w w - w % scale return img[:h, :w]这个函数把图像宽高裁剪到scale的整数倍。如果不做这一步最后输出的SR图像和HR参考图会存在半个像素级别的错位PSNR会严重偏低。这不是网络本身的问题而是评估协议的问题。4.3 训练与测试时padding不一致的坑训练时所有卷积层都没有padding这是为了消除边界效应。如果加了padding训练样本的边界像素会通过补零产生虚假信息网络学到的特征不再纯粹来自图像本身。因此训练输出小一圈是正常的MSE只计算中心20×20区域。但在测试阶段我们希望输出图像与输入图像同尺寸所以需要把padding设置成kernel_size // 2让输出尺寸不变。测试时的padding不会大幅降低质量因为输入图像已经足够大单次前向传播的边缘影响可以忽略。这个差异在很多复现代码中很容易被忽略有人训练时用了padding有人测试时忘了padding结果就是训练和测试行为不一致PSNR很难复现到论文水平。一个快速检查方法是用同一张图像分别走训练模式的模型和测试模式的模型如果输出尺寸不同说明padding不一致。正常的复现流程应该是训练阶段无padding测试阶段动态调整为保持尺寸的padding或者对输入图像提前裁剪好让输出刚好对齐参考区域。4.4 PSNR评估指标的Python实现PSNR是超分辨率最常用的评价指标它的定义是信号最大可能功率与噪声功率的比值。实现时需要注意输入图像的数据类型和取值范围。论文里的PIXEL_MAX是255.0对应8bit灰度图。如果图像是0到1的浮点数PIXEL_MAX要改成1.0。import numpy as np import math def psnr(img1, img2): # img1和img2都是float类型值域0-255 mse np.mean((img1 - img2) ** 2) if mse 0: return 100 pixel_max 255.0 return 20 * math.log10(pixel_max / math.sqrt(mse))这段代码是最常见的PSNR实现。mse越接近0PSNR越高。需要注意计算PSNR前必须把两张图像对齐到相同尺寸而且最好裁掉边界区域。因为测试时如果模型没有做padding输出会比输入小一圈直接和原图比PSNR会得到一个很低的分数。通常做法是先把HR参考图裁剪到与输出相同的位置再去算PSNR。比如SRCNN测试时输出和输入同尺寸但参考图像也需要经过modcrop处理去掉尺寸无法被scale整除的边缘像素。验证指标时不要只看一张图上的PSNR常见做法是在Set5、Set14这种标准测试集上跑完整结果取平均。论文里的PSNR是在Y通道上计算的因为Cb/Cr都是插值出来的直接算彩色PSNR会把插值误差也算进去无法反映SRCNN的真实能力。5. 复现SRCNN时最容易忽略的五个检查点如果你已经动手写了SRCNN的训练代码发现PSNR总是比论文低先别急着调网络结构检查下面五个点。第一个是输入预处理bicubic放大是否发生在训练数据生成阶段。很多人直接拿原始LR小块训练没有先放大到目标尺寸输入和输出尺寸对不上。第二个是标签裁剪计算MSE时target是否从32×32中心裁出20×20。如果target还是32×32训练loss会永远偏大。第三个是学习率与初始化训练三层SRCNN时初始学习率最好从0.01开始如果Loss在第一个epoch就变成NaN把学习率降到0.001并把卷积核权重用均值为0、标准差0.001的高斯分布初始化。第四个是尺度对齐训练模型是scale2就用2的倍数来裁剪测试前先用modcrop把HR图像裁成scale的整数倍避免边缘产生半像素偏移。第五个是评估协议PSNR只在Y通道计算Cb/Cr一律用bicubic插值。为了可视化验证可以在每个epoch结束后取一张验证图像把输出和HR标签并排显示观察边缘是否有振铃效应。振铃效应说明卷积核学习到的不是清晰化滤波器而是一个高频放大器常见原因是训练数据里LR和HR对没有对齐或者高斯模糊参数设得过大。另一个快速自检方法是用一个双三次插值结果作为输入期望输出比输入更接近HR。如果SRCNN的输出比双三次插值还差说明网络没有学到有效映射多半是学习率或损失区域选错。对于放大因子3和4不要用2倍训练好的模型直接上论文明确说过每个尺度必须单独训练一个模型。这一条最容易被新手忽略也是最容易踩的坑。最后用一个快速断言检查MSE计算区域是否正确pred model(lr_patch) # 输出20x20 target hr_patch[:, :, 6:26, 6:26] assert pred.shape[2:] target.shape[2:]assert两行代码能挡住大部分训练集标签错位问题。如果shape匹配但loss降不下去去检查高斯模糊参数和bicubic预处理顺序。顺序不对的时候LR和HR的高频成分没有对应关系网络再怎么调参也学不出来。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询