Main Content

深層学習を使用したイメージの高解像度化

この例では、Very-Deep Super-Resolution (VDSR) ニューラル ネットワークを使用して低解像度イメージから高解像度イメージを作成する方法を説明します。

超解像処理は低解像度イメージから高解像度イメージを作成するプロセスです。この例では、単一イメージ超解像処理 (SISR) について考えます。目標は、1 つの低解像度イメージから 1 つの高解像度イメージを復元することです。SISR が難しいのは、一般的にはイメージの高周波数成分を低解像度イメージから回復することができないためです。高周波数情報なしでは、高解像度イメージの画質には限界があります。さらに、1 つの低解像度イメージから複数の高解像度イメージの候補が生成される可能性があるため、SISR は不良設定問題です。

深層学習アルゴリズムを含め、いくつかの手法が SISR を実行するために提案されています。この例では、Very-Deep Super-Resolution (VDSR) と呼ばれる SISR のための深層学習アルゴリズムを扱います [1]。

VDSR ネットワーク

VDSR は、単一イメージ超解像処理を実行するために設計された、畳み込みニューラル ネットワーク アーキテクチャです [1]。VDSR ネットワークは低解像度イメージと高解像度イメージとのマッピングを学習します。低解像度イメージと高解像度イメージは類似したイメージの内容をもち、異なるのは主に細かい高周波数成分であるため、このマッピングは可能です。

VDSR は残差学習法を使用しています。これはネットワークに残差イメージを推定するように学習させるものです。超解像処理のコンテキストでは、残差イメージは高解像度の参照イメージと参照イメージのサイズに一致するように双三次内挿を使用してアップスケーリングされた低解像度イメージとの差分です。残差イメージには、イメージの詳細な高周波数成分に関する情報が含まれます。

VDSR ネットワークは、カラー イメージの輝度から残差イメージを検出します。イメージの輝度チャネル Y は、各ピクセルの明度を赤、緑および青ピクセル値の線形結合で表したものです。一方、イメージの 2 つの色差チャネル Cb および Cr は、赤、緑および青ピクセル値の異なる線形結合で、色差情報を表します。VDSR は輝度チャネルだけを使用して学習します。人間の知覚は色の変化よりも明度の変化に敏感であるからです。

Yhighres が高解像度イメージの輝度で、Ylowres が双三次内挿を使用してアップスケーリングされた低解像度イメージの輝度である場合、VDSR ネットワークへの入力は Ylowres で、ネットワークは学習データから Yresidual = Yhighres-Ylowres を予測するために学習します。

VDSR ネットワークに残差イメージを推定するように学習させた後、推定した残差イメージをアップサンプリングした低解像度イメージに追加し、イメージを RGB 色空間に変換し直すことによって、高解像度イメージを再構築できます。

倍率は、参照イメージのサイズの低解像度イメージのサイズに対するものです。低解像度イメージでは、イメージの高周波数成分に関する情報が特に失われるため、倍率が高くなるほど SISR はさらに不良設定になります。VDSR は大規模受容野を使用して、この問題を解決します。この例では、複数の倍率でのスケーリングによる拡張を使用して、VDSR ネットワークを学習させます。スケーリングによる拡張では、低倍率のイメージ コンテキストをネットワークが活用できるため、高倍率での結果が改善されます。さらに、VDSR ネットワークは整数でない倍率のイメージを受け入れることで一般化できます。

学習データとテスト データのダウンロード

20,000 個の静止した自然イメージから成る、IAPR TC-12 ベンチマークをダウンロードします [2]。このデータ セットには、人物、動物、都市などの写真が含まれます。データ ファイルのサイズは~1.8 GB です。学習データ セットをダウンロードしない場合、コマンド ラインで load("trainedVDSRNet.mat"); と入力して、事前学習済みの VDSR ネットワークを読み込むことができます。その後、この例の VDSR ネットワークを使用した単一イメージ超解像処理の実行の節に直接進みます。

データをダウンロードするために補助関数 downloadIAPRTC12Data を使用します。この関数は、この例にサポート ファイルとして添付されています。dataDir をデータの目的の場所として指定します。

dataDir = tempdir;
downloadIAPRTC12Data(dataDir);

この例ではネットワークを IAPR TC-12 ベンチマーク データの小さなサブセットで学習させます。imageCLEF 学習データを読み込みます。すべてのイメージは 32 ビット JPEG カラー イメージです。

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

学習イメージの数を表示します。

numel(pristineImages.Files)
ans = 616

学習データの準備

学習データ セットを作成するには、アップサンプリングされたイメージと対応する残差イメージで構成されるイメージのペアを生成します。

アップサンプリングされたイメージは、ディレクトリ upsampledDirName の MAT ファイルとしてディスクに格納されます。ネットワーク応答を表す計算された残差イメージは、ディレクトリ residualDirName の MAT ファイルとしてディスクに格納されます。ネットワークの学習時に精度を向上させるため、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( ...
    RandRotatio=@()randi([0,1],1)*90, ...
    RandXReflection=true);

アップサンプリングされたイメージのデータストアと残差イメージのデータストアからのランダム化されたパッチ抽出を実行する randomPatchExtractionDatastore (Image Processing Toolbox) を作成します。パッチ抽出は、小さなイメージ パッチ、すなわちタイルを、より大きな 1 つのイメージから抽出するプロセスです。このタイプのデータ拡張は、非常に小さなサイズの入力イメージで多くのネットワーク アーキテクチャに学習させることができる image-to-image 回帰問題でよく使用されます。これは、元の学習セットに含まれるフルサイズの各イメージから多くのパッチを抽出することができ、学習セットのサイズが大幅に大きくなることを意味します。

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 ネットワークを定義します。

第 1 層 imageInputLayer はイメージ パッチに対して作用します。パッチ サイズは、ネットワークの最上位層の応答に影響する空間イメージ領域であるネットワーク受容野に基づきます。ネットワーク受容野がイメージ内の高レベルの特徴をすべて確認できるように、受容野のサイズはイメージのサイズと同じであるのが理想的です。この場合、畳み込み層 D をもつネットワークに対して、受容野は (2D+1) 行 (2D+1) 列です。

VDSR には 20 個の畳み込み層があるため、受容野とイメージ パッチのサイズは 41 行 41 列です。イメージ入力層は、1 チャネルのイメージを受け入れます。VDSR が輝度チャネルだけを使用して学習しているからです。

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

イメージ入力層に続いて、サイズが 3 行 3 列の 64 個のフィルターがある 2 次元畳み込み層があります。ミニバッチのサイズによりフィルター数が決まります。それぞれの畳み込みの後で、特徴マップのサイズが入力サイズと同じになるように、各畳み込み層の入力をゼロ パディングします。He の手法 [3] は、ニューロン学習が非対称になるようにランダムな値に重みを初期化します。各畳み込み層の後に、ネットワークに非線形性をもたらす ReLU 層が続きます。

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

ReLU 層を指定します。

relLayer = reluLayer(Name="ReLU1");

中間層は 18 個あり、畳み込み層と正規化線形ユニット層が交互に配置されています。各畳み込み層はサイズ 3 x 3 x 64 の 64 個のフィルターを含みます。1 つのフィルターは 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

最後から 2 番目の層は、イメージを再構成するサイズ 3 x 3 x 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];

学習オプションの指定

モーメンタム項付き確率的勾配降下 (SGDM) 最適化を使用してネットワークに学習させます。関数 trainingOptions を使用して SGDM 用ハイパーパラメーター設定を指定します。最初の学習率は 0.1 であり、10 エポックごとに 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);

ネットワークの学習

この例では既定で、超解像処理イメージを倍率 2、3、および 4 で学習させた、事前学習済みバージョンの VDSR ネットワークを読み込みます。この事前学習済みのネットワークを使用することで、学習の完了を待たずに、テスト イメージの超解像処理を実行できます。

VDSR ネットワークに学習させるには、以下のコードで変数 doTrainingtrue に設定します。関数 trainNetwork を使用してネットワークを学習させます。

GPU が利用できる場合、GPU で学習を行います。GPU を使用するには、Parallel Computing Toolbox™、および CUDA® 対応の NVIDIA® GPU が必要です。詳細については、リリース別の GPU サポート (Parallel Computing Toolbox)を参照してください。学習には NVIDIA Titan X で約 6 時間を要します。

doTraining = false;
if doTraining
    net = trainNetwork(dsTrain,layers,options);
    modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss"));
    save("trainedVDSR-"+modelDateTime+".mat","net");
else
    load("trainedVDSRNet.mat");
end

VDSR ネットワークを使用した単一イメージ超解像処理の実行

VDSR ネットワークを使用して単一イメージ超解像処理 (SISR) を実行するには、この例の残りの手順に従って以下を行います。

  • 高解像度の参照イメージからサンプルの低解像度イメージを作成します。

  • 深層学習に依存しない従来型のイメージ処理ソリューションである双三次内挿を使用した SISR を、低解像度イメージで実行します。

  • VDSR ニューラル ネットワークを使用した、低解像度イメージでの SISR を実行します。

  • 双三次内挿と VDSR を使用して再構成した高解像度イメージを視覚的に比較します。

  • 超解像処理イメージと高解像度の参照イメージとの類似度を定量化することで、このイメージの画質を評価します。

低解像度サンプル イメージの作成

テスト データ セット 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)

いずれかのテスト イメージを選択して、超解像ネットワークのテストに使用します。

testImage = "sherlock.jpg";
Ireference = imread(testImage);
Ireference = im2double(Ireference);
imshow(Ireference)
title("High-Resolution Reference Image")

imresizeを 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);

双三次内挿を使用して、輝度と 2 つの色差チャネルをアップスケーリングします。アップサンプリングされた色差チャネル 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 width height] の形式で指定します。各要素は、ROI の左上隅の x と y 座標、幅と高さで定義されます。

roi = [360 50 400 350];

高解像度イメージをこの ROI にトリミングして、結果をモンタージュとして表示します。VDSR イメージは、双三次内挿を使用して作成された高解像度イメージと比べ、細部がより鮮明で、鋭いエッジがあります。

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

画質メトリクスを使用して、双三次内挿による高解像度イメージと VDSR イメージを定量的に比較します。参照イメージは元の高解像度イメージ Ireference で、サンプルの低解像度イメージを作成する前のものです。

参照イメージに対する各イメージのピーク S/N 比 (PSNR) を測定します。PSNR の値が大きいほど、一般には画質が高いことを示します。このメトリクスの詳細については、psnr (Image Processing Toolbox)を参照してください。

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

各イメージの構造的類似性 (SSIM) 指数を測定します。SSIM は参照イメージに対してイメージの 3 つの特性である輝度、コントラスト、構造の視覚的影響を評価します。SSIM 値が 1 に近いほど、テスト イメージは参照イメージに近づきます。このメトリクスの詳細については、ssim (Image Processing Toolbox)を参照してください。

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

Naturalness Image Quality Evaluator (NIQE) を使用して、知覚的画質を測定します。NIQE スコアが小さいほど知覚的画質が良好なことを示します。このメトリクスの詳細については、niqe (Image Processing Toolbox)を参照してください。

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

テスト イメージのセット全体に対して、倍率 2、3 および 4 で平均 PSNR および SSIM を計算します。簡単にするために、補助関数 vdsrMetrics を使用して平均メトリクスを計算できます。この関数は、この例にサポート ファイルとして添付されています。

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

Average PSNR for Bicubic = 31.467070
Average PSNR for VDSR = 31.481973
Average SSIM for Bicubic = 0.935820
Average SSIM for VDSR = 0.947057

Results for Scale factor 3

Average PSNR for Bicubic = 28.107057
Average PSNR for VDSR = 28.430546
Average SSIM for Bicubic = 0.883927
Average SSIM for VDSR = 0.894634

Results for Scale factor 4

Average PSNR for Bicubic = 27.066129
Average PSNR for VDSR = 27.846590
Average SSIM for Bicubic = 0.863270
Average SSIM for VDSR = 0.878101

双三次内挿と比較すると、各倍率で 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.

参考

| | | | | |

関連するトピック