使用深度学习的语义分割
这个例子展示了如何使用语义分割网络对图像进行分割。
语义分割网络对图像中的每个像素进行分类,从而得到按类分割的图像。语义分割的应用包括用于自动驾驶的道路分割和用于医疗诊断的癌细胞分割。要了解更多信息,请参见开始使用深度学习进行语义分割.
这个例子首先向您展示了如何使用预训练的Deeplab v3+[1]网络对图像进行分割,这是一种为语义图像分割而设计的卷积神经网络(CNN)。其他类型的语义分割网络包括全卷积网络(FCN)、SegNet和U-Net。然后,您可以选择下载数据集来使用迁移学习训练Deeplab v3网络。这里展示的训练过程可以应用于其他类型的语义分割网络。
为了说明训练过程,本示例使用CamVid数据集b[2]来自剑桥大学。该数据集是一组包含驾驶时获得的街道视图的图像集合。该数据集为32个语义类提供像素级标签,包括汽车、行人和道路。
强烈建议使用支持cuda的NVIDIA™GPU来运行此示例。使用GPU需要Parallel Computing Toolbox™。有关支持的计算能力的信息,请参见金宝appGPU计算要求(并行计算工具箱).
下载预训练语义分割网络
下载在CamVid数据集上训练的deepplab v3+的预训练版本。
pretrainedURL =“https://ssd.mathworks.com/金宝appsupportfiles/vision/data/deeplabv3plusResnet18CamVid.zip”;pretrainedFolder = fullfile(tempdir,“pretrainedNetwork”);pretrainedNetworkZip = fullfile(pretrainedFolder,“deeplabv3plusResnet18CamVid.zip”);如果~存在(pretrainedNetworkZip“文件”mkdir (pretrainedFolder);disp (“正在下载预训练网络(58 MB)…”);websave (pretrainedNetworkZip pretrainedURL);结束解压缩(pretrainedNetworkZip pretrainedFolder)
加载预训练的网络。
pretrainedNetwork = fullfile(pretrainedFolder,“deeplabv3plusResnet18CamVid.mat”);data = load(pretrainedNetwork);Net = data.net;
列出这个网络被训练来分类的类。
class = string(net.Layers(end).Classes)
类=11×1的字符串“天空”“建筑”“电线杆”“道路”“人行道”“树”“标志”“栅栏”“汽车”“行人”“骑自行车的人”
进行语义图像分割
读取一幅图像,其中包含网络训练分类的类别。
I = imread()“highway.png”);
将图像大小调整为网络的输入大小。
inputSize = net.Layers(1).InputSize;I = imresize(I,inputSize(1:2));
执行语义分割semanticseg
函数和预训练网络。
C = semanticseg(I,net);
将分割结果叠加在图像的顶部labeloverlay
.设置叠加颜色地图为CamVid数据集[2]定义的颜色地图值。
cmap = camvidColorMap;B = labeloverlay(I,C,“Colormap”提出,“透明”, 0.4);图imshow(B) pixelLabelColorbar(cmap, classes);
虽然该网络是在城市驾驶图像上进行预训练的,但在高速公路驾驶场景上得到的结果是合理的。为了改善分割结果,需要使用包含高速公路驾驶场景的附加图像对网络进行再训练。这个示例的其余部分将向您展示如何使用迁移学习训练语义分割网络。
训练一个语义分割网络
这个例子训练了一个deepplab v3+网络,其权重从预训练的Resnet-18网络初始化。ResNet-18是一个高效的网络,非常适合处理资源有限的应用程序。其他预训练的网络,如MobileNet v2或ResNet-50也可以根据应用需求使用。有关详细信息,请参见预训练深度神经网络(深度学习工具箱).
要获得预训练的Resnet-18,请安装resnet18
(深度学习工具箱).安装完成后,运行以下代码来验证安装是否正确。
resnet18 ();
下载CamVid数据集
从以下网址下载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”);labelsZip = fullfile(输出文件夹,“labels.zip”);imagesZip = fullfile(输出文件夹,“images.zip”);如果~存在(labelsZip“文件”) || ~存在(imagesZip,“文件”) mkdir(outputFolder)“正在下载16mb CamVid数据集标签…”);websave (labelsZip labelURL);解压缩(labelsZip fullfile (outputFolder“标签”));disp (“正在下载557 MB CamVid数据集图像…”);websave (imagesZip imageURL);解压缩(imagesZip fullfile (outputFolder“图片”));结束
注:数据的下载时间取决于您的互联网连接。上面使用的命令阻塞MATLAB,直到下载完成。或者,您可以使用web浏览器先将数据集下载到本地磁盘。要使用从web下载的文件,请更改outputFolder
变量设置为下载文件的位置。
加载CamVid图像
使用imageDatastore
加载CamVid图像。的imageDatastore
使您能够有效地将大量映像加载到磁盘上。
imgDir = fullfile(输出文件夹,“图片”,701 _stillsraw_full);imds = imageDatastore(imgDir);
显示其中一个图像。
I = readimage(imds,559);I = histeq(I);imshow(我)
加载CamVid像素标记图像
使用pixelLabelDatastore
加载CamVid像素标签图像数据。一个pixelLabelDatastore
将像素标签数据和标签ID封装到类名映射中。
为了使培训更容易,将CamVid中的32个原始类分组为11个类。
类= [“天空”“建筑”“极”“路”“路面”“树”“SignSymbol”“篱笆”“汽车”“行人”“自行车”];
为了将32个类减少到11个,原始数据集中的多个类被分组在一起。例如,“Car”是“Car”、“SUVPickupTruck”、“Truck_Bus”、“Train”和“OtherMoving”的组合。使用支持函数返回分组标签id金宝appcamvidPixelLabelIDs
,这在本示例的末尾列出。
labelIDs = camvidPixelLabelIDs();
使用类和标签id来创建pixelLabelDatastore。
labelDir = fullfile(输出文件夹,“标签”);pxds = pixelLabelDatastore(labelDir,classes,labelIDs);
通过将其中一个像素标记的图像覆盖在图像上,读取并显示该图像。
C = readimage(pxds,559);cmap = camvidColorMap;B = labeloverlay(I,C,“ColorMap”,提出);imshow (B) pixelLabelColorbar(提出、类);
没有颜色覆盖的区域没有像素标签,并且在训练期间不使用。
分析数据集统计信息
要查看类标签在CamVid数据集中的分布,使用countEachLabel
.这个函数根据类标签计算像素的数量。
tbl = countEachLabel(pxds)
台=11×3表名称PixelCount ImagePixelCount ______________ __________ _______________ {'Sky'} 7.6801e+07 4.8315e+08 {'Building'} 1.1737e+08 4.8315e+08 {'Pole'} 4.7987e+06 4.8315e+08 {'Road'} 1.4054e+08 4.8453e+08 {'Pavement'} 3.3614e+07 4.7209e+08 {'Tree'} 5.4259e+07 4.479e+08 {'SignSymbol'} 5.2242e+06 4.6863e+08 {'Fence'} 6.9211e+06 2.516e+08 {'Car'} 3.4029e+06 4.4444e+08 {'Pedestrian'} 3.4029e+06 4.4444e+08 {'Bicyclist'} 2.5912e+06 2.6196e+08
按类可视化像素计数。
frequency = tbl.PixelCount/sum(tbl.PixelCount);bar(1:num (classes),frequency) xticks(1:num (classes)) xticklabels(tbl.Name) xtickangle(45) ylabel(“频率”)
理想情况下,所有的类都有相同数量的观察值。然而,在CamVid类是不平衡的,这是一个常见的问题,在街道场景的汽车数据集。这样的场景比行人和骑自行车的像素有更多的天空、建筑物和道路像素,因为天空、建筑物和道路在图像中覆盖的面积更大。如果处理不当,这种不平衡对学习过程是有害的,因为学习偏向于优势阶级。稍后在本例中,您将使用类权重来处理这个问题。
CamVid数据集中的图像大小为720 * 960。在具有12 GB内存的NVIDIA™Titan X上进行训练时,图像大小的选择使足够大的批量图像可以装入内存。如果您的GPU没有足够的内存或减少训练批大小,您可能需要将图像调整为更小的大小。
准备训练、验证和测试集
deepplab v3+使用数据集中60%的图像进行训练。其余图像平均分成20%和20%,分别进行验证和测试。下面的代码将图像和像素标签数据随机分成训练集、验证集和测试集。
[imdsTrain, imdsVal, imdsTest, pxdsTrain, pxdsVal, pxdsTest] = partitionCamVidData(imds,pxds);
60/20/20分割会产生以下数量的训练、验证和测试图像:
numTrainingImages = numel(imdsTrain.Files)
numTrainingImages = 421
numValImages = nummel (imdsVal.Files)
numValImages = 140
numTestingImages = numel(imdsTest.Files)
numTestingImages = 140
建立关系网
使用deeplabv3plusLayers
函数创建基于ResNet-18的DeepLab v3+网络。为您的应用程序选择最佳网络需要经验分析,这是超参数调优的另一个级别。例如,您可以尝试不同的基础网络,如ResNet-50或MobileNet v2,或者您可以尝试其他语义分割网络架构,如SegNet,全卷积网络(FCN)或U-Net。
%指定网络映像大小。这通常与训练图像的大小相同。imagessize = [720 960 3];%指定类的数量。numClasses = nummel (classes);%创建DeepLab v3+。lgraph = deeplabv3plusLayers(imageSize, numClasses,“resnet18”);
使用类权重平衡类
如前所述,在CamVid类是不平衡的。为了改进训练,您可以使用类权重来平衡类。使用前面计算的像素标签计数countEachLabel
并计算中位数频率类权重。
imageFreq = tbl。PixelCount ./ tbl.ImagePixelCount;classWeights = median(imageFreq) ./ imageFreq ./
classWeights =11×10.3182 0.2082 5.0924 0.1744 0.7103 0.4175 4.5371 1.8386 1.0000 6.6059
指定类的权重pixelClassificationLayer
.
pxLayer = pixelClassificationLayer(“名字”,“标签”,“类”资源描述。名字“ClassWeights”, classWeights);lgraph = replaceLayer(lgraph;“分类”, pxLayer);
选择培训选项
用于训练的优化算法是随机动量梯度下降算法(SGDM)。使用trainingOptions
(深度学习工具箱)指定用于SGDM的超参数。
定义验证数据。dsVal = combine(imdsVal,pxdsVal);%定义培训选项。options = trainingOptions(“个”,…“LearnRateSchedule”,“分段”,…“LearnRateDropPeriod”10…“LearnRateDropFactor”, 0.3,…“动量”, 0.9,…“InitialLearnRate”1 e - 3,…“L2Regularization”, 0.005,…“ValidationData”dsVal,…“MaxEpochs”30岁的…“MiniBatchSize”8…“洗牌”,“every-epoch”,…“CheckpointPath”tempdir,…“VerboseFrequency”2,…“阴谋”,“训练进步”,…“ValidationPatience”4);
学习率使用分段调度。学习率每10次降低0.3倍。这使得网络能够以较高的初始学习率快速学习,同时能够在学习率下降时找到接近局部最优的解。
每个历元对验证数据进行测试“ValidationData”
参数。的“ValidationPatience”
设置为4表示在验证精度收敛时提前停止训练。这可以防止网络对训练数据集的过拟合。
使用8的小批大小来减少训练时的内存使用。您可以根据系统上的GPU内存数量增加或减少此值。
此外,“CheckpointPath”
设置为临时位置。这个名称-值对允许在每个训练时段结束时保存网络检查点。如果训练因系统故障或停电而中断,您可以从保存的检查点恢复训练。确保指定的位置“CheckpointPath”
有足够的空间来存储网络检查点。例如,保存100个Deeplab v3+检查点需要约6 GB的磁盘空间,因为每个检查点是61 MB。
数据增加
数据增强是通过在训练过程中对原始数据进行随机变换来提高网络的准确率。通过使用数据增强,您可以在不增加标记训练样本数量的情况下为训练数据添加更多种类。要对图像和像素标签数据应用相同的随机转换,请使用datastore结合
和变换
.首先,结合imdsTrain
和pxdsTrain
.
dsTrain = combine(imdsTrain, pxdsTrain);
接下来,使用数据存储变换
应用在支持函数中定义的所需数据增广金宝appaugmentImageAndLabel
.这里,随机左/右反射和随机X/Y平移+/- 10像素用于数据增强。
xTrans = [-10 10];yTrans = [-10 10];dsTrain = transform(dsTrain, @(data)augmentImageAndLabel(data,xTrans,yTrans));
注意,数据扩展不应用于测试和验证数据。理想情况下,测试和验证数据应该代表原始数据,并且不被修改以进行无偏评价。
开始训练
开始训练trainNetwork
(深度学习工具箱)如果doTraining
Flag是真的。否则,加载一个预训练的网络。
注意:训练是在NVIDIA™Titan X上进行验证的,GPU内存为12gb。如果您的GPU内存较少,则可能会在训练期间耗尽内存。如果发生这种情况,请尝试设置“MiniBatchSize”
到1英寸trainingOptions
,或者减少网络输入并调整训练数据的大小。训练这个网络大约需要70分钟。这取决于你的GPU硬件,可能需要更长的时间。
doTraining = false;如果doTraining [net, info] = trainNetwork(dsTrain,lgraph,options);结束
在一个图像上测试网络
作为一个快速的完整性检查,在一个测试映像上运行训练好的网络。
I = readimage(imdsTest,35);C = semanticseg(I, net);
显示结果。
B = labeloverlay(I,C,“Colormap”提出,“透明”, 0.4);imshow(B) pixelLabelColorbar(cmap, classes);
将结果与C
将预期的真相储存在pxdsTest
.绿色和品红区域突出了分割结果与预期的基础真值不同的区域。
expectedResult = readimage(pxdsTest,35);= uint8(C);expected = uint8(expectedResult);预计imshowpair(实际)
从视觉上看,对于道路、天空和建筑等类,语义分割结果重叠得很好。然而,像行人和汽车这样的小物体就不那么精确了。每个类的重叠量可以使用交叉-超并(IoU)度量来度量,也称为Jaccard索引。使用jaccard
度量借据的函数。
iou = jaccard(C, expecteresult);表(类、借据)
ans =11×2表类别____________ _______“天空”0.93418“建筑”0.86604“电线杆”0.37524“道路”0.94517“人行道”0.85422“树”0.91563“标志”0.62075“栅栏”0.81075“汽车”0.71446“行人”0.37249“骑自行车的人”0.69775
IoU指标确认了视觉结果。道路、天空和建筑类别的IoU得分较高,而行人和汽车等类别的IoU得分较低。其他常见的分割指标包括骰子
和bfscore
轮廓匹配分数。
评估训练过的网络
要测量多个测试图像的准确性,请运行semanticseg
在整个测试集上。在分割图像时,使用小批大小为4来减少内存使用。您可以根据系统上的GPU内存数量增加或减少此值。
pxdsResults = semanticseg(imdsTest,net)…“MiniBatchSize”4…“WriteLocation”tempdir,…“详细”、假);
semanticseg
返回测试集的结果pixelLabelDatastore
对象。中每个测试图像的实际像素标签数据imdsTest
的指定位置写入磁盘“WriteLocation”
参数。使用evaluateSemanticSegmentation
对测试集结果进行语义分割度量。
metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTest,“详细”、假);
evaluateSemanticSegmentation
返回整个数据集、单个类和每个测试图像的各种指标。要查看数据集级别指标,请检查指标。DataSetMetrics
.
指标。DataSetMetrics
ans =表1×5GlobalAccuracy MeanAccuracy MeanIoU WeightedIoU MeanBFScore ______________ ____________ _______ ___________ ___________ 0.89244 0.8657 0.66347 0.82837 0.69324
数据集指标提供了网络性能的高级概述。要查看每个类对总体性能的影响,请使用指标。ClassMetrics
.
指标。ClassMetrics
ans =11×3表精确度IoU MeanBFScore ________ _______ ___________天空0.94272 0.90979 0.90853建筑0.81488 0.79161 0.63963杆子0.75997 0.24632 0.58505道路0.93955 0.92638 0.80615人行道0.90048 0.73874 0.74538树0.88173 0.7746 0.72892标志0.76491 0.42338 0.53707围栏0.83661 0.57442 0.5567汽车0.92588 0.79441 0.74331行人0.86718 0.47077 0.64356骑自行车者0.88881 0.6478 0.59473
尽管整体数据集性能相当高,但类指标显示,未被充分代表的类,例如行人
,骑自行车
,车
是不是也有分段类如路
,天空
,建筑
.包含更多代表性不足的类别样本的额外数据可能有助于改进结果。
金宝app支持功能
函数labelIDs = camvidPixelLabelIDs()%返回每个类对应的标签id。%CamVid数据集有32个类。将它们分为以下11类%原始SegNet训练方法b[1]。%这11类是:%的“天空”“建筑”,“极”,“路”,“路面”、“树”、“SignSymbol”,“篱笆”、“汽车”、“行人”和“骑自行车的人”。%% CamVid像素标签id以RGB颜色值提供。将它们分为% 11类,并将它们作为m × 3矩阵的单元格数组返回。的原始CamVid类名与每个RGB值一起列出。请注意% Other/Void类被排除在下面。labelIDs = {…%的“天空”[128 128 128;…%的“天空”]%“建设”[000 128 064;…%“桥”128 000 000;…%“建设”064 192 000;…%的“墙”064 000 064;…%的“隧道”192 000 128;…%的“拱门”]%“极”[192 192 128;…%”Column_Pole”000 000 064;…%”TrafficCone”]%的道路[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;…%的“汽车”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(提出)在当前轴上添加一个颜色条。颜色栏已格式化%显示带有颜色的类名。甘氨胆酸colormap(提出)在当前图形中添加颜色条。C = colorbar()“对等”甘氨胆酸,);使用类名作为勾号。c.TickLabels = classNames;numClasses = size(cmap,1);%勾号标签居中。c.Ticks = 1/(numClasses*2):1/numClasses:1删除勾号。c.TickLength = 0;结束
函数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% SignSymbol64 64 128%的栅栏64 0 128%的车64 64 0%行人0 128 192%骑自行车];%在[0 1]之间归一化。Cmap = Cmap ./ 255;结束
函数[imdsTrain, imdsVal, imdsTest, pxdsTrain, pxdsVal, pxdsTest] = partitionCamVidData(imds,pxds)%分割CamVid数据,随机选择60%的数据进行训练。的% rest用于测试。%设置初始随机状态,例如再现性。rng (0);numFiles = numel(imds.Files);shuffledIndices = randperm(numFiles);%使用60%的图像进行训练。numTrain = round(0.60 * numFiles);trainingIdx = shuffledindiices (1:numTrain);%使用20%的图像进行验证numVal = round(0.20 * numFiles);valIdx = shuffledindiices (numTrain+1:numTrain+numVal);%剩下的用于测试。testdx = shuffledIndices(numTrain+numVal+1:end);创建用于训练和测试的图像数据存储。trainingImages = imds.Files(trainingIdx);valImages = imds.Files(valIdx);testImages = imds.Files(testdx);imdsTrain = imageDatastore(trainingImages);imdsVal = imageDatastore(valImages);imdsTest = imageDatastore(testImages);%提取类和标签id信息。class = pxds.ClassNames;labelIDs = camvidPixelLabelIDs();%创建用于训练和测试的像素标签数据存储。trainingLabels = pxds.Files(trainingIdx);valLabels = pxds.Files(valIdx);testLabels = pxds.Files(testdx);pxdsTrain = pixelLabelDatastore(trainingLabels, classes, labelIDs);pxdsVal = pixelLabelDatastore(valLabels, classes, labelIDs);pxdsTest = pixelLabelDatastore(testLabels, classes, labelIDs);结束
函数data = augmentImageAndLabel(data, xTrans, yTrans)使用随机反射和增强图像和像素标签图像%的翻译。为1:size(data,1) tform = randomAffine2d(…“XReflection”,真的,…“XTranslation”xTrans,…“YTranslation”, yTrans);在输出空间中将视图居中于图像的中心%允许翻译将输出图像移出视图。route = affineOutputView(size(data{i,1}), format,“BoundsStyle”,“centerOutput”);使用相同的变换来扭曲图像和像素标签。数据{i,1} = imwarp(数据{i,1}, tform,“OutputView”,溃败);数据{i,2} = imwarp(数据{i,2}, tform,“OutputView”,溃败);结束结束
参考文献
[10]陈良杰等,“基于可分离卷积的语义图像分割编码器”。大会(2018)。
[b]布罗斯托,G. J.福克尔,R.西波拉。视频中的语义对象类:一个高清地面真相数据库。模式识别字母.第30卷,第2期,2009年,第88-97页。
另请参阅
pixelLabelDatastore
|pixelLabelImageDatastore
|semanticseg
|labeloverlay
|countEachLabel
|segnetLayers
|pixelClassificationLayer
|trainingOptions
(深度学习工具箱)|imageDataAugmenter
(深度学习工具箱)|trainNetwork
(深度学习工具箱)|evaluateSemanticSegmentation
相关的话题
- 开始使用深度学习进行语义分割
- 标记像素用于语义分割
- MATLAB中的深度学习(深度学习工具箱)
- 预训练深度神经网络(深度学习工具箱)