基於深度學習的語義分割

此示例顯示如何使用深度學習訓練語義分段網絡。html

語義分割網絡對圖像中的每一個像素進行分類,從而產生按類別分割的圖像。語義分割的應用包括用於自主駕駛的道路分割和用於醫學診斷的癌細胞分割。有關詳細信息,請參閱語義分段基礎知識(計算機視覺系統工具箱)。web

爲了說明訓練過程,本例訓練SegNet [1],一種設計用於語義圖像分割的卷積神經網絡(CNN)。用於語義分段的其餘類型網絡包括徹底卷積網絡(FCN)和U-Net。此處顯示的培訓程序也能夠應用於這些網絡。算法

此示例使用劍橋大學CamVid數據集 [2]進行培訓。此數據集是包含駕駛時得到的街道視圖的圖像集合。該數據集爲32種語義類提供了像素級標籤,包括汽車,行人和道路。數據庫

創建

此示例建立具備從VGG-16網絡初始化的權重的SegNet網絡。要得到VGG-16,請安裝適用於VGG-16網絡的Deep Learning Toolbox™模型安裝完成後,運行如下代碼以驗證安裝是否正確。瀏覽器

vgg16();
警告:重命名重複的類名。

此外,下載預訓練版的SegNet。預訓練模型容許您運行整個示例,而無需等待培訓完成。微信

pretrainedURL = 'https: //www.mathworks.com/supportfiles/vision/data/segnetVGG16CamVid.mat ' ;
pretrainedFolder = fullfile(tempdir,'pretrainedSegNet');
pretrainedSegNet = fullfile(pretrainedFolder,'segnetVGG16CamVid.mat'); 
若是〜存在(pretrainedFolder,'dir'
    MKDIR(pretrainedFolder);
    disp('下載預訓練的SegNet(107 MB)......');
    websave(pretrainedSegNet,pretrainedURL);
結束

強烈建議使用具備計算能力3.0或更高版本的支持CUDA的NVIDIA™GPU來運行此示例。使用GPU須要Parallel Computing Toolbox™。網絡

下載CamVid數據集

從如下URL下載CamVid數據集。架構

imageURL = 'http: //web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/files/701_StillsRaw_full.zip ' ;
labelURL = 'http: //web4.cs.ucl.ac.uk/staff/g.brostow/MotionSegRecData/data/LabeledApproved_full.zip ' ;

outputFolder = fullfile(tempdir,'CamVid');

若是〜存在(outputFolder,'dir'
   
    MKDIR(outputFolder)
    labelsZip = fullfile(outputFolder,'labels.zip');
    imagesZip = fullfile(outputFolder,'images.zip');   
    
    disp('下載16 MB CamVid數據集標籤......');
    websave(labelsZip,labelURL);
    unzip(labelsZip,fullfile(outputFolder,'labels'));
    
    disp('下載557 MB CamVid數據集圖像......');  
    websave(imagesZip,imageURL);       
    解壓縮(imagesZip,fullfile(outputFolder,'images'));    
結束

注意:數據的下載時間取決於您的Internet鏈接。上面使用的命令會阻止MATLAB,直到下載完成。或者,您可使用Web瀏覽器首先將數據集下載到本地磁盤。要使用從Web下載的文件,請將outputFolder上面變量更改爲下載文件的位置。ide

加載CamVid圖像

使用imageDatastore加載CamVid圖像。imageDatastore使您可以高效地裝載大量收集圖像的磁盤上。函數

imgDir = fullfile(outputFolder,'images''701_StillsRaw_full');
imds = imageDatastore(imgDir);

顯示其中一個圖像。

I = readimage(imds,1);
我= histeq(I);
imshow(I)

加載CamVid像素標籤圖像

使用pixelLabelDatastore加載CamVid像素標籤圖像數據。pixelLabelDatastore將像素標籤數據和標籤ID封裝到類名映射中。

按照原始SegNet論文[1]中使用的程序,將CamVid中的32個原始類分組爲11個類。指定這些類。

class = [
     「Sky」 
    「Building」 
    「Pole」 
    「Road」 
    「Pavement」 
    「Tree」 
    「SignSymbol」 
    「Fence」 
    「Car」 
    「Pedestrian」 
    「Bicyclist」 
    ];

要將32個類減小爲11個,將原始數據集中的多個類組合在一塊兒。例如,「Car」是「Car」,「SUVPickupTruck」,「Truck_Bus」,「Train」和「OtherMoving」的組合。使用支持函數返回分組的標籤ID,該函數camvidPixelLabelIDs在本示例的末尾列出。

labelIDs = camvidPixelLabelIDs();

使用類和標籤ID來建立 pixelLabelDatastore.

labelDir = fullfile(outputFolder,'labels');
pxds = pixelLabelDatastore(labelDir,classes,labelIDs);

經過將其疊加在圖像上來讀取並顯示其中一個像素標記的圖像。

C = readimage(pxds,1);

cmap = camvidColorMap;

B = labeloverlay(I,C,'ColorMap',cmap);
imshow(B)
pixelLabelColorbar(CMAP,班);

沒有顏色疊加的區域沒有像素標籤,在訓練期間不使用。

分析數據集統計

要查看CamVid數據集中類標籤的分佈,請使用countEachLabel此函數按類標籤計算像素數。

tbl = countEachLabel(pxds)
tbl = 11×3表 名稱PixelCount ImagePixelCount ____________ __________ _______________
 'Sky'7.6801e + 07 4.8315e + 08  '建築'1.1737e + 08 4.8315e + 08  '極'4.7987e + 06 4.8315e + 08  '道路'1.4054e + 08 4.8453e + 08  '路面'3.3614e + 07 4.7209e + 08  '樹'5.4259e + 07 4.479e + 08  'SignSymbol'5.2242e + 06 4.6863e + 08  '圍欄'6.9211e + 06 2.516e + 08  '汽車'2.4437e + 07 4.8315e + 08  'Pedestrian'3.4029e + 06 4.4444e + 08  '自行車'2.5912e + 06 2.6196e + 08 

按類可視化像素計數。

frequency = tbl.PixelCount / sum(tbl.PixelCount);

杆(1:numel(類),頻率)
xticks(1:numel(類)) 
xticklabels(tbl.Name)
xtickangle(45)
ylabel('頻率'

理想狀況下,全部類都有相同數量的觀察。可是,CamVid中的類是不平衡的,這是街道場景的汽車數據集中的常見問題。因爲天空,建築物和道路覆蓋了圖像中的更多區域,所以這些場景具備比行人和騎車像素更多的天空,建築物和道路像素。若是處理不當,這種不平衡可能對學習過程有害,由於學習偏向於支配階級。稍後在此示例中,您將使用類權重來處理此問題。

調整CamVid數據的大小

CamVid數據集中的圖像爲720 x 960.爲減小訓練時間和內存使用量,請將圖像和像素標籤圖像的大小調整爲360 x 480 resizeCamVidImagesresizeCamVidPixelLabels並支持本示例末尾列出的功能。

imageFolder = fullfile(outputFolder,'imagesResized',filesep);
imds = resizeCamVidImages(imds,imageFolder);

labelFolder = fullfile(outputFolder,'labelsResized',filesep);
pxds = resizeCamVidPixelLabels(pxds,labelFolder);

準備培訓和測試集

使用來自數據集的60%的圖像訓練SegNet。其他圖像用於測試。如下代碼將圖像和像素標籤數據隨機分紅訓練和測試集。

[imdsTrain,imdsTest,pxdsTrain,pxdsTest] = partitionCamVidData(imds,pxds);

60/40分割產生如下數量的訓練和測試圖像:

numTrainingImages = numel(imdsTrain.Files)
numTrainingImages = 421
numTestingImages = numel(imdsTest.Files)
numTestingImages = 280

建立網絡

使用segnetLayers建立SegNet網絡使用VGG-16的權重初始化。segnetLayers自動執行從VGG-16傳輸權重所需的網絡轉換,並添加語義分段所需的附加層。輸出segnetLayersLayerGraph表示SegNet 對象。LayerGraph對象封裝網絡層和所述層之間的鏈接。

imageSize = [360 480 3];
numClasses = numel(classes);
lgraph = segnetLayers(imageSize,numClasses,'vgg16');
警告:重命名重複的類名。

基於數據集中圖像的大小選擇圖像大小。根據CamVid中的類選擇類的數量。

使用類權重的餘額類

如前所示,CamVid中的類不平衡。要改進培訓,您可使用班級加權來平衡班級。使用先前計算的像素標籤計數countEachLabel並計算中值頻率類權重。

imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount;
classWeights = median(imageFreq)./ imageFreq
classWeights = 11×1
 0.3182 0.2082 5.0924 0.1744 0.7103 0.4175 4.5371 1.8386 1.0000 6.6059

使用a指定類權重pixelClassificationLayer

pxLayer = pixelClassificationLayer('Name''labels''Classes',tbl.Name,'ClassWeights',classWeights)
pxLayer = 
  PixelClassificationLayer具備屬性:

            名稱:'標籤'
         職業:[11×1分類]
    ClassWeights:[11×1雙]
      OutputSize:'auto'

   超參數
    LossFunction:'crossentropyex'

pixelClassificationLayer經過刪除當前pixelClassificationLayer並添加新圖層,使用new更新SegNet網絡電流pixelClassificationLayer名爲'pixelLabels'。使用刪除它,使用removeLayers添加新的addLayers,並使用新層鏈接到網絡的其他部分connectLayers

lgraph = removeLayers(lgraph,'pixelLabels');
lgraph = addLayers(lgraph,pxLayer);
lgraph = connectLayers(lgraph,'softmax''labels');

選擇培訓選項

用於訓練的優化算法是具備動量的隨機梯度降低(SGDM)。使用trainingOptions指定用於SGDM的超參數。

options = trainingOptions('sgdm'... 
    'Momentum',0.9,...... 
    'InitialLearnRate',1e-3,...... 
    'L2Regularization',0.0005,...... 
    'MaxEpochs',100,...... ' MiniBatchSize',4,...... 'Shuffle''every-epoch'... 'CheckpointPath',tempdir,...... 'VerboseFrequency',2);  
    
    
    
    

小於4的小批量用於減小訓練時的內存使用量。您能夠根據系統上的GPU內存量增長或減小此值。

另外,'CheckpointPath'設置爲臨時位置。此名稱 - 值對可在每一個訓練時期結束時保存網絡檢查點。若是因爲系統故障或停電而致使培訓中斷,您能夠從保存的檢查點恢復培訓。確保指定的位置'CheckpointPath'有足夠的空間來存儲網絡檢查點。例如,保存100個SegNet檢查點須要大約11 GB的磁盤空間,由於每一個檢查點都是107 MB。

數據擴充

在訓練期間使用數據增長來向網絡提供更多示例,由於它有助於提升網絡的準確性。這裏,隨機左/右反射和+/- 10像素的隨機X / Y平移用於數據加強。使用imageDataAugmenter指定這些數據擴充參數。

augmenter = imageDataAugmenter('RandXReflection',true,... 
    'RandXTranslation',[ -  10 10],'RandYTranslation',[ -  10 10]);

imageDataAugmenter支持其餘幾種類型的數據擴充。選擇它們須要經驗分析,而且是超參數調整的另外一個層次。

開始訓練

使用如下方法組合訓練數據和數據加強選擇pixelLabelImageDatastorepixelLabelImageDatastore讀取的訓練數據的批次,施加數據加強,而且加強的數據發送給訓練算法。

pximds = pixelLabelImageDatastore(imdsTrain,pxdsTrain,... 
    'DataAugmentation',augmenter);

trainNetwork若是doTraining標誌爲真,開始訓練不然,加載預訓練網絡。

注意:培訓在具備12 GB GPU內存的NVIDIA™Titan X上進行了驗證。若是您的GPU內存較少,則可能會耗盡內存。若是發生這種狀況,請嘗試MiniBatchSize' 在使用中將'設置爲1 trainingOptions培訓這個網絡大約須要5個小時。根據您的GPU硬件,可能須要更長時間。

doTraining = false;
若是作訓練    
    [net,info] = trainNetwork(pximds,lgraph,options);
其餘
    data = load(pretrainedSegNet);
    net = data.net;
結束

在一個圖像上測試網絡

做爲快速健全檢查,在一個測試圖像上運行訓練有素的網絡。

我=讀(imdsTest);
C = semanticseg(I,net);

顯示結果。

B = labeloverlay(I,C,'Colormap',cmap,'Transparency',0.4);
imshow(B)
pixelLabelColorbar(cmap,classes);

將結果C與存儲的預期基本事實進行比較pxdsTest綠色和洋紅色區域突出顯示分割結果與預期的基本事實不一樣的區域。

expectedResult = read(pxdsTest);
actual = uint8(C);
expected = uint8(expectedResult);
imshowpair(實際,預期)

從視覺上看,語義分割結果與道路,天空和建築等類很好地重疊。然而,行人和汽車等較小的物體並不那麼準確。可使用交叉聯合(IoU)度量(也稱爲Jaccard索引)來度量每類的重疊量。使用此jaccard功能測量IoU。

iou = jaccard(C,expectedResult);
表(類,IOU)
ans = 11×2表 上課  ____________ ________
 「天空」0.92659 「建築」0.7987 「極」0.16978 「道路」0.95177 「路面」0.41877 「樹」0.43401 「SignSymbol」0.32509 「圍欄」0.492 「汽車」0.068756 「行人」0 「騎自行車的人」0

IoU指標確認了視覺結果。道路,天空和建築類具備較高的IoU分數,而諸如行人和汽車等類別的分數較低。其餘常見的分割度量包括dicebfscore輪廓匹配分數。

評估受過訓練的網絡

要測量多個測試圖像的準確度,請semanticseg在整個測試集上運行小塊大小爲4用於在分割圖像時減小內存使用。您能夠根據系統上的GPU內存量增長或減小此值。

pxdsResults = semanticseg(imdsTest,net,... 
    'MiniBatchSize',4,...... 
    'WriteLocation',tempdir,...... 
    '詳細',false);

semanticseg將測試集的結果做爲pixelLabelDatastore對象返回。每一個測試圖像的實際像素標籤數據imdsTest'WriteLocation'參數指定的位置寫入磁盤使用evaluateSemanticSegmentation來衡量測試的結果集語義分割指標。

metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTest,'Verbose',false);

evaluateSemanticSegmentation返回整個數據集,各個類以及每一個測試圖像的各類度量標準。要查看數據集級別指標,請檢查metrics.DataSetMetrics

metrics.DataSetMetrics
ans = 1×5表 GlobalAccuracy MeanAccuracy MeanIoU WeightedIoU MeanBFScore ______________ ____________ _______ ___________ ___________
 0.88204 0.85097 0.60893 0.79795 0.6098 

數據集指標提供了網絡性能的高級概述。要查看每一個類對總體性能的影響,請使用檢查每一個類的度量標準metrics.ClassMetrics

metrics.ClassMetrics
ans = 11×3表 準確度IoU MeanBFScore ________ _______ ___________
 天空0.93493 0.89243 0.88152  建築物0.79777 0.75263 0.59707  極點0.72635 0.18663 0.52252  道路0.93676 0.90672 0.71043  路面0.90674 0.72865 0.70362  樹0.86657 0.73747 0.6642  SignSymbol 0.75591 0.3452 0.434  柵欄0.82807 0.50592 0.5083  汽車0.91187 0.75001 0.64351  行人0.84866 0.35046 0.4555  自行車騎士0.84705 0.54208 0.46818 

雖然總體數據集的性能是至關高的,該類指標顯示,表明性不足類,例如PedestrianBicyclistCar不分段,以及類,如RoadSkyBuilding包含更多表明性不足的類的樣本的其餘數據可能有助於改善結果。

支持功能

function labelIDs = camvidPixelLabelIDs()
 %返回與每一個類對應的標籤ID。
%% 
CamVid數據集有32個類。
按照最初的SegNet培訓方法[1]將
它們分爲11個等級%%
11個班級是:
%「天空」「建築物」,「杆」,「道路」,「路面」,「樹」,「SignSymbol」,
%「圍欄」,「汽車」,「行人」和「騎自行車」。
%%
CamVid像素標籤ID做爲RGB顏色值提供。將它們分組爲
%11類,並將它們做爲M-by-3矩陣的單元陣列返回。
%原裝CamVid類名稱列並排RGB值。
注意%下面排除了Other / Void類。
labelIDs = { ...
    
    %「天空」
    [
    128 128 128; ...... %「天空」
    ]
    
    % 「建造」 
    [
    000 128 064; ...... %「橋」 
    128 000 000; ...... %「建築」 
    064 192 000; ... %「Wall」 
    064 000 064; ...... %「隧道」 
    192 000 128; ...... %「拱門」
    ]
    
    %「極」
    [
    192 192 128; ... %「Column_Pole」 
    000 000 064; ... %「TrafficCone」
    ]
    
    %Road
    [
    128 064 128; ...... %「道路」 
    128 000 192; ...... %「LaneMkgsDriv」 
    192 000 064; ...... %「LaneMkgsNonDriv」
    ]
    
    %「路面」
    [
    000 000 192; ... %「人行道」 
    064 192 128; ... %「ParkingBlock」 
    128 128 192; ...... %「RoadShoulder」
    ]
        
    %「樹」
    [
    128 128 000; ...... %「樹」 
    192 192 000; ...... %「VegetationMisc」
    ]
    
    %「SignSymbol」
    [
    192 128 128; ... %「SignSymbol」 
    128 128 064; ... %「Misc_Text」 
    000 064 064; ...... %「TrafficLight」
    ]
    
    %「圍欄」
    [
    064 064 128; ...... %「圍欄」
    ]
    
    %「車」
    [
    064 000 128; ... %「Car」 
    064 128 192; ... %「SUVPickupTruck」 
    192 128 192; ... %「Truck_Bus」 
    192 064 128; ... %「火車」 
    128 064 064; ... %「OtherMoving」
    ]
    
    % 「行人」
    [
    064 064 000; ...... %「行人」 
    192 128 064; ...... %「孩子」 
    064 000 192; ... %「CartLuggagePram」 
    064 128 064; ...... %「動物」
    ]
    
    %「騎自行車者」
    [
    000 128 192; ...... %「自行車手」 
    192 000 192; ...... %「MotorcycleScooter」
    ]
    
    };
結束
函數 pixelLabelColorbar(cmap,classNames)%將
顏色條 添加到當前軸。colorbar的格式爲%以顯示帶有顏色的類名。

顏色表(GCA,CMAP)

%將顏色條添加到當前圖形。
c = colorbar('peer',gca);

%使用類名做爲刻度線。
c.TickLabels = classNames;
numClasses = size(cmap,1);

%Center刻度標籤。
c.Ticks = 1 /(numClasses * 2):1 / numClasses:1;

%刪除刻度線。
c.TickLength = 0;
end 
function cmap = camvidColorMap()
 %定義CamVid數據集使用的顏色映射。

cmap = [
    128 128 128    %天空 
    128 0 0        %建築物 
    192 192 192    %極點 
    128 64 128     %道路 
    60 40 222      %路面 
    128 128 0      %樹 
    192 128 128    %SignSymbol 
    64 64 128      %圍欄 
    64 0 128       %車 
    64 64 0        %行人 
    0 128 192      %自行車手
    ]。

%在[0 1]之間歸一化。
cmap = cmap ./ 255;
結束
函數 imds = resizeCamVidImages(imds,imageFolder)
 %將圖像大小調整爲[360 480]。

若是〜存在(imageFolder,'dir'
    MKDIR(imageFolder)
其餘
    imds = imageDatastore(imageFolder);
    迴歸 ; %若是圖像已經調整
結束,則跳過

復位(IMDS) hasdata(imds)
     %閱讀圖像。
    [我,信息] =讀取(imds);     
    
    %調整圖像大小。
    我= imresize(I,[360 480]);    
    
    % 寫入磁盤。
    [〜,filename,ext] = fileparts(info.Filename);
    imwrite(I,[imageFolder filename ext])
結束

imds = imageDatastore(imageFolder);
結束
函數 pxds = resizeCamVidPixelLabels(pxds,labelFolder)
 %將像素標籤數據調整爲[360 480]。

classes = pxds.ClassNames;
labelIDs = 1:numel(classes);
若是〜存在(labelFolder,'dir'
    MKDIR(labelFolder)
其餘
    pxds = pixelLabelDatastore(labelFolder,classes,labelIDs);
    迴歸 ; %若是圖像已經調整
結束,則跳過

復位(pxds) hasdata(pxds)
     %讀取像素數據。
    [C,info] = read(pxds);
    
    %從分類轉換爲uint8。
    L = uint8(C);
    
    %調整數據大小。使用「最近」插值來
    保留標籤ID。
    L = imresize(L,[360 480],'最近');
    
    %將數據寫入磁盤。
    [〜,filename,ext] = fileparts(info.Filename);
    imwrite(L,[labelFolder filename ext])
結束

labelIDs = 1:numel(classes);
pxds = pixelLabelDatastore(labelFolder,classes,labelIDs);
結束
函數 [imdsTrain,imdsTest,pxdsTrain,pxdsTest] = partitionCamVidData(imds,pxds)
 %經過隨機選擇60%的數據進行訓練,對CamVid數據進行分區。
%其他的用於測試。
    
%設置初始隨機狀態,例如再現性。
RNG(0); 
numFiles = numel(imds.Files);
shuffledIndices = randperm(numFiles);

%使用60%的圖像進行訓練。
N =圓形(0.60 * numFiles);
trainingIdx = shuffledIndices(1:N);

%使用其他的進行測試。
testIdx = shuffledIndices(N + 1:end);

%建立用於培訓和測試的圖像數據存儲。
trainingImages = imds.Files(trainingIdx);
testImages = imds.Files(testIdx);
imdsTrain = imageDatastore(trainingImages);
imdsTest = imageDatastore(testImages);

%提取類和標籤ID信息。
classes = pxds.ClassNames;
labelIDs = 1:numel(pxds.ClassNames);

%建立用於訓練和測試的像素標籤數據存儲。
trainingLabels = pxds.Files(trainingIdx);
testLabels = pxds.Files(testIdx);
pxdsTrain = pixelLabelDatastore(trainingLabels,classes,labelIDs);
pxdsTest = pixelLabelDatastore(testLabels,classes,labelIDs);
結束

參考

[1] Badrinarayanan,V.,A。Kendall和R. Cipolla。「SegNet:用於圖像分割的深度卷積編碼器 - 解碼器架構。」 arXiv preprint arXiv:1511.00561,2015。

[2] Brostow,GJ,J。Fauqueur和R. Cipolla。「視頻中的語義對象類:高清地面實況數據庫。」 模式識別字母卷。30,Issue 2,2009,pp 88-97。

 

關注公衆號: MATLAB基於模型的設計 (ID:xaxymaker) ,天天推送MATLAB學習最多見的問題,天天進步一點點,業精於勤荒於嬉

 打開微信掃一掃哦!

相關文章
相關標籤/搜索