基于深度学习的语义分割
这个例子展示了如何使用语义分割网络分割图像。
语义分割网络对图像中的每个像素进行分类,从而得到按类分割的图像。语义分割的应用包括用于自动驾驶的道路分割和用于医疗诊断的癌细胞分割。要了解更多信息,请参见开始使用深度学习进行语义分割.
这个例子首先向您展示了如何使用预训练的Deeplab v3+[1]网络分割图像,这是一种用于语义图像分割的卷积神经网络(CNN)。用于语义分割的其他类型的网络包括全卷积网络(FCN)、SegNet和U-Net。然后,您可以选择下载一个数据集,使用迁移学习来训练Deeplab v3网络。这里展示的训练过程可以应用于其他类型的语义分割网络。
为了说明训练过程,本示例使用CamVid数据集[2]来自剑桥大学。此数据集是包含在驾驶时获得的街道级别视图的图像集合。该数据集为包括汽车、行人和道路在内的32个语义类提供了像素级标签。
强烈建议使用支持cuda的NVIDIA™GPU来运行此示例。GPU的使用需要并行计算工具箱™。有关受支持的计算功能的信息,请参见金宝appGPU计算要求(并行计算工具箱).
下载预训练的语义分割网络
下载在CamVid数据集上训练的预训练版本的DeepLab 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;
列出这个网络被训练分类的类。
classes = 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);(B) pixelLabelColorbar(cmap, classes);
尽管该网络是在城市驾驶图像上进行预训练的,但它在高速公路驾驶场景上产生了合理的结果。为了改善分割结果,需要使用包含高速公路行驶场景的额外图像对网络进行再训练。本例的其余部分将向您展示如何使用迁移学习训练语义分割网络。
训练一个语义分割网络
这个例子训练了一个Deeplab v3+网络,权重初始化自预训练的Resnet-18网络。ResNet-18是一种高效的网络,非常适合处理资源有限的应用程序。其他预先训练的网络,如MobileNet v2或ResNet-50,也可以根据应用程序需求使用。详情请参见预训练的深度神经网络(深度学习工具箱).
要获得一个预先训练的Resnet-18,安装resnet18
(深度学习工具箱).安装完成后,运行以下代码验证安装是否正确。
resnet18 ();
下载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”);labelsZip = fullfile(输出文件夹,“labels.zip”);imagesZip = fullfile(输出文件夹,“images.zip”);如果~存在(labelsZip“文件”) || ~exist(imagesZip,“文件”mkdir(输出目录)“正在下载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 ______________ __________ _______________{'天空'}7.6801e+07 4.8315e+08{'建筑物'}1.1737e+08 4.8315e+08{'杆'}4.7987e+ 08 4.8453e+08{'道路'}3.4054e +08 4.8453e+08{'树'}5.4259e+07 4.4209e +08{'标志'}5.2242e+06 4.6863e+08{'围栏'}6.9211e+06 2.516e+08{'行人'}3.4029e+06 4.4444e+08{'骑车人'}2.5912e+06 2.6196e+08
按类可视化像素计数。
frequency = tbl.PixelCount/sum(tbl.PixelCount);bar(1:numel(classes),frequency) xticks(1:numel(classes)) xticklabels(tb . name) xtickangle(45) ylabel(“频率”)
理想情况下,所有类都有相同数量的观测值。然而,CamVid中的类是不平衡的,这是汽车街景数据集的一个常见问题。这样的场景比行人和骑自行车的人有更多的天空、建筑和道路像素,因为天空、建筑和道路在图像中覆盖了更多的区域。如果处理不当,这种不平衡可能不利于学习过程,因为学习倾向于优势阶级。在本例的后面部分,您将使用类加权来处理这个问题。
CamVid数据集中的图像大小为720 * 960。图像大小的选择是为了在NVIDIA™Titan X(内存为12 GB)上训练时能够容纳足够大的一批图像。如果您的GPU没有足够的内存,您可能需要将图像大小调整为较小的大小,或者减少训练批处理大小。
准备训练、验证和测试集
Deeplab v3+使用数据集中60%的图像进行训练。其余图像分别被平均分成20%和20%进行验证和测试。下面的代码将图像和像素标签数据随机分为训练集、验证集和测试集。
[imdsTrain, imdsVal, imdsTest, pxdsTrain, pxdsVal, pxdsTest] = partitionCamVidData(imds,pxds);
60/20/20分割产生以下数量的训练、验证和测试图像:
numTrainingImages = nummel (imdsTrain.Files)
numTrainingImages = 421
numValImages = nummel (imdsVal.Files)
numValImages = 140
numTestingImages = nummel (imdste . files)
numTestingImages = 140
创建网络
使用deeplabv3plusLayers
功能创建基于ResNet-18的DeepLab v3+网络。为您的应用程序选择最佳的网络需要经验分析,这是另一个级别的超参数调优。例如,您可以尝试不同的基础网络,如ResNet-50或MobileNet v2,或者您可以尝试其他语义分割网络架构,如SegNet、全卷积网络(FCN)或U-Net。
指定网络映像大小。这通常与训练图像大小相同。imageSize = [720 960 3];指定类的数量。numClasses = nummel(类);创建DeepLab v3+。lgraph = deeplabv3plusLayers(imageSize, numClasses,“resnet18”);
使用类加权来平衡类
如前所述,CamVid中的类是不平衡的。为了改进训练,您可以使用类权重来平衡类。使用前面计算的像素标签计数countEachLabel
并计算中值频率类权重。
imageFreq = tbl。PixelCount ./ tb . 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 =替换层(lgraph,“分类”, pxLayer);
选择培训选项
用于训练的优化算法是随机动量梯度下降(SGDM)。使用trainingOptions
(深度学习工具箱)来指定用于SGDM的超参数。
定义验证数据。dsVal = combine(imdsVal,pxdsVal);定义培训选项。选项= 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个epoch降低0.3个因子。这使得网络能够以更高的初始学习率快速学习,同时能够在学习率下降时找到接近局部最优的解决方案。
的方法对每个epoch的验证数据对网络进行测试“ValidationData”
参数。的“ValidationPatience”
设置为4以在验证精度收敛时提前停止训练。这可以防止网络在训练数据集上过度拟合。
使用8的迷你批处理大小来减少训练时的内存使用。您可以根据系统上的GPU内存数量增加或减少这个值。
此外,“CheckpointPath”
设置为临时位置。这个名称-值对可以在每个训练阶段结束时保存网络检查点。如果由于系统故障或停电导致培训中断,您可以从保存的检查点恢复培训。确保所指定的位置“CheckpointPath”
有足够的空间存储网络检查点。例如,节省100个Deeplab v3+检查点需要大约6 GB的磁盘空间,因为每个检查点是61 MB。
数据增加
数据增强是通过在训练过程中随机转换原始数据来提高网络的准确性。通过使用数据增强,您可以在不增加标记训练样本数量的情况下为训练数据添加更多的多样性。要对图像和像素标签数据应用相同的随机转换,请使用数据存储结合
而且变换
.首先,结合imdsTrain
而且pxdsTrain
.
dsTrain = combine(imdsTrain, pxdsTrain);
接下来,使用数据存储变换
应用在支持函数中定义的所需数据扩充金宝appaugmentImageAndLabel
.这里使用随机左/右反射和+/- 10像素的随机X/Y平移进行数据增强。
xTrans = [-10 10];yTrans = [-10 10];dsTrain = transform(dsTrain, @(data)augmentImageAndLabel(data,xTrans,yTrans));
请注意,数据扩充并不应用于测试和验证数据。理想情况下,测试和验证数据应该代表原始数据,并且不进行修改,以便进行公正的评估。
开始训练
开始使用trainNetwork
(深度学习工具箱)如果doTraining
Flag为true。否则,加载一个预先训练好的网络。
注意:训练是在NVIDIA™Titan X上验证的,GPU内存为12 GB。如果你的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);expect = uint8(expectedResult);预计imshowpair(实际)
从视觉上看,道路、天空和建筑物等类的语义分割结果重叠良好。然而,像行人和汽车这样的小物体就没有那么准确了。每个类的重叠量可以使用交集-联合(IoU)度量,也称为Jaccard指数。使用jaccard
函数来测量借据。
iou = jaccard(C,expectedResult);表(类、借据)
ans =11×2表类iou ____________ _______“天空”0.93418“建筑”0.86604“极点”0.37524“道路”0.94517“路面”0.85422“树”0.91563“标志符号”0.62075“栅栏”0.81075“汽车”0.71446“行人”0.37249“骑自行车的人”0.69775
借条指标确认了视觉结果。道路类、天空类、建筑物类的借条得分较高,而行人类、汽车类的借条得分较低。其他常见的细分指标包括骰子
和bfscore
轮廓匹配分数。
评估训练网络
若要测量多个测试图像的准确性,请运行semanticseg
在整个测试集中。在分割图像时,使用小批处理大小为4来减少内存使用。您可以根据系统上的GPU内存数量增加或减少这个值。
pxdsResults = semanticseg(imdsTest,net,...“MiniBatchSize”4...“WriteLocation”tempdir,...“详细”、假);
semanticseg
返回测试集的结果pixelLabelDatastore
对象。中每个测试图像的实际像素标签数据imdsTest
写入磁盘中“WriteLocation”
参数。使用evaluateSemanticSegmentation
对测试集结果进行语义分割度量。
metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTest, 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平均bfscore ________ _______ ___________天空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训练方法[1]。%这11个类是:%的“天空”“建筑”,“极”,“路”,“路面”、“树”、“SignSymbol”,“栅栏”、“汽车”、“行人”和“骑自行车的人”。%CamVid像素标签id作为RGB颜色值提供。把它们分成% 11类,并将它们作为m × 3矩阵的单元格数组返回。的%原始CamVid类名与每个RGB值一起列出。请注意%,其他/Void类被排除在下面。labelIDs = {...%的“天空”[128 128 128;...%的“天空”]%“建设”[000 128 064;...%“桥”12.80万;...%“建设”064 192 000;...%的“墙”064 000 064;...%的“隧道”192 000 128;...%的“拱门”]%“极”[192 192 128;...%”Column_Pole”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%的天空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 = nummel (imds.Files);shuffledIndices = randperm(numFiles);使用60%的图像进行训练。numTrain = round(0.60 * numFiles);trainingIdx = shuffledIndices(1:numTrain);使用20%的图像进行验证numVal = round(0.20 * numFiles);valIdx = shuffledIndices(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信息。classes = 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)使用随机反射和增加图像和像素标签图像%的翻译。为i = 1:size(data,1) tform = randomAffine2d(...“XReflection”,真的,...“XTranslation”xTrans,...“YTranslation”, yTrans);将视图置于输出空间中图像的中心%允许翻译将输出图像移出视图。rout = affineOutputView(size(data{i,1}), tform,“BoundsStyle”,“centerOutput”);使用相同的变换扭曲图像和像素标签。数据{i,1} = imwarp(数据{i,1}, tform,“OutputView”,溃败);数据{i,2} = imwarp(数据{i,2}, tform,“OutputView”,溃败);结束结束
参考文献
[1] Chen Liang-Chieh等,“用于语义图像分割的Atrous可分离卷积编码器-解码器。”大会(2018)。
[2]布罗斯托,G. J.福克尔,R.西波拉。“视频中的语义对象类:高清地面真相数据库。”模式识别信.第30卷,第2期,2009年,pp 88-97。
另请参阅
pixelLabelDatastore
|pixelLabelImageDatastore
|semanticseg
|labeloverlay
|countEachLabel
|segnetLayers
|pixelClassificationLayer
|trainingOptions
(深度学习工具箱)|imageDataAugmenter
(深度学习工具箱)|trainNetwork
(深度学习工具箱)|evaluateSemanticSegmentation
相关的话题
- 开始使用深度学习进行语义分割
- 标记像素进行语义分割
- MATLAB深度学习(深度学习工具箱)
- 预训练的深度神经网络(深度学习工具箱)