Main Content

本页的翻译已过时。点击此处可查看最新英文版本。

使用深度学习执行单图像超分辨率

此示例说明如何训练超深超分辨率 (VDSR) 神经网络,然后使用 VDSR 网络基于单个低分辨率图像估计高分辨率图像。

此示例说明如何训练 VDSR 网络,并提供预训练的 VDSR 网络。如果您选择训练 VDSR 网络,强烈推荐使用支持 CUDA、计算能力为 3.0 或更高的 NVIDIA™ GPU。使用 GPU 需要 Parallel Computing Toolbox™。

简介

超分辨率是从低分辨率图像创建高分辨率图像的过程。此示例以单图像超分辨率 (SISR) 为例,其目标是从一个低分辨率图像中恢复一个高分辨率图像。SISR 具有挑战性,因为高频图像内容通常无法从低分辨率图像中恢复。没有高频信息,所得高分辨率图像的质量是有限的。此外,SISR 是一个不适定问题,因为一个低分辨率图像可以产生若干种可能的高分辨率图像。

目前已提出包括深度学习算法在内的几种方法来执行 SISR。此示例探索了一种适用于 SISR 的深度学习算法,称为超深超分辨率 (VDSR) [1]。

VDSR 网络

VDSR 是一种用于执行单图像超分辨率 [1] 的卷积神经网络架构。VDSR 网络学习低分辨率图像和高分辨率图像之间的映射。这种映射是有可能获得的,因为低分辨率和高分辨率图像具有相似的图像内容,主要区别在于高频细节。

VDSR 采用一种残差学习策略,这意味着网络会学习估计残差图像。在超分辨率中,残差图像是高分辨率参考图像和低分辨率图像之间的差异,低分辨率图像已使用双三次插值扩增以匹配参考图像的大小。残差图像包含关于图像高频细节的信息。

VDSR 网络基于彩色图像的亮度检测残差图像。图像的亮度通道 Y 使用红色、绿色和蓝色像素值的线性组合来表示每个像素的亮度。另一方面,图像的两个色度通道 CbCr 使用红色、绿色和蓝色像素值的其他线性组合来表示色差信息。VDSR 仅使用亮度通道进行训练,因为人类对亮度变化的感知比对颜色变化更敏感。

如果 Yhighres 是高分辨率图像的亮度,Ylowres 是使用双三次插值扩增的低分辨率图像的亮度,则 VDSR 网络的输入是 Ylowres,网络学习基于训练数据预测 Yresidual=Yhighres-Ylowres

在 VDSR 网络学习估计残差图像后,您可以将估计的残差图像添加到上采样的低分辨率图像中,然后将图像转换回 RGB 颜色空间,从而重构高分辨率图像。

参考图像大小与低分辨率图像大小的关系可通过缩放因子来表示。随着缩放因子的增大,SISR 变得更加不适定,因为低分辨率图像会丢失有关高频图像内容的更多信息。VDSR 通过使用大感受野来解决此问题。此示例通过缩放增强来训练多缩放因子 VDSR 网络。缩放增强可以改进在较大缩放因子下的结果,因为网络可以利用较小缩放因子下的图像上下文。此外,VDSR 网络可以泛化至接受具有非整数缩放因子的图像。

下载训练和测试数据

下载包含 20,000 个静态自然图像的 IAPR TC-12 Benchmark [2]。该数据集包括人、动物、城市等的照片。数据文件的大小约为 1.8 GB。如果您不想下载训练数据集,则可以通过在命令行中键入 load('trainedVDSR-Epoch-100-ScaleFactors-234.mat'); 来加载预训练的 VDSR 网络。然后,直接转至本示例中的使用 VDSR 网络执行单图像超分辨率部分。

使用辅助函数 downloadIAPRTC12Data 下载数据。此函数作为支持文件包含在本示例中。

imagesDir = tempdir;
url = 'http://www-i6.informatik.rwth-aachen.de/imageclef/resources/iaprtc12.tgz';
downloadIAPRTC12Data(url,imagesDir);

此示例将使用 IAPR TC-12 Benchmark 数据的一个小型子集来训练网络。加载 imageCLEF 训练数据。所有图像均为 32 位 JPEG 彩色图像。

trainImagesDir = fullfile(imagesDir,'iaprtc12','images','02');
exts = {'.jpg','.bmp','.png'};
pristineImages = imageDatastore(trainImagesDir,'FileExtensions',exts);

列出训练图像的数量。

numel(pristineImages.Files)
ans = 616

准备训练数据

要创建训练数据集,请生成由上采样图像和对应残差图像组成的图像对。

上采样图像以 MAT 文件形式存储在磁盘的 upsampledDirName 目录中。计算出的表示网络响应的残差图像以 MAT 文件形式存储在磁盘的 residualDirName 目录中。这些 MAT 文件以 double 数据类型存储,以在训练网络时实现更高的精度。

upsampledDirName = [trainImagesDir filesep 'upsampledImages'];
residualDirName = [trainImagesDir filesep 'residualImages'];

使用辅助函数 createVDSRTrainingSet 预处理训练数据。此函数作为支持文件包含在本示例中。

该辅助函数对 trainImages 中的每个原始图像执行以下操作:

  • 将图像转换为 YCbCr 颜色空间

  • 以不同缩放因子缩小亮度 (Y) 通道,以创建低分辨率样本图像,然后使用双三次插值将图像大小调整为原始大小

  • 计算原始图像和调整大小后的图像之间的差异。

  • 将调整大小后的图像和残差图像保存到磁盘。

scaleFactors = [2 3 4];
createVDSRTrainingSet(pristineImages,scaleFactors,upsampledDirName,residualDirName);

定义训练集的预处理流程

在本示例中,网络输入是使用双三次插值进行上采样后的低分辨率图像。所需的网络响应是残差图像。从输入图像文件集合中创建名为 upsampledImages 的图像数据存储。从计算的残差图像文件集合中创建名为 residualImages 的图像数据存储。两个数据存储都需要使用辅助函数 matRead 从图像文件中读取图像数据。此函数作为支持文件包含在本示例中。

upsampledImages = imageDatastore(upsampledDirName,'FileExtensions','.mat','ReadFcn',@matRead);
residualImages = imageDatastore(residualDirName,'FileExtensions','.mat','ReadFcn',@matRead);

创建指定数据增强参数的 imageDataAugmenter。在训练期间使用数据增强来更改训练数据,这可以有效地增加可用的训练数据量。此处,增强器指定 90 度的随机旋转和 x 方向上的随机翻转。

augmenter = imageDataAugmenter( ...
    'RandRotation',@()randi([0,1],1)*90, ...
    'RandXReflection',true);

创建一个 randomPatchExtractionDatastore (Image Processing Toolbox) 以根据上采样图像和残差图像数据存储执行随机补片提取。补片提取是从单个较大图像中提取大量小图像补片或图块的过程。这种类型的数据增强经常用于图像到图像回归问题,其中许多网络架构可以基于非常小的输入图像大小进行训练。这意味着可以从原始训练集中的每个全大小图像中提取大量补片,这极大地增加了训练集的大小。

patchSize = [41 41];
patchesPerImage = 64;
dsTrain = randomPatchExtractionDatastore(upsampledImages,residualImages,patchSize, ...
    "DataAugmentation",augmenter,"PatchesPerImage",patchesPerImage);

生成的数据存储 dsTrain 在一轮训练的每次迭代中向网络提供小批量数据。预览从数据存储中读取的结果。

inputBatch = preview(dsTrain);
disp(inputBatch)
      InputImage      ResponseImage 
    ______________    ______________

    {41×41 double}    {41×41 double}
    {41×41 double}    {41×41 double}
    {41×41 double}    {41×41 double}
    {41×41 double}    {41×41 double}
    {41×41 double}    {41×41 double}
    {41×41 double}    {41×41 double}
    {41×41 double}    {41×41 double}
    {41×41 double}    {41×41 double}

设置 VDSR 层

此示例使用 Deep Learning Toolbox™ 中的 41 个单独层定义 VDSR 网络,这些层包括:

第一个层,即 imageInputLayer,对图像补片进行操作。补片大小基于网络感受野,它是一个空间图像区域,影响网络中最顶层的响应。理想情况下,网络感受野与图像大小相同,这样感受野可以看到图像中的所有高级特征。在这种情况下,对于具有 D 个卷积层的网络,感受野是 (2D+1)×(2D+1)。

VDSR 有 20 个卷积层,因此感受野和图像补片大小为 41×41。图像输入层接受具有一个通道的图像,因为仅使用亮度通道训练 VDSR。

networkDepth = 20;
firstLayer = imageInputLayer([41 41 1],'Name','InputLayer','Normalization','none');

图像输入层后跟一个二维卷积层,其中包含 64 个大小为 3×3 的滤波器。小批量大小决定滤波器的数量。对每个卷积层的输入填零,使特征图与每次卷积后的输入始终大小相同。He 的方法 [3] 将权重初始化为随机值,以在神经元学习中引入不对称性。每个卷积层后跟一个 ReLU 层,该层在网络中引入非线性。

convLayer = convolution2dLayer(3,64,'Padding',1, ...
    'WeightsInitializer','he','BiasInitializer','zeros','Name','Conv1');

指定一个 ReLU 层。

relLayer = reluLayer('Name','ReLU1');

中间各层包含 18 个交替的卷积层和修正线性单元层。每个卷积层包含 64 个大小为 3×3×64 的滤波器,每个滤波器对 64 个通道中的 3×3 空间区域进行操作。如前所述,每个卷积层都后跟一个 ReLU 层。

middleLayers = [convLayer relLayer];
for layerNumber = 2:networkDepth-1
    convLayer = convolution2dLayer(3,64,'Padding',[1 1], ...
        'WeightsInitializer','he','BiasInitializer','zeros', ...
        'Name',['Conv' num2str(layerNumber)]);
    
    relLayer = reluLayer('Name',['ReLU' num2str(layerNumber)]);
    middleLayers = [middleLayers convLayer relLayer];    
end

倒数第二个层是一个卷积层,该层具有一个大小为 3×3×64 的滤波器,用于重构图像。

convLayer = convolution2dLayer(3,1,'Padding',[1 1], ...
    'WeightsInitializer','he','BiasInitializer','zeros', ...
    'NumChannels',64,'Name',['Conv' num2str(networkDepth)]);

最后一层是一个回归层,而不是 ReLU 层。该回归层计算残差图像和网络预测之间的均方误差。

finalLayers = [convLayer regressionLayer('Name','FinalRegressionLayer')];

串联所有层以形成 VDSR 网络。

layers = [firstLayer middleLayers finalLayers];

您也可以使用 vdsrLayers 辅助函数来创建 VDSR 层。此函数作为支持文件包含在本示例中。

layers = vdsrLayers;

指定训练选项

使用具有动量的随机梯度下降 (SGDM) 优化来训练网络。使用 trainingOptions 函数指定 SGDM 的超参数设置。学习率最初为 0.1,每 10 轮降低为原来的十分之一。进行 100 轮训练。

训练深度网络是很费时间的。通过指定高学习率可加快训练速度。然而,这可能会导致网络的梯度爆炸或不受控制地增长,阻碍网络训练成功。要将梯度保持在有意义的范围内,请通过将 'GradientThreshold' 指定为 0.01 来启用梯度裁剪,并指定 'GradientThresholdMethod' 使用梯度的 L2-范数。

maxEpochs = 100;
epochIntervals = 1;
initLearningRate = 0.1;
learningRateFactor = 0.1;
l2reg = 0.0001;
miniBatchSize = 64;
options = trainingOptions('sgdm', ...
    'Momentum',0.9, ...
    'InitialLearnRate',initLearningRate, ...
    'LearnRateSchedule','piecewise', ...
    'LearnRateDropPeriod',10, ...
    'LearnRateDropFactor',learningRateFactor, ...
    'L2Regularization',l2reg, ...
    'MaxEpochs',maxEpochs, ...
    'MiniBatchSize',miniBatchSize, ...
    'GradientThresholdMethod','l2norm', ...
    'GradientThreshold',0.01, ...
    'Plots','training-progress', ...
    'Verbose',false);

训练网络

在配置训练选项和随机补片提取数据存储后,使用 trainNetwork 函数训练 VDSR 网络。要训练网络,请将以下代码中的 doTraining 参数设置为 true。强烈建议使用具有 3.0 或更高计算能力的、支持 CUDA 的 NVIDIA™ GPU 来进行训练。

如果您将以下代码中的 doTraining 参数保留为 false,则该示例会返回一个可对图像进行缩放因子为 2、3 和 4 的超分辨率处理的预训练 VDSR 网络。

注意:在 NVIDIA™ Titan X 上训练需要大约 6 个小时,根据您的 GPU 硬件情况,训练时间可能会更长。

doTraining = false;
if doTraining
    modelDateTime = datestr(now,'dd-mmm-yyyy-HH-MM-SS');
    net = trainNetwork(dsTrain,layers,options);
    save(['trainedVDSR-' modelDateTime '-Epoch-' num2str(maxEpochs*epochIntervals) '-ScaleFactors-' num2str(234) '.mat'],'net','options');
else
    load('trainedVDSR-Epoch-100-ScaleFactors-234.mat');
end

使用 VDSR 网络执行单图像超分辨率

要使用 VDSR 网络执行单图像超分辨率 (SISR),请按照此示例的后续步骤进行操作。该示例的后续部分显示如何:

  • 基于高分辨率参考图像创建一个示例低分辨率图像。

  • 使用双三次插值对该低分辨率图像执行 SISR,双三次插值是一种不依赖于深度学习的传统图像处理解决方案。

  • 使用 VDSR 神经网络对该低分辨率图像执行 SISR。

  • 以可视化方式比较使用双三次插值和 VDSR 重构的高分辨率图像。

  • 量化超分辨率图像与高分辨率参考图像的相似性,以此评估前者的质量。

创建示例低分辨率图像

创建一个低分辨率图像,用于比较使用深度学习的超分辨率结果和使用双三次插值等传统图像处理方法的结果。测试数据集 testImages 包含 Image Processing Toolbox™ 中提供的 21 个未失真图像。将这些图像加载到 imageDatastore 中。

exts = {'.jpg','.png'};
fileNames = {'sherlock.jpg','car2.jpg','fabric.png','greens.jpg','hands1.jpg','kobi.png', ...
    'lighthouse.png','micromarket.jpg','office_4.jpg','onion.png','pears.png','yellowlily.jpg', ...
    'indiancorn.jpg','flamingos.jpg','sevilla.jpg','llama.jpg','parkavenue.jpg', ...
    'peacock.jpg','car1.jpg','strawberries.jpg','wagon.jpg'};
filePath = [fullfile(matlabroot,'toolbox','images','imdata') filesep];
filePathNames = strcat(filePath,fileNames);
testImages = imageDatastore(filePathNames,'FileExtensions',exts);

将测试图像显示为蒙太奇。

montage(testImages)

选择其中一个图像作为超分辨率的参考图像。您也可以自选其他高分辨率图像作为参考图像。

indx = 1; % Index of image to read from the test image datastore
Ireference = readimage(testImages,indx);
Ireference = im2double(Ireference);
imshow(Ireference)
title('High-Resolution Reference Image')

使用 imresize (Image Processing Toolbox) 和缩放因子 0.25 创建高分辨率参考图像的一个低分辨率版本。图像的高频分量在缩减分辨率过程中丢失。

scaleFactor = 0.25;
Ilowres = imresize(Ireference,scaleFactor,'bicubic');
imshow(Ilowres)
title('Low-Resolution Image')

使用双三次插值提高图像分辨率

要提高图像分辨率,一种不借助于深度学习的标准方法是双三次插值。使用双三次插值扩增低分辨率图像,使所得高分辨率图像与参考图像大小相同。

[nrows,ncols,np] = size(Ireference);
Ibicubic = imresize(Ilowres,[nrows ncols],'bicubic');
imshow(Ibicubic)
title('High-Resolution Image Obtained Using Bicubic Interpolation')

使用预训练的 VDSR 网络提高图像分辨率

如前文所述,VDSR 只使用图像的亮度通道进行训练,因为人类对亮度变化的感知比对颜色变化更敏感。

使用 rgb2ycbcr (Image Processing Toolbox) 函数将低分辨率图像从 RGB 颜色空间转换为亮度 (Iy) 和色度(IcbIcr)通道。

Iycbcr = rgb2ycbcr(Ilowres);
Iy = Iycbcr(:,:,1);
Icb = Iycbcr(:,:,2);
Icr = Iycbcr(:,:,3);

使用双三次插值扩增亮度通道和两个色度通道。上采样的色度通道 Icb_bicubicIcr_bicubic 不需要进一步处理。

Iy_bicubic = imresize(Iy,[nrows ncols],'bicubic');
Icb_bicubic = imresize(Icb,[nrows ncols],'bicubic');
Icr_bicubic = imresize(Icr,[nrows ncols],'bicubic');

对扩增的亮度分量 Iy_bicubic 应用经过训练的 VDSR 网络。观察来自最终层(回归层)的activations。网络的输出是所需的残差图像。

Iresidual = activations(net,Iy_bicubic,41);
Iresidual = double(Iresidual);
imshow(Iresidual,[])
title('Residual Image from VDSR')

将残差图像与扩增的亮度分量相加,得到高分辨率 VDSR 亮度分量。

Isr = Iy_bicubic + Iresidual;

将高分辨率 VDSR 亮度分量与扩增的颜色分量串联起来。使用 ycbcr2rgb (Image Processing Toolbox) 函数将图像转换为 RGB 颜色空间。结果为使用 VDSR 得到的最终高分辨率彩色图像。

Ivdsr = ycbcr2rgb(cat(3,Isr,Icb_bicubic,Icr_bicubic));
imshow(Ivdsr)
title('High-Resolution Image Obtained Using VDSR')

可视化和定量比较

为了在视觉上更好地理解高分辨率图像,请检查每个图像中的一个小区域。使用向量 roi 指定关注区域 (ROI),格式为 [x y 宽度 高度]。其中的元素定义 ROI 左上角的 x 坐标和 y 坐标,以及它的宽度和高度。

roi = [320 30 480 400];

将高分辨率图像裁剪到该 ROI,并将结果显示为蒙太奇。与使用双三次插值创建的高分辨率图像相比,VDSR 图像具有更清晰的细节和更锐利的边缘。

montage({imcrop(Ibicubic,roi),imcrop(Ivdsr,roi)})
title('High-Resolution Results Using Bicubic Interpolation (Left) vs. VDSR (Right)');

使用图像质量指标来定量比较使用双三次插值的高分辨率图像和 VDSR 图像。参考图像是准备低分辨率图像示例时所用的原始高分辨率图像 Ireference

对照参考图像测量每个图像的峰值信噪比 (PSNR)。PSNR 值越大,通常表示图像质量越好。有关该指标的详细信息,请参阅 psnr (Image Processing Toolbox)

bicubicPSNR = psnr(Ibicubic,Ireference)
bicubicPSNR = 38.4747
vdsrPSNR = psnr(Ivdsr,Ireference)
vdsrPSNR = 39.2346

测量每个图像的结构相似性指数 (SSIM)。SSIM 对照参考图像评估图像三个特性的视觉效果:亮度、对比度和结构。SSIM 值越接近 1,测试图像与参考图像越一致。有关该指标的详细信息,请参阅 ssim (Image Processing Toolbox)

bicubicSSIM = ssim(Ibicubic,Ireference)
bicubicSSIM = 0.9861
vdsrSSIM = ssim(Ivdsr,Ireference)
vdsrSSIM = 0.9874

使用自然图像质量评价方法 (NIQE) 测量图像感知质量。NIQE 分数越小,表示感知质量越好。有关该指标的详细信息,请参阅 niqe (Image Processing Toolbox)

bicubicNIQE = niqe(Ibicubic)
bicubicNIQE = 5.1721
vdsrNIQE = niqe(Ivdsr)
vdsrNIQE = 4.7611

分别计算缩放因子为 2、3 和 4 时整个测试图像集的平均 PSNR 和 SSIM。为简单起见,您可以使用辅助函数 superResolutionMetrics 来计算平均指标。此函数作为支持文件包含在本示例中。

scaleFactors = [2 3 4];
superResolutionMetrics(net,testImages,scaleFactors);
Results for Scale factor 2

Average PSNR for Bicubic = 31.809683
Average PSNR for VDSR = 31.921784
Average SSIM for Bicubic = 0.938194
Average SSIM for VDSR = 0.949404

Results for Scale factor 3

Average PSNR for Bicubic = 28.170441
Average PSNR for VDSR = 28.563952
Average SSIM for Bicubic = 0.884381
Average SSIM for VDSR = 0.895830

Results for Scale factor 4

Average PSNR for Bicubic = 27.010839
Average PSNR for VDSR = 27.837260
Average SSIM for Bicubic = 0.861604
Average SSIM for VDSR = 0.877132

对于每个缩放因子,VDSR 都具有比双三次插值更好的指标分数。

参考资料

[1] Kim, J., J. K. Lee, and K. M. Lee."Accurate Image Super-Resolution Using Very Deep Convolutional Networks."Proceedings of the IEEE® Conference on Computer Vision and Pattern Recognition.2016, pp. 1646-1654.

[2] Grubinger, M., P. Clough, H. Müller, and T. Deselaers."The IAPR TC-12 Benchmark:A New Evaluation Resource for Visual Information Systems."Proceedings of the OntoImage 2006 Language Resources For Content-Based Image Retrieval.Genoa, Italy.Vol. 5, May 2006, p. 10.

[3] He, K., X. Zhang, S. Ren, and J. Sun."Delving Deep into Rectifiers:Surpassing Human-Level Performance on ImageNet Classification."Proceedings of the IEEE International Conference on Computer Vision, 2015, pp. 1026-1034.

另请参阅

| | | | | |

相关主题