深層学習を使用したマルチスペクトル イメージのセマンティック セグメンテーション
この例では、U-Net を使用して 7 チャネルのマルチスペクトル イメージのセマンティック セグメンテーションを実行する方法を示します。
セマンティック セグメンテーションでは、イメージの各ピクセルにクラスでラベル付けします。セマンティック セグメンテーションの適用例の 1 つに、森林被覆の時間的変化である森林伐採の追跡があります。環境保護機関は、森林伐採を追跡し、地域の環境的生態学的健全性を評価し測定します。
深層学習ベースのセマンティック セグメンテーションにより、高解像度の航空写真から植被を正確に測定できます。課題の 1 つに、視覚的に類似した特性のクラスを切り分けること、たとえば緑のピクセルを草、低木または樹木として区別することがあります。分類の精度を高めるために、一部のデータ セットには各ピクセルに関する追加情報を提供するマルチスペクトル イメージが含まれています。たとえば、ハームリン ビーチ州立公園のデータ セットでは、クラスをより明確に分離する 3 つの近赤外チャネルでカラー イメージが補完されています。
この例では、まず、事前学習済みの U-Net を使用してセマンティック セグメンテーションを実行する方法を示し、次に、セグメンテーションの結果を使用して植被率を計算する方法を示します。さらに、オプションとして、パッチベースの学習手法を使用して、Hamlin Beach State Park データ セットで U-Net ネットワークに学習させることもできます。
データセットのダウンロード
この例では、ネットワークに学習させるために、高解像度のマルチスペクトル データ セットを使用します [1]。このイメージ セットはニューヨーク州のハームリン ビーチ州立公園の上空でドローンを使用して撮影されました。このデータには、18 個のオブジェクト クラス ラベルの付いた、ラベル付き学習セット、検証セットおよびテスト セットが含まれます。データ ファイルのサイズは 3.0 GB です。
補助関数 downloadHamlinBeachMSIData
を使用して、データセットの MAT ファイル バージョンをダウンロードします。この関数は、この例にサポート ファイルとして添付されています。dataDir
をデータの目的の場所として指定します。
dataDir = fullfile(tempdir,"rit18_data");
downloadHamlinBeachMSIData(dataDir);
データセットを読み込みます。
load(fullfile(dataDir,"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
学習データを MAT ファイルとして、学習ラベルを PNG ファイルとして保存します。これにより、imageDatastore
と pixelLabelDatastore
を使用して学習中に学習データを簡単に読み込むことができます。
save("train_data.mat","train_data"); imwrite(train_labels,"train_labels.png");
マルチスペクトル データの可視化
このデータセットでは、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, Validation, and Test Image (Left to Right)")
ヒストグラム均等化を行った学習データの最後の 3 つのチャネルを、モンタージュとして表示します。これらのチャネルは、近赤外帯域に対応します。熱の痕跡に基づいて、イメージの異なる成分を強調します。たとえば、2 番目のチャネル イメージの中央にある木は、他の 2 つのチャネルの木より詳しく表示されます。
figure montage(... {histeq(train_data(:,:,4)),histeq(train_data(:,:,5)),histeq(train_data(:,:,6))}, ... BorderSize=10,BackgroundColor="white") title("Training Image IR Channels 1, 2, and 3 (Left to Right)")
チャネル 7 は有効なセグメンテーション領域を示すマスクです。学習イメージ、検証イメージおよびテスト イメージのマスクを表示します。
figure montage(... {train_data(:,:,7),val_data(:,:,7),test_data(:,:,7)}, ... BorderSize=10,BackgroundColor="white") title("Mask of Training, Validation, and Test Image (Left to 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 imshow(B) title("Training Labels") N = numel(classNames); ticks = 1/(N*2):1/N:1; colorbar(TickLabels=cellstr(classNames),Ticks=ticks,TickLength=0,TickLabelInterpreter="none"); colormap(cmap)
セマンティック セグメンテーションの実行
事前学習済みの U-Net ネットワークをダウンロードします。
trainedUnet_url = "https://www.mathworks.com/supportfiles/vision/data/multispectralUnet.mat"; downloadTrainedNetwork(trainedUnet_url,dataDir); load(fullfile(dataDir,"multispectralUnet.mat"));
学習済みネットワークでセマンティック セグメンテーションを実行するには、補助関数 segmentMultispectralImage
と検証データを使用します。この関数は、この例にサポート ファイルとして添付されています。関数 segmentMultispectralImage
は、関数semanticseg
(Computer Vision Toolbox)を使用してイメージ パッチのセグメンテーションを実行します。このイメージのサイズではイメージ全体を一度に処理できないため、パッチを処理する必要があります。
predictPatchSize = [1024 1024]; segmentedImage = segmentMultispectralImage(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 Segmented Image") colorbar(TickLabels=cellstr(classNames),Ticks=ticks,TickLength=0,TickLabelInterpreter="none"); colormap(cmap)
植被率の計算
セマンティック セグメンテーションの結果を使用して、関連する生態系に関する質問に答えることができます。たとえば、植生に覆われている土地の割合を知りたいとします。この質問に答えるには、植生としてラベル付けされたピクセルの数を求めます。ラベル 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%.
この例の残りの部分では、Hamlin Beach データセットで U-Net に学習させる方法を示します。
学習用ランダム パッチ抽出データストアの作成
ランダム パッチ抽出データストアを使用して、ネットワークに学習データを供給します。このデータストアは、グラウンド トゥルース イメージとピクセル ラベル データを含むイメージ データストアとピクセル ラベル データストアから、対応する複数のランダム パッチを抽出します。パッチは、大きなイメージに対するメモリ不足を防ぎ、利用可能な学習データの量を効果的に増やすための一般的な手法です。
まず、imageDatastore
で "train_data.mat"
から学習イメージを読み込みます。MAT ファイル形式は非標準イメージ形式であるため、イメージ データを読み取るために MAT ファイル リーダーを使用しなければなりません。学習データから最初の 6 つのチャネルを抽出し、マスクを含む最後のチャネルを除外する補助 MAT ファイル リーダー matRead6Channels
を使用できます。この関数は、この例にサポート ファイルとして添付されています。
imds = imageDatastore("train_data.mat",FileExtensions=".mat",ReadFcn=@matRead6Channels);
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)
58×1 Layer array with layers: 1 'ImageInputLayer' Image Input 256×256×6 images with 'zerocenter' normalization 2 'Encoder-Section-1-Conv-1' 2-D Convolution 64 3×3×6 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' 2-D Convolution 64 3×3×64 convolutions with stride [1 1] and padding [1 1 1 1] 5 'Encoder-Section-1-ReLU-2' ReLU ReLU 6 'Encoder-Section-1-MaxPool' 2-D Max Pooling 2×2 max pooling with stride [2 2] and padding [0 0 0 0] 7 'Encoder-Section-2-Conv-1' 2-D Convolution 128 3×3×64 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' 2-D Convolution 128 3×3×128 convolutions with stride [1 1] and padding [1 1 1 1] 10 'Encoder-Section-2-ReLU-2' ReLU ReLU 11 'Encoder-Section-2-MaxPool' 2-D Max Pooling 2×2 max pooling with stride [2 2] and padding [0 0 0 0] 12 'Encoder-Section-3-Conv-1' 2-D Convolution 256 3×3×128 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' 2-D Convolution 256 3×3×256 convolutions with stride [1 1] and padding [1 1 1 1] 15 'Encoder-Section-3-ReLU-2' ReLU ReLU 16 'Encoder-Section-3-MaxPool' 2-D Max Pooling 2×2 max pooling with stride [2 2] and padding [0 0 0 0] 17 'Encoder-Section-4-Conv-1' 2-D Convolution 512 3×3×256 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' 2-D Convolution 512 3×3×512 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' 2-D Max Pooling 2×2 max pooling with stride [2 2] and padding [0 0 0 0] 23 'Mid-Conv-1' 2-D Convolution 1024 3×3×512 convolutions with stride [1 1] and padding [1 1 1 1] 24 'Mid-ReLU-1' ReLU ReLU 25 'Mid-Conv-2' 2-D Convolution 1024 3×3×1024 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' 2-D Transposed Convolution 512 2×2×1024 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' 2-D Convolution 512 3×3×1024 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' 2-D Convolution 512 3×3×512 convolutions with stride [1 1] and padding [1 1 1 1] 34 'Decoder-Section-1-ReLU-2' ReLU ReLU 35 'Decoder-Section-2-UpConv' 2-D Transposed Convolution 256 2×2×512 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' 2-D Convolution 256 3×3×512 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' 2-D Convolution 256 3×3×256 convolutions with stride [1 1] and padding [1 1 1 1] 41 'Decoder-Section-2-ReLU-2' ReLU ReLU 42 'Decoder-Section-3-UpConv' 2-D Transposed Convolution 128 2×2×256 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' 2-D Convolution 128 3×3×256 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' 2-D Convolution 128 3×3×128 convolutions with stride [1 1] and padding [1 1 1 1] 48 'Decoder-Section-3-ReLU-2' ReLU ReLU 49 'Decoder-Section-4-UpConv' 2-D Transposed Convolution 64 2×2×128 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' 2-D Convolution 64 3×3×128 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' 2-D Convolution 64 3×3×64 convolutions with stride [1 1] and padding [1 1 1 1] 55 'Decoder-Section-4-ReLU-2' ReLU ReLU 56 'Final-ConvolutionLayer' 2-D Convolution 18 1×1×64 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);
ネットワークの学習または事前学習済みネットワークのダウンロード
ネットワークに学習させるには、次のコードで変数 doTraining
を true
に設定します。関数trainNetwork
を使用して、モデルに学習させます。
GPU が利用できる場合、GPU で学習を行います。GPU を使用するには、Parallel Computing Toolbox™、および CUDA® 対応の NVIDIA® GPU が必要です。詳細については、GPU 計算の要件 (Parallel Computing Toolbox)を参照してください。学習には NVIDIA Titan X で約 20 時間を要します。
doTraining = false; if doTraining net = trainNetwork(dsTrain,lgraph,options); modelDateTime = string(datetime("now",Format="yyyy-MM-dd-HH-mm-ss")); save(fullfile(dataDir,"multispectralUnet-"+modelDateTime+".mat"),"net"); end
セグメンテーションの精度の評価
検証データをセグメント化します。
segmentedImage = segmentMultispectralImage(val_data,net,predictPatchSize);
セグメント化されたイメージとグラウンド トゥルース ラベルを 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.90411
グローバル精度のスコアは、正しく分類されたピクセルの割合が 90% をわずかに超えていることを示しています。
参考文献
[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.
[3] Kemker, Ronald, Carl Salvaggio, and Christopher Kanan. "Algorithms for Semantic Segmentation of Multispectral Remote Sensing Imagery Using Deep Learning." ISPRS Journal of Photogrammetry and Remote Sensing, Deep Learning RS Data, 145 (November 1, 2018): 60-77. https://doi.org/10.1016/j.isprsjprs.2018.04.014.
参考
trainingOptions
| trainNetwork
| randomPatchExtractionDatastore
(Image Processing Toolbox) | pixelLabelDatastore
(Computer Vision Toolbox) | semanticseg
(Computer Vision Toolbox) | evaluateSemanticSegmentation
(Computer Vision Toolbox) | imageDatastore
| histeq
(Image Processing Toolbox) | unetLayers
(Computer Vision Toolbox)
関連するトピック
- 深層学習を使用したセマンティック セグメンテーション入門 (Computer Vision Toolbox)
- 深層学習を使用したセマンティック セグメンテーション
- 膨張畳み込みを使用したセマンティック セグメンテーション
- 深層学習用のデータストア