主要内容

使用深度学习进行 JPEG 图像去块

此示例说明如何使用去噪卷积神经网络 (DnCNN) 来减少图像中的 JPEG 压缩伪影。

图像压缩用于减少图像的内存占用。JPEG 图像格式采用一种功能强大的常见压缩方法,它使用质量因子来指定压缩量。降低质量值会提升压缩率并减小内存占用,但会牺牲图像的视觉质量。

JPEG 压缩是一种有损压缩,这意味着压缩过程会导致图像丢失信息。对于 JPEG 图像,这种信息丢失表现为图像中出现块伪影。如图所示,压缩程度越高,信息丢失越多,伪影越严重。具有高频成分的纹理区域(例如草地和云彩)看起来模糊不清。锐边(例如屋顶和灯塔顶部护栏)会呈现振铃效应。

JPEG 去块是减少 JPEG 图像中压缩伪影影响的过程。有几种 JPEG 去块方法,其中包括一些使用深度学习的更为高效的方法。此示例实现一种基于深度学习的方法,该方法尝试尽量减少 JPEG 压缩伪影的影响。

DnCNN 网络

此示例使用一种内置深度前馈卷积神经网络,称为 DnCNN。该网络主要是为了去除图像中的噪声而设计的。不过,也可以训练 DnCNN 架构以去除 JPEG 压缩伪影或提高图像分辨率。

参考论文 [1] 采用残差学习策略,这意味着 DnCNN 网络会学习估计残差图像。残差图像是原始图像和图像的畸变副本之间的差异。残差图像包含关于图像畸变的信息。在本示例中,畸变显示为 JPEG 块伪影。

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

如果 YOriginal 是原始图像的亮度,而 YCompressed 是包含 JPEG 压缩伪影的图像的亮度,则 DnCNN 网络的输入是 YCompressed,并且网络学习基于训练数据预测 YResidual=YCompressed-YOriginal

一旦 DnCNN 网络学会如何估计残差图像,它就可以通过将残差图像添加到压缩的亮度通道、然后将图像转换回 RGB 颜色空间来重新构造压缩的 JPEG 图像的未失真版本。

下载训练数据

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

使用辅助函数 downloadIAPRTC12Data 下载数据。此函数作为支持文件包含在本示例中。指定 dataDir 作为数据的存放位置。

dataDir = tempdir;
downloadIAPRTC12Data(dataDir);

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

trainImagesDir = fullfile(dataDir,"iaprtc12","images","00");
exts = [".jpg",".bmp",".png"];
imdsPristine = imageDatastore(trainImagesDir,FileExtensions=exts);

列出训练图像数。

numel(imdsPristine.Files)
ans = 251

准备训练数据

要创建训练数据集,请读入原始图像,并以 JPEG 文件格式输出具有不同压缩级别的图像。

指定用于呈现图像压缩伪影的 JPEG 图像质量值。质量值必须在 [0, 100] 范围内。较小的质量值会导致更高的压缩率和更严重的压缩伪影。使用更密集的小质量值采样,以使训练数据具有广泛的压缩伪影。

JPEGQuality = [5:5:40 50 60 70 80];

压缩图像以 MAT 文件形式存储在磁盘的 compressedImagesDir 目录中。计算出的残差图像以 MAT 文件形式存储在磁盘的 residualImagesDir 目录中。这些 MAT 文件以 double 数据类型存储,以在训练网络时实现更高的精确度。

compressedImagesDir = fullfile(dataDir,"iaprtc12","JPEGDeblockingData","compressedImages");
residualImagesDir = fullfile(dataDir,"iaprtc12","JPEGDeblockingData","residualImages");

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

对于每个原始训练图像,该辅助函数会生成一个质量因子为 100 的图像副本以用作参考图像,并且生成具有每个质量因子的多个图像副本以用作网络输入。该函数以 double 数据类型计算参考图像和压缩图像的亮度 (Y) 通道,以在计算残差图像时获得更精确的结果。压缩图像以 .MAT 文件形式存储在磁盘的 compressedDirName 目录中。计算出的残差图像以 .MAT 文件形式存储在磁盘的 residualDirName 目录中。

[compressedDirName,residualDirName] = createJPEGDeblockingTrainingSet(imdsPristine,JPEGQuality);

为训练创建随机补片提取数据存储

使用随机补片提取数据存储将训练数据馈送到网络。该数据存储从包含网络输入和预期网络响应的两个图像数据存储中提取随机的对应补片。

在此示例中,网络输入是压缩图像。所需的网络响应是残差图像。基于压缩图像文件集合创建一个名为 imdsCompressed 的图像数据存储。基于计算出的残差图像文件集合创建一个名为 imdsResidual 的图像数据存储。两个数据存储都需要使用辅助函数 matRead 从图像文件中读取图像数据。此函数作为支持文件包含在本示例中。

imdsCompressed = imageDatastore(compressedDirName,FileExtensions=".mat",ReadFcn=@matRead);
imdsResidual = imageDatastore(residualDirName,FileExtensions=".mat",ReadFcn=@matRead);

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

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

基于这两个图像数据存储创建 randomPatchExtractionDatastore。指定 50×50 像素的补片大小。每个图像生成 128 个大小为 50×50 像素的随机补片。指定小批量大小为 128。

patchSize = 50;
patchesPerImage = 128;
dsTrain = randomPatchExtractionDatastore(imdsCompressed,imdsResidual,patchSize, ...
    PatchesPerImage=patchesPerImage, ...
    DataAugmentation=augmenter);
dsTrain.MiniBatchSize = patchesPerImage;

随机补片提取数据存储 dsTrain 在训练轮次迭代中向网络提供小批量数据。预览从数据存储中读取的结果。

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

    {50×50 double}    {50×50 double}
    {50×50 double}    {50×50 double}
    {50×50 double}    {50×50 double}
    {50×50 double}    {50×50 double}
    {50×50 double}    {50×50 double}
    {50×50 double}    {50×50 double}
    {50×50 double}    {50×50 double}
    {50×50 double}    {50×50 double}

设置 DnCNN 层

使用 dnCNNLayers 函数创建内置 DnCNN 网络的层。默认情况下,网络深度(卷积层个数)为 20。

layers = dnCNNLayers
layers = 
  1×58 Layer array with layers:

     1   'InputLayer'   Image Input           50×50×1 images
     2   'Conv1'        2-D Convolution       64 3×3×1 convolutions with stride [1  1] and padding [1  1  1  1]
     3   'ReLU1'        ReLU                  ReLU
     4   'Conv2'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
     5   'BNorm2'       Batch Normalization   Batch normalization with 64 channels
     6   'ReLU2'        ReLU                  ReLU
     7   'Conv3'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
     8   'BNorm3'       Batch Normalization   Batch normalization with 64 channels
     9   'ReLU3'        ReLU                  ReLU
    10   'Conv4'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    11   'BNorm4'       Batch Normalization   Batch normalization with 64 channels
    12   'ReLU4'        ReLU                  ReLU
    13   'Conv5'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    14   'BNorm5'       Batch Normalization   Batch normalization with 64 channels
    15   'ReLU5'        ReLU                  ReLU
    16   'Conv6'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    17   'BNorm6'       Batch Normalization   Batch normalization with 64 channels
    18   'ReLU6'        ReLU                  ReLU
    19   'Conv7'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    20   'BNorm7'       Batch Normalization   Batch normalization with 64 channels
    21   'ReLU7'        ReLU                  ReLU
    22   'Conv8'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    23   'BNorm8'       Batch Normalization   Batch normalization with 64 channels
    24   'ReLU8'        ReLU                  ReLU
    25   'Conv9'        2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    26   'BNorm9'       Batch Normalization   Batch normalization with 64 channels
    27   'ReLU9'        ReLU                  ReLU
    28   'Conv10'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    29   'BNorm10'      Batch Normalization   Batch normalization with 64 channels
    30   'ReLU10'       ReLU                  ReLU
    31   'Conv11'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    32   'BNorm11'      Batch Normalization   Batch normalization with 64 channels
    33   'ReLU11'       ReLU                  ReLU
    34   'Conv12'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    35   'BNorm12'      Batch Normalization   Batch normalization with 64 channels
    36   'ReLU12'       ReLU                  ReLU
    37   'Conv13'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    38   'BNorm13'      Batch Normalization   Batch normalization with 64 channels
    39   'ReLU13'       ReLU                  ReLU
    40   'Conv14'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    41   'BNorm14'      Batch Normalization   Batch normalization with 64 channels
    42   'ReLU14'       ReLU                  ReLU
    43   'Conv15'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    44   'BNorm15'      Batch Normalization   Batch normalization with 64 channels
    45   'ReLU15'       ReLU                  ReLU
    46   'Conv16'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    47   'BNorm16'      Batch Normalization   Batch normalization with 64 channels
    48   'ReLU16'       ReLU                  ReLU
    49   'Conv17'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    50   'BNorm17'      Batch Normalization   Batch normalization with 64 channels
    51   'ReLU17'       ReLU                  ReLU
    52   'Conv18'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    53   'BNorm18'      Batch Normalization   Batch normalization with 64 channels
    54   'ReLU18'       ReLU                  ReLU
    55   'Conv19'       2-D Convolution       64 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]
    56   'BNorm19'      Batch Normalization   Batch normalization with 64 channels
    57   'ReLU19'       ReLU                  ReLU
    58   'Conv20'       2-D Convolution       1 3×3×64 convolutions with stride [1  1] and padding [1  1  1  1]

基于 DnCNN 层创建一个 dlnetwork 对象。使用示例输入来初始化 dlnetwork 对象。

net = dlnetwork(layers);
exampleInput = preview(dsTrain);
exampleInput = dlarray(exampleInput.InputImage{1},"SSCB");
net = initialize(net,exampleInput);

选择训练选项

使用具有动量的随机梯度下降 (SGDM) 优化来训练网络。使用 trainingOptions (Deep Learning Toolbox) 函数指定 SGDM 的超参数设置。

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

maxEpochs = 30;
initLearningRate = 0.1;
l2reg = 0.0001;
batchSize = 64;

options = trainingOptions("sgdm", ...
    Momentum=0.9, ...
    InitialLearnRate=initLearningRate, ...
    LearnRateSchedule="piecewise", ...
    GradientThresholdMethod="absolute-value", ...
    GradientThreshold=0.005, ...
    L2Regularization=l2reg, ...
    MiniBatchSize=batchSize, ...
    MaxEpochs=maxEpochs, ...
    Plots="training-progress", ...
    Verbose=false);

训练网络

默认情况下,该示例会加载预训练版本的低光恢复网络。借助预训练网络,您无需等待训练完成,即可运行整个示例。

要训练网络,请将以下代码中的 doTraining 变量设置为 true。使用 trainnet (Deep Learning Toolbox) 函数训练模型。对于回归,请使用均方误差损失。默认情况下,trainnet 函数使用 GPU(如果有)。在 GPU 上进行训练需要 Parallel Computing Toolbox™ 许可证和受支持的 GPU 设备。有关受支持设备的信息,请参阅GPU 计算要求 (Parallel Computing Toolbox)。否则,trainnet 函数使用 CPU。要指定执行环境,请使用 ExecutionEnvironment 训练选项。在 NVIDIA® Titan X GPU 上训练大约需要 40 个小时。

doTraining = false; 
if doTraining  
    [net,info] = trainnet(dsTrain,net,"mean-squared-error",options);
    modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss"));
    save("trainedJPEGDnCNN_v2-"+modelDateTime+".mat","net");
else 
    load("trainedJPEGDnCNN_v2.mat"); 
end

现在,您可以使用 DnCNN 网络从图像中去除 JPEG 压缩伪影。

使用 DnCNN 网络执行 JPEG 去块

要使用 DnCNN 执行 JPEG 去块,请按照此示例的后续步骤进行操作:

  • 用三种不同质量级别的 JPEG 压缩伪影创建示例测试图像。

  • 使用 DnCNN 网络去除压缩伪影。

  • 以可视化方式比较去块之前和之后的图像。

  • 对压缩和去块图像与未畸变参考图像之间的相似性进行量化,以此评估压缩和去块图像的质量。

创建具有块伪影的示例图像

测试数据集 testImages 包含 Image Processing Toolbox™ 中提供的 20 个未畸变图像。将这些图像加载到 imageDatastore 中。

fileNames = ["sherlock.jpg","peacock.jpg","fabric.png","greens.jpg", ...
    "hands1.jpg","kobi.png","lighthouse.png","office_4.jpg", ...
    "onion.png","pears.png","yellowlily.jpg","indiancorn.jpg", ...
    "flamingos.jpg","sevilla.jpg","llama.jpg","parkavenue.jpg", ...
    "strawberries.jpg","trailer.jpg","wagon.jpg","football.jpg"];
filePath = fullfile(matlabroot,"toolbox","images","imdata")+filesep;
filePathNames = strcat(filePath,fileNames);
testImages = imageDatastore(filePathNames);

以蒙太奇方式显示测试图像。

montage(testImages)

Figure contains an axes object. The axes object contains an object of type image.

选择其中一个测试图像以用于测试 JPEG 去块网络。

testImage = "lighthouse.png";
Ireference = imread(testImage);
imshow(Ireference)
title("Uncompressed Reference Image")

Figure contains an axes object. The axes object with title Uncompressed Reference Image contains an object of type image.

使用 JPEG Quality 值 10、20 和 50 创建三个压缩测试图像。

imwrite(Ireference,fullfile(tempdir,"testQuality10.jpg"),"Quality",10);
imwrite(Ireference,fullfile(tempdir,"testQuality20.jpg"),"Quality",20);
imwrite(Ireference,fullfile(tempdir,"testQuality50.jpg"),"Quality",50);

预处理压缩图像

将压缩版本的图像读入工作区。

I10 = imread(fullfile(tempdir,"testQuality10.jpg"));
I20 = imread(fullfile(tempdir,"testQuality20.jpg"));
I50 = imread(fullfile(tempdir,"testQuality50.jpg"));

以蒙太奇方式显示压缩图像。

montage({I50,I20,I10},Size=[1 3])
title("JPEG-Compressed Images with Quality Factor: 50, 20 and 10 (left to right)")

Figure contains an axes object. The axes object with title JPEG-Compressed Images with Quality Factor: 50, 20 and 10 (left to right) contains an object of type image.

如前文所述,DnCNN 只使用图像的亮度通道进行训练,因为人类对亮度变化的感知比对颜色变化更敏感。使用 rgb2ycbcr 函数将 JPEG 压缩图像从 RGB 颜色空间转换为 YCbCr 颜色空间。

I10ycbcr = rgb2ycbcr(I10);
I20ycbcr = rgb2ycbcr(I20);
I50ycbcr = rgb2ycbcr(I50);

应用 DnCNN 网络

为了执行网络的前向传导,请使用 denoiseImage 函数。该函数使用完全相同的训练和测试过程对图像进行去噪。您可以将 JPEG 压缩伪影视为一种图像噪声。

I10y_predicted = denoiseImage(I10ycbcr(:,:,1),net);
I20y_predicted = denoiseImage(I20ycbcr(:,:,1),net);
I50y_predicted = denoiseImage(I50ycbcr(:,:,1),net);

色度通道不需要处理。将去块后的亮度通道与原始色度通道串联起来,以获得 YCbCr 颜色空间中的去块图像。

I10ycbcr_predicted = cat(3,I10y_predicted,I10ycbcr(:,:,2:3));
I20ycbcr_predicted = cat(3,I20y_predicted,I20ycbcr(:,:,2:3));
I50ycbcr_predicted = cat(3,I50y_predicted,I50ycbcr(:,:,2:3));

使用 ycbcr2rgb 函数将去块 YCbCr 图像转换为 RGB 颜色空间。

I10_predicted = ycbcr2rgb(I10ycbcr_predicted);
I20_predicted = ycbcr2rgb(I20ycbcr_predicted);
I50_predicted = ycbcr2rgb(I50ycbcr_predicted);

以蒙太奇方式显示去块图像。

montage({I50_predicted,I20_predicted,I10_predicted},Size=[1 3])
title("Deblocked Images with Quality Factor 50, 20 and 10 (Left to Right)")

Figure contains an axes object. The axes object with title Deblocked Images with Quality Factor 50, 20 and 10 (Left to Right) contains an object of type image.

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

roi = [30 440 100 80];

将压缩图像裁切到该 ROI,并以蒙太奇方式显示结果。

i10 = imcrop(I10,roi);
i20 = imcrop(I20,roi);
i50 = imcrop(I50,roi);
montage({i50 i20 i10},Size=[1 3])
title("Patches from JPEG-Compressed Images with Quality Factor 50, 20 and 10 (Left to Right)")

Figure contains an axes object. The axes object with title Patches from JPEG-Compressed Images with Quality Factor 50, 20 and 10 (Left to Right) contains an object of type image.

将去块图像裁切到该 ROI,并以蒙太奇方式显示结果。

i10predicted = imcrop(I10_predicted,roi);
i20predicted = imcrop(I20_predicted,roi);
i50predicted = imcrop(I50_predicted,roi);
montage({i50predicted,i20predicted,i10predicted},Size=[1 3])
title("Patches from Deblocked Images with Quality Factor 50, 20 and 10 (Left to Right)")

Figure contains an axes object. The axes object with title Patches from Deblocked Images with Quality Factor 50, 20 and 10 (Left to Right) contains an object of type image.

定量比较

通过四个度量量化去块图像的质量。您可以使用 jpegDeblockingMetrics 辅助函数分别计算质量因子为 10、20 和 50 时压缩图像和去块图像的这些度量。此函数作为支持文件包含在本示例中。

  • 结构相似性索引 (SSIM)。SSIM 对照参考图像评估图像的以下三个特征的视觉效果:亮度、对比度和结构。SSIM 值越接近 1,测试图像与参考图像越一致。此处,参考图像是在 JPEG 压缩之前的未畸变原始图像 Ireference。有关该度量的详细信息,请参阅 ssim

  • 峰值信噪比 (PSNR)。PSNR 值越大,信号与失真相比就越强。有关该度量的详细信息,请参阅 psnr

  • 自然图像质量评估方法 (NIQE)。NIQE 使用基于自然场景训练的模型来测量图像感知质量。NIQE 分数越小,表示感知质量越好。有关该度量的详细信息,请参阅 niqe

  • 盲/无参考图像空间质量评估方法 (BRISQUE)。BRISQUE 使用基于具有图像畸变的自然场景训练的模型来测量图像感知质量。BRISQUE 分数越小,表示感知质量越好。有关该度量的详细信息,请参阅 brisque

jpegDeblockingMetrics(Ireference,I10,I20,I50,I10_predicted,I20_predicted,I50_predicted)
------------------------------------------
SSIM Comparison
===============
I10: 0.90624    I10_predicted: 0.91549
I20: 0.94904    I20_predicted: 0.95622
I50: 0.97238    I50_predicted: 0.97515
------------------------------------------
PSNR Comparison
===============
I10: 26.6046    I10_predicted: 27.2558
I20: 28.8015    I20_predicted: 29.5105
I50: 31.4512    I50_predicted: 31.9039
------------------------------------------
NIQE Comparison
===============
I10: 7.2194    I10_predicted: 3.873
I20: 4.5158    I20_predicted: 3.2203
I50: 2.8874    I50_predicted: 2.7533
NOTE: Smaller NIQE score signifies better perceptual quality
------------------------------------------
BRISQUE Comparison
==================
I10: 52.372    I10_predicted: 36.2841
I20: 45.3772    I20_predicted: 30.2512
I50: 27.7093    I50_predicted: 23.8529
NOTE: Smaller BRISQUE score signifies better perceptual quality

参考资料

[1] Zhang, K., W. Zuo, Y. Chen, D. Meng, and L. Zhang, "Beyond a Gaussian Denoiser:Residual Learning of Deep CNN for Image Denoising."IEEE® Transactions on Image Processing.Feb 2017.

[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.

另请参阅

| | (Deep Learning Toolbox) | (Deep Learning Toolbox) | (Deep Learning Toolbox) |

主题