最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

深層学習を使用したマルチスペクトル イメージのセマンティック セグメンテーション

この例では、7 チャネル (3 つのカラー チャネル、3 つの近赤外チャネルおよびマスク) のマルチスペクトル イメージのセマンティック セグメンテーションを実行する、U-Net 畳み込みニューラル ネットワークを学習させる方法を示します。

この例では、U-Net ネットワークに学習させる方法を示し、さらに事前学習済みの U-Net ネットワークも示します。U-Net ネットワークの学習を選択した場合、Compute Capability 3.0 以上の CUDA 対応 NVIDIA™ GPU を使用します (Parallel Computing Toolbox™ が必要)。

はじめに

セマンティック セグメンテーションでは、イメージの各ピクセルにクラスでラベル付けします。セマンティック セグメンテーションの適用例の 1 つに、森林被覆の時間的変化である森林伐採の追跡があります。環境保護機関は、森林伐採を追跡し、地域の環境的生態学的健全性を評価し測定します。

深層学習ベースのセマンティック セグメンテーションにより、高解像度の航空写真から植被を正確に測定できます。課題の 1 つに、視覚的に類似した特性のクラスを切り分けること、たとえば緑のピクセルを草、低木または樹木として区別することがあります。分類の精度を高めるために、一部のデータ セットには各ピクセルに関する追加情報を提供するマルチスペクトル イメージが含まれています。たとえば、ハームリン ビーチ州立公園のデータ セットでは、クラスをより明確に分離する近赤外チャネルでカラー イメージが補完されています。

この例では、深層学習ベースのセマンティック セグメンテーション手法を使用して、マルチスペクトル イメージ セットから領域の植被率を計算する方法を説明します。

データのダウンロード

この例では、ネットワークに学習させるために、高解像度のマルチスペクトル データセットを使用します [1]。このイメージ セットはニューヨーク州のハームリン ビーチ州立公園の上空でドローンを使用して撮影されました。このデータには、18 個のオブジェクト クラス ラベルの付いた、ラベル付き学習セット、検証セットおよびテスト セットが含まれます。データ ファイルのサイズは~ 3.0 GB です。

補助関数 downloadHamlinBeachMSIData を使用して、データセットの MAT ファイル バージョンをダウンロードします。この関数は、この例にサポート ファイルとして添付されています。

imageDir = tempdir;
url = 'http://www.cis.rit.edu/~rmk6217/rit18_data.mat';
downloadHamlinBeachMSIData(url,imageDir);

さらに、補助関数 downloadTrainedUnet を使用して、このデータセット用に事前学習済みバージョンの U-Net をダウンロードします。この関数は、この例にサポート ファイルとして添付されています。事前学習済みのモデルを使用することで、学習の完了を待つことなく例全体を実行できます。

trainedUnet_url = 'https://www.mathworks.com/supportfiles/vision/data/multispectralUnet.mat';
downloadTrainedUnet(trainedUnet_url,imageDir);

学習データの検証

データセットをワークスペースに読み込みます。

load(fullfile(imageDir,'rit18_data','rit18_data.mat'));

データの構造体を確認します。

whos train_data val_data test_data
  Name            Size                         Bytes  Class     Attributes

  test_data       7x12446x7654            1333663576  uint16              
  train_data      7x9393x5642              741934284  uint16              
  val_data        7x8833x6918              855493716  uint16              

マルチスペクトル イメージ データは numChannels x width x height 配列に配置されます。ただし、MATLAB® では、マルチチャネル イメージは width x height x numChannels 配列に配置されます。チャネルが 3 番目の次元になるようにデータを形状変更するには、補助関数 switchChannelsToThirdPlane を使用します。この関数は、この例にサポート ファイルとして添付されています。

train_data = switchChannelsToThirdPlane(train_data);
val_data   = switchChannelsToThirdPlane(val_data);
test_data  = switchChannelsToThirdPlane(test_data);

データが正しい構造体であることを確認します。

whos train_data val_data test_data
  Name                Size                     Bytes  Class     Attributes

  test_data       12446x7654x7            1333663576  uint16              
  train_data       9393x5642x7             741934284  uint16              
  val_data         8833x6918x7             855493716  uint16              

RGB カラー チャネルは 3 番目、2 番目および 1 番目のイメージ チャネルです。学習、検証およびテスト イメージの色成分をモンタージュとして表示します。イメージを画面上で明るく表示するには、関数 histeq (Image Processing Toolbox) を使用してヒストグラム均等化を行います。

figure
montage(...
    {histeq(train_data(:,:,[3 2 1])), ...
    histeq(val_data(:,:,[3 2 1])), ...
    histeq(test_data(:,:,[3 2 1]))}, ...
    'BorderSize',10,'BackgroundColor','white')
title('RGB Component of Training Image (Left), Validation Image (Center), and Test Image (Right)')

ヒストグラム均等化を行った学習データの最後の 3 つのチャネルを、モンタージュとして表示します。これらのチャネルは、近赤外域に対応します。熱の痕跡に基づいて、イメージの異なる成分を強調します。たとえば、2 番目のチャネル イメージの中央にある木は、他の 2 つのチャネルの木より詳しく表示されます。

figure
montage(...
    {histeq(train_data(:,:,4)), ...
    histeq(train_data(:,:,5)), ...
    histeq(train_data(:,:,6))}, ...
    'BorderSize',10,'BackgroundColor','white')
title('IR Channels 1 (Left), 2, (Center), and 3 (Right) of Training Image')

チャネル 7 は有効なセグメンテーション領域を示すマスクです。学習イメージ、検証イメージおよびテスト イメージのマスクを表示します。

figure
montage(...
    {train_data(:,:,7), ...
    val_data(:,:,7), ...
    test_data(:,:,7)}, ...
    'BorderSize',10,'BackgroundColor','white')
title('Mask of Training Image (Left), Validation Image (Center), and Test Image (Right)')

ラベル付きイメージには、セグメンテーション用のグラウンド トゥルース データが含まれ、それぞれのピクセルには 18 個のクラスのいずれかが割り当てられています。クラスとそれに対応する ID のリストを取得します。

disp(classes)
0. Other Class/Image Border      
1. Road Markings                 
2. Tree                          
3. Building                      
4. Vehicle (Car, Truck, or Bus)  
5. Person                        
6. Lifeguard Chair               
7. Picnic Table                  
8. Black Wood Panel              
9. White Wood Panel              
10. Orange Landing Pad           
11. Water Buoy                   
12. Rocks                        
13. Other Vegetation             
14. Grass                        
15. Sand                         
16. Water (Lake)                 
17. Water (Pond)                 
18. Asphalt (Parking Lot/Walkway)

クラス名のベクトルを作成します。

classNames = [ "RoadMarkings","Tree","Building","Vehicle","Person", ...
               "LifeguardChair","PicnicTable","BlackWoodPanel",...
               "WhiteWoodPanel","OrangeLandingPad","Buoy","Rocks",...
               "LowLevelVegetation","Grass_Lawn","Sand_Beach",...
               "Water_Lake","Water_Pond","Asphalt"]; 

ヒストグラム均等化を行った RGB 学習イメージにラベルを重ね合わせます。カラー バーをイメージに追加します。

cmap = jet(numel(classNames));
B = labeloverlay(histeq(train_data(:,:,4:6)),train_labels,'Transparency',0.8,'Colormap',cmap);

figure
title('Training Labels')
imshow(B)
N = numel(classNames);
ticks = 1/(N*2):1/N:1;
colorbar('TickLabels',cellstr(classNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none');
colormap(cmap)

学習データを MAT ファイルとして、学習ラベルを PNG ファイルとして保存します。

save('train_data.mat','train_data');
imwrite(train_labels,'train_labels.png');

学習用ランダム パッチ抽出データストアの作成

ランダム パッチ抽出データストアを使用して、ネットワークに学習データを供給します。このデータストアは、グラウンド トゥルース イメージとピクセル ラベル データを含むイメージ データストアとピクセル ラベル データストアから、対応する複数のランダム パッチを抽出します。パッチは、大きなイメージに対するメモリ不足を防ぎ、利用可能な学習データの量を効果的に増やすための一般的な手法です。

学習イメージを 'train_data.mat' から imageDatastore に格納することから始めます。MAT ファイル形式は非標準イメージ形式であるため、イメージ データを読み取るために MAT ファイル リーダーを使用しなければなりません。学習データから最初の 6 つのチャネルを抽出し、マスクを含む最後のチャネルを除外する補助 MAT ファイル リーダー matReader を使用できます。この関数は、この例にサポート ファイルとして添付されています。

imds = imageDatastore('train_data.mat','FileExtensions','.mat','ReadFcn',@matReader);

pixelLabelDatastore (Computer Vision Toolbox) を作成して、18 個のラベル付き領域を含むラベル パッチを格納します。

pixelLabelIds = 1:18;
pxds = pixelLabelDatastore('train_labels.png',classNames,pixelLabelIds);

イメージ データストアとピクセル ラベル データストアから randomPatchExtractionDatastore (Image Processing Toolbox) を作成します。それぞれのミニバッチには、サイズが 256 x 256 ピクセルの 16 個のパッチが含まれます。エポックのそれぞれの反復で、1000 個のミニバッチが抽出されます。

dsTrain = randomPatchExtractionDatastore(imds,pxds,[256,256],'PatchesPerImage',16000);

ランダム パッチ抽出データストア dsTrain は、エポックの各反復でデータのミニバッチをネットワークに渡します。データストアをプレビューしてデータを調査します。

inputBatch = preview(dsTrain);
disp(inputBatch)
        InputImage        ResponsePixelLabelImage
    __________________    _______________________

    {256×256×6 uint16}     {256×256 categorical} 
    {256×256×6 uint16}     {256×256 categorical} 
    {256×256×6 uint16}     {256×256 categorical} 
    {256×256×6 uint16}     {256×256 categorical} 
    {256×256×6 uint16}     {256×256 categorical} 
    {256×256×6 uint16}     {256×256 categorical} 
    {256×256×6 uint16}     {256×256 categorical} 
    {256×256×6 uint16}     {256×256 categorical} 

U-Net ネットワーク層の作成

この例では U-Net ネットワークのバリエーションを使用します。U-Net では、最初の一連の畳み込み層に最大プーリング層が点在し、入力イメージの解像度を逐次下げていきます。これらの層に、一連の畳み込み層が続き、その中にアップサンプリング演算処理が点在し、入力イメージの解像度を逐次上げていきます [2]。U-Net の名前は、このネットワークが文字「U」のように対称の形状で描けることに由来しています。

この例では、畳み込みに対する入力と出力が同じサイズになるように、U-Net を変更して、畳み込みでゼロパディングを使用します。補助関数 createUnet を使用して、数個のハイパーパラメーターが事前に選択された U-Net を作成します。この関数は、この例にサポート ファイルとして添付されています。

inputTileSize = [256,256,6];
lgraph = createUnet(inputTileSize);
disp(lgraph.Layers)
  58x1 Layer array with layers:

     1   'ImageInputLayer'                        Image Input                  256x256x6 images with 'zerocenter' normalization
     2   'Encoder-Section-1-Conv-1'               Convolution                  64 3x3x6 convolutions with stride [1  1] and padding [1  1  1  1]
     3   'Encoder-Section-1-ReLU-1'               ReLU                         ReLU
     4   'Encoder-Section-1-Conv-2'               Convolution                  64 3x3x64 convolutions with stride [1  1] and padding [1  1  1  1]
     5   'Encoder-Section-1-ReLU-2'               ReLU                         ReLU
     6   'Encoder-Section-1-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]
     7   'Encoder-Section-2-Conv-1'               Convolution                  128 3x3x64 convolutions with stride [1  1] and padding [1  1  1  1]
     8   'Encoder-Section-2-ReLU-1'               ReLU                         ReLU
     9   'Encoder-Section-2-Conv-2'               Convolution                  128 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]
    10   'Encoder-Section-2-ReLU-2'               ReLU                         ReLU
    11   'Encoder-Section-2-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]
    12   'Encoder-Section-3-Conv-1'               Convolution                  256 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]
    13   'Encoder-Section-3-ReLU-1'               ReLU                         ReLU
    14   'Encoder-Section-3-Conv-2'               Convolution                  256 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]
    15   'Encoder-Section-3-ReLU-2'               ReLU                         ReLU
    16   'Encoder-Section-3-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]
    17   'Encoder-Section-4-Conv-1'               Convolution                  512 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]
    18   'Encoder-Section-4-ReLU-1'               ReLU                         ReLU
    19   'Encoder-Section-4-Conv-2'               Convolution                  512 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]
    20   'Encoder-Section-4-ReLU-2'               ReLU                         ReLU
    21   'Encoder-Section-4-DropOut'              Dropout                      50% dropout
    22   'Encoder-Section-4-MaxPool'              Max Pooling                  2x2 max pooling with stride [2  2] and padding [0  0  0  0]
    23   'Mid-Conv-1'                             Convolution                  1024 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]
    24   'Mid-ReLU-1'                             ReLU                         ReLU
    25   'Mid-Conv-2'                             Convolution                  1024 3x3x1024 convolutions with stride [1  1] and padding [1  1  1  1]
    26   'Mid-ReLU-2'                             ReLU                         ReLU
    27   'Mid-DropOut'                            Dropout                      50% dropout
    28   'Decoder-Section-1-UpConv'               Transposed Convolution       512 2x2x1024 transposed convolutions with stride [2  2] and cropping [0  0  0  0]
    29   'Decoder-Section-1-UpReLU'               ReLU                         ReLU
    30   'Decoder-Section-1-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs
    31   'Decoder-Section-1-Conv-1'               Convolution                  512 3x3x1024 convolutions with stride [1  1] and padding [1  1  1  1]
    32   'Decoder-Section-1-ReLU-1'               ReLU                         ReLU
    33   'Decoder-Section-1-Conv-2'               Convolution                  512 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]
    34   'Decoder-Section-1-ReLU-2'               ReLU                         ReLU
    35   'Decoder-Section-2-UpConv'               Transposed Convolution       256 2x2x512 transposed convolutions with stride [2  2] and cropping [0  0  0  0]
    36   'Decoder-Section-2-UpReLU'               ReLU                         ReLU
    37   'Decoder-Section-2-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs
    38   'Decoder-Section-2-Conv-1'               Convolution                  256 3x3x512 convolutions with stride [1  1] and padding [1  1  1  1]
    39   'Decoder-Section-2-ReLU-1'               ReLU                         ReLU
    40   'Decoder-Section-2-Conv-2'               Convolution                  256 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]
    41   'Decoder-Section-2-ReLU-2'               ReLU                         ReLU
    42   'Decoder-Section-3-UpConv'               Transposed Convolution       128 2x2x256 transposed convolutions with stride [2  2] and cropping [0  0  0  0]
    43   'Decoder-Section-3-UpReLU'               ReLU                         ReLU
    44   'Decoder-Section-3-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs
    45   'Decoder-Section-3-Conv-1'               Convolution                  128 3x3x256 convolutions with stride [1  1] and padding [1  1  1  1]
    46   'Decoder-Section-3-ReLU-1'               ReLU                         ReLU
    47   'Decoder-Section-3-Conv-2'               Convolution                  128 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]
    48   'Decoder-Section-3-ReLU-2'               ReLU                         ReLU
    49   'Decoder-Section-4-UpConv'               Transposed Convolution       64 2x2x128 transposed convolutions with stride [2  2] and cropping [0  0  0  0]
    50   'Decoder-Section-4-UpReLU'               ReLU                         ReLU
    51   'Decoder-Section-4-DepthConcatenation'   Depth concatenation          Depth concatenation of 2 inputs
    52   'Decoder-Section-4-Conv-1'               Convolution                  64 3x3x128 convolutions with stride [1  1] and padding [1  1  1  1]
    53   'Decoder-Section-4-ReLU-1'               ReLU                         ReLU
    54   'Decoder-Section-4-Conv-2'               Convolution                  64 3x3x64 convolutions with stride [1  1] and padding [1  1  1  1]
    55   'Decoder-Section-4-ReLU-2'               ReLU                         ReLU
    56   'Final-ConvolutionLayer'                 Convolution                  18 1x1x64 convolutions with stride [1  1] and padding [0  0  0  0]
    57   'Softmax-Layer'                          Softmax                      softmax
    58   'Segmentation-Layer'                     Pixel Classification Layer   Cross-entropy loss 

学習オプションの選択

モーメンタム項付き確率的勾配降下 (SGDM) 最適化を使用してネットワークに学習させます。関数 trainingOptions を使用して SGDM 用ハイパーパラメーター設定を指定します。

深いネットワークの学習には時間がかかります。高い学習率を指定して学習を加速します。ただし、これによりネットワークの勾配が発散つまり制御できないほど増大し、ネットワークを正常に学習させることができない場合があります。勾配を意味のある範囲に維持するには、'GradientThreshold'0.05 として指定することで勾配のクリップを有効にし、'GradientThresholdMethod' を指定して勾配の L2 ノルムを使用します。

initialLearningRate = 0.05;
maxEpochs = 150;
minibatchSize = 16;
l2reg = 0.0001;

options = trainingOptions('sgdm',...
    'InitialLearnRate',initialLearningRate, ...
    'Momentum',0.9,...
    'L2Regularization',l2reg,...
    'MaxEpochs',maxEpochs,...
    'MiniBatchSize',minibatchSize,...
    'LearnRateSchedule','piecewise',...    
    'Shuffle','every-epoch',...
    'GradientThresholdMethod','l2norm',...
    'GradientThreshold',0.05, ...
    'Plots','training-progress', ...
    'VerboseFrequency',20);

ネットワークの学習

学習オプションとランダム パッチ抽出データストアを構成した後、関数 trainNetwork を使用して U-Net ネットワークに学習させます。ネットワークを学習させるには、以下のコードで doTraining パラメーターを true に設定します。学習には、Compute Capability 3.0 以上の CUDA 対応 NVIDIA™ GPU を使用してください。

次のコードで doTraining パラメーターを false のままにしておくと、この例は事前学習済みの U-Net ネットワークを返します。

メモ: NVIDIA™ Titan X での学習には約 20 時間を要します。ご使用の GPU ハードウェアによっては、さらに長い時間がかかる可能性もあります。

doTraining = false; 
if doTraining
    modelDateTime = datestr(now,'dd-mmm-yyyy-HH-MM-SS');
    [net,info] = trainNetwork(dsTrain,lgraph,options);
    save(['multispectralUnet-' modelDateTime '-Epoch-' num2str(maxEpochs) '.mat'],'net','options');
else 
    load(fullfile(imageDir,'trainedUnet','multispectralUnet.mat'));
end

ここで U-Net を使用して、マルチスペクトル イメージを意味ごとにセグメント化できます。

テスト データでの結果の予測

学習済みネットワークでフォワード パスを実行するには、補助関数 segmentImage で検証データセットを使用します。この関数は、この例にサポート ファイルとして添付されています。segmentImage は、関数 semanticseg (Computer Vision Toolbox) を使用してイメージ パッチのセグメンテーションを実行します。

predictPatchSize = [1024 1024];
segmentedImage = segmentImage(val_data,net,predictPatchSize);

セグメンテーションの有効な部分だけを抽出するには、セグメント化されたイメージに検証データのマスク チャネルを乗算します。

segmentedImage = uint8(val_data(:,:,7)~=0) .* segmentedImage;

figure
imshow(segmentedImage,[])
title('Segmented Image')

セマンティック セグメンテーションの出力にはノイズが多く含まれます。イメージの後処理を実行して、ノイズと散在ピクセルを取り除きます。関数 medfilt2 (Image Processing Toolbox) を使用して、セグメンテーションからごま塩ノイズを取り除きます。ノイズが除去され、セグメント化されたイメージを可視化します。

segmentedImage = medfilt2(segmentedImage,[7,7]);
imshow(segmentedImage,[]);
title('Segmented Image  with Noise Removed')

ヒストグラム均等化を行った RGB 検証イメージにセグメント化されたイメージを重ね合わせます。

B = labeloverlay(histeq(val_data(:,:,[3 2 1])),segmentedImage,'Transparency',0.8,'Colormap',cmap);

figure
imshow(B)
title('Labeled Validation Image')
colorbar('TickLabels',cellstr(classNames),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none');
colormap(cmap)

セグメント化されたイメージとグラウンド トゥルース ラベルを PNG ファイルとして保存します。これらは精度メトリクスを算出するために使用されます。

imwrite(segmentedImage,'results.png');
imwrite(val_labels,'gtruth.png');

セグメンテーションの精度の定量化

セグメンテーションの結果とグラウンド トゥルース ラベル用に pixelLabelDatastore (Computer Vision Toolbox) を作成します。

pxdsResults = pixelLabelDatastore('results.png',classNames,pixelLabelIds);
pxdsTruth = pixelLabelDatastore('gtruth.png',classNames,pixelLabelIds);

関数 evaluateSemanticSegmentation (Computer Vision Toolbox) を使用して、セマンティック セグメンテーションのグローバル精度を測定します。

ssm = evaluateSemanticSegmentation(pxdsResults,pxdsTruth,'Metrics','global-accuracy');
Evaluating semantic segmentation results
----------------------------------------
* Selected metrics: global accuracy.
* Processed 1 images.
* Finalizing... Done.
* Data set metrics:

    GlobalAccuracy
    ______________

       0.90698    

グローバル精度のスコアは、正しく分類されたピクセルの割合が 90% をわずかに超えていることを示しています。

植被率の計算

この例の最終目標は、マルチスペクトル イメージで植被率を計算することです。

植生としてラベル付けされたピクセルの数を求めます。ラベル ID 2 ("Trees")、13 ("LowLevelVegetation") および 14 ("Grass_Lawn") は植生のクラスです。またマスク イメージの ROI のピクセル数を合計することにより、有効なピクセル数の合計を求めます。

vegetationClassIds = uint8([2,13,14]);
vegetationPixels = ismember(segmentedImage(:),vegetationClassIds);
validPixels = (segmentedImage~=0);

numVegetationPixels = sum(vegetationPixels(:));
numValidPixels = sum(validPixels(:));

植被率は、植生のピクセルの数を有効なピクセルの数で除算して計算されます。

percentVegetationCover = (numVegetationPixels/numValidPixels)*100;
fprintf('The percentage of vegetation cover is %3.2f%%.',percentVegetationCover);
The percentage of vegetation cover is 51.72%.

参考文献

[1] Kemker, R., C. Salvaggio, and C. Kanan. "High-Resolution Multispectral Dataset for Semantic Segmentation." CoRR, abs/1703.01918. 2017.

[2] Ronneberger, O., P. Fischer, and T. Brox. "U-Net: Convolutional Networks for Biomedical Image Segmentation." CoRR, abs/1505.04597. 2015.

参考

| | | (Computer Vision Toolbox) | (Computer Vision Toolbox) | (Computer Vision Toolbox) | (Computer Vision Toolbox) | (Image Processing Toolbox) | (Image Processing Toolbox)

関連するトピック

外部の Web サイト