В этом примере показано, как обучить PointNet ++ нейронная сеть для глубокого обучения выполнять семантическую сегментацию на воздушных данных о лидаре.
Данные о лидаре, полученные от бортовых лазерных систем сканирования, используются в приложениях, таких как топографическое отображение, городское моделирование, измерение биомассы и борьба со стихийными бедствиями. Извлечение значимой информации из этих данных требует семантической сегментации, процесс, где каждая точка в облаке точек присвоена уникальная метка класса.
В этом примере вы обучаете сеть PointNet ++, чтобы выполнить семантическую сегментацию при помощи набора данных Dayton Annotated Lidar Earth Scan (DALES) [1]. Набор данных содержит сцены плотных, пометил воздушные данные о лидаре из городских, пригородных, сельских, и коммерческих настроек. Набор данных обеспечивает метки семантической сегментации для 8 классов, таких как создания, автомобили, грузовики, полюса, линии электропередачи, заборы, земля и растительность.
Набор данных DALES содержит 40 сцен воздушных данных о лидаре. Из этих 40 сцен 29 сцен используются для обучения, и остающиеся 11 сцен используются для тестирования. Каждый пиксель в данных имеет метку класса. Следуйте инструкциям на веб-сайте DALES, чтобы загрузить набор данных на папку, заданную dataFolder
переменная. Создайте папки, чтобы сохранить обучение и тестовые данные.
dataFolder = fullfile(tempdir,'DALES'); trainDataFolder = fullfile(dataFolder,'dales_las','train'); testDataFolder = fullfile(dataFolder,'dales_las','test');
Предварительно просмотрите облако точек от обучающих данных.
lasReader = lasFileReader(fullfile(trainDataFolder,'5080_54435.las')); [pc,attr] = readPointCloud(lasReader,'Attributes','Classification'); labels = attr.Classification; % Select only labeled data. pc = select(pc,labels~=0); labels = labels(labels~=0); classNames = [ "ground" "vegetation" "cars" "trucks" "powerlines" "fences" "poles" "buildings" ]; figure; ax = pcshow(pc.Location,labels); helperLabelColorbar(ax,classNames); title('Point Cloud with Overlaid Semantic Labels');
Каждое облако точек в наборе данных DALES покрывает область 500 500 метров, которая намного больше, чем типичная область, покрытая наземными облаками точек лидара вращения. Для эффективной обработки памяти разделите облако точек на маленькие, неперекрывающиеся сетки.
Используйте helperCropPointCloudsAndMergeLabels
функция, присоединенная к этому примеру как вспомогательный файл, к:
Обрежьте облака точек в неперекрывающиеся сетки размера 50 50 метры.
Downsample облако точек к фиксированному размеру.
Нормируйте облака точек, чтобы расположиться [0 1].
Сохраните обрезанные сетки и семантические метки как PCD и файлы PNG, соответственно.
Задайте размерности сетки и определите постоянный номер точек на сетку, чтобы включить более быстрое обучение.
gridSize = [50,50]; numPoints = 8192;
Если обучающие данные уже разделены на сетки, установите writeFiles
к false
. Обратите внимание на то, что обучающие данные должны быть в формате, поддержанном pcread
(Computer Vision Toolbox) функция.
writeFiles = true;
numClasses = numel(classNames);
[pcCropTrainPath,labelsCropTrainPath,weights] = helperCropPointCloudsAndMergeLabels( ...
gridSize,trainDataFolder,numPoints,writeFiles,numClasses);
Примечание: Обработка может занять время. Код приостанавливает выполнение MATLAB®, пока обработка не завершена.
Распределение точки в обучающем наборе данных через все классы получено в weights
. Нормируйте weights
использование maxWeight
.
[maxWeight,maxLabel] = max(weights); weights = sqrt(maxWeight./weights);
Создайте fileDatastore
возразите, чтобы загрузить файлы PCD с помощью pcread
(Computer Vision Toolbox) функция.
ldsTrain = fileDatastore(pcCropTrainPath,'ReadFcn',@(x) pcread(x));
Используйте pixelLabelDatastore
Объект (Computer Vision Toolbox) сохранить мудрые пикселем метки от пикселя помечает изображения. Объект сопоставляет каждую пиксельную метку с именем класса и присваивает уникальную метку ID каждому классу.
% Specify label IDs from 1 to the number of classes.
labelIDs = 1 : numClasses;
pxdsTrain = pixelLabelDatastore(labelsCropTrainPath,classNames,labelIDs);
Загрузите и отобразите облако точек.
ptcld = preview(ldsTrain);
labels = preview(pxdsTrain);
figure;
ax = pcshow(ptcld.Location,uint8(labels));
helperLabelColorbar(ax,classNames);
title('Cropped Point Cloud with Overlaid Semantic Labels');
Используйте helperConvertPointCloud
функция, заданная в конце этого примера, чтобы преобразовать облако точек в массив ячеек. Эта функция также переставляет размерности облака точек, чтобы сделать его совместимым с входным слоем сети.
ldsTransformed = transform(ldsTrain,@(x) helperConvertPointCloud(x));
Используйте combine
функционируйте, чтобы объединить облака точек и пиксельные метки в один datastore для обучения.
dsTrain = combine(ldsTransformed,pxdsTrain);
PointNet ++ [2] модель сегментации состоит из двух основных компонентов:
Установите модули абстракции
Покажите модули распространения
Серия модулей абстракции набора прогрессивно поддемонстрационные интересные места путем иерархической группировки точек и использования пользовательская архитектура PointNet, чтобы закодировать точки в характеристические векторы. Поскольку задачи семантической сегментации требуют функций точки всех исходных точек, серия модулей распространения функции используются, чтобы иерархически интерполировать функции к исходным точкам с помощью основанной на обратном расстоянии схемы интерполяции.
Задайте PointNet ++ архитектура с помощью pointnetplusLayers
(Lidar Toolbox) функция.
lgraph = pointnetplusLayers(numPoints,3,numClasses);
Обрабатывать неустойчивость класса на наборе данных DALES, взвешенной потере перекрестной энтропии от pixelClassificationLayer
(Computer Vision Toolbox) функция используется. Это оштрафует сеть больше, если точка, принадлежащая классу с более низким весом, будет неправильно классифицирована.
% Replace the FocalLoss layer with pixelClassificationLayer. larray = pixelClassificationLayer('Name','SegmentationLayer','ClassWeights', ... weights,'Classes',classNames); lgraph = replaceLayer(lgraph,'FocalLoss',larray);
Используйте Adam
алгоритм оптимизации, чтобы обучить сеть. Используйте trainingOptions
функция, чтобы задать гиперпараметры.
learningRate = 0.0005; l2Regularization = 0.01; numEpochs = 20; miniBatchSize = 6; learnRateDropFactor = 0.1; learnRateDropPeriod = 10; gradientDecayFactor = 0.9; squaredGradientDecayFactor = 0.999; options = trainingOptions('adam', ... 'InitialLearnRate',learningRate, ... 'L2Regularization',l2Regularization, ... 'MaxEpochs',numEpochs, ... 'MiniBatchSize',miniBatchSize, ... 'LearnRateSchedule','piecewise', ... 'LearnRateDropFactor',learnRateDropFactor, ... 'LearnRateDropPeriod',learnRateDropPeriod, ... 'GradientDecayFactor',gradientDecayFactor, ... 'SquaredGradientDecayFactor',squaredGradientDecayFactor, ... 'Plots','training-progress');
Примечание: уменьшайте miniBatchSize
значение к использованию управляющей памяти, когда обучение.
Можно обучить сеть сами путем установки doTraining
аргумент к true
. Если вы обучаете сеть, можно использовать центральный процессор или графический процессор. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA® графический процессор. Для получения дополнительной информации смотрите Поддержку графического процессора Релизом (Parallel Computing Toolbox). В противном случае загружает предварительно обученную сеть.
doTraining = false; if doTraining % Train the network on the dsTrain datastore using the trainNetwork function. [net, info] = trainNetwork(dsTrain,lgraph,options); else % Load the pretrained network. load('pointnetplusTrained.mat','net'); end
Сеть обучена на прореженных облаках точек. Чтобы выполнить сегментацию в облаке тестовой точки, сначала проредите облако тестовой точки, похожее на то, как прорежены обучающие данные. Выполните вывод в этом прореженном облаке тестовой точки, чтобы вычислить метки предсказания. Интерполируйте метки предсказания, чтобы получить метки предсказания на плотном облаке точек.
Задайте numNearestNeighbors
и radius
найти самые близкие точки в прореженном облаке точек для каждой точки в плотном облаке точек и выполнить интерполяцию эффективно.
numNearestNeighbors = 20; radius = 0.05;
Считайте полное облако тестовой точки.
lasReader = lasFileReader(fullfile(testDataFolder,'5080_54470.las')); [pc,attr] = readPointCloud(lasReader,'Attributes','Classification'); labelsDenseTarget = attr.Classification; % Select only labeled data. pc = select(pc,labelsDenseTarget~=0); labelsDenseTarget = labelsDenseTarget(labelsDenseTarget~=0); % Initialize prediction labels labelsDensePred = zeros(size(labelsDenseTarget));
Вычислите количество неперекрывающихся сеток на основе gridSize
'XLimits'
, и YLimits
из облака точек.
numGridsX = round(diff(pc.XLimits)/gridSize(1)); numGridsY = round(diff(pc.YLimits)/gridSize(2)); [~,edgesX,edgesY,indx,indy] = histcounts2(pc.Location(:,1),pc.Location(:,2), ... [numGridsX,numGridsY],'XBinLimits',pc.XLimits,'YBinLimits',pc.YLimits); ind = sub2ind([numGridsX,numGridsY],indx,indy);
Выполните итерации по всем неперекрывающимся сеткам и предскажите метки с помощью semanticseg
(Computer Vision Toolbox) f
смазывание.
for num=1:numGridsX*numGridsY idx = ind==num; ptCloudDense = select(pc,idx); labelsDense = labelsDenseTarget(idx); % Use the helperDownsamplePoints function, attached to this example as a % supporting file, to extract a downsampled point cloud from the % dense point cloud. ptCloudSparse = helperDownsamplePoints(ptCloudDense, ... labelsDense,numPoints); % Make the spatial extent of the dense point cloud and the sparse point % cloud same. limits = [ptCloudDense.XLimits;ptCloudDense.YLimits;ptCloudDense.ZLimits]; ptCloudSparseLocation = ptCloudSparse.Location; ptCloudSparseLocation(1:2,:) = limits(:,1:2)'; ptCloudSparse = pointCloud(ptCloudSparseLocation,'Color',ptCloudSparse.Color, ... 'Intensity',ptCloudSparse.Intensity, ... 'Normal',ptCloudSparse.Normal); % Use the helperNormalizePointCloud function, attached to this example as % a supporting file, to normalize the point cloud between 0 and 1. ptCloudSparseNormalized = helperNormalizePointCloud(ptCloudSparse); ptCloudDenseNormalized = helperNormalizePointCloud(ptCloudDense); % Use the helperConvertPointCloud function, defined at the end of this % example, to convert the point cloud to a cell array and to permute the % dimensions of the point cloud to make it compatible with the input layer % of the network. ptCloudSparseForPrediction = helperConvertPointCloud(ptCloudSparseNormalized); % Get the output predictions. labelsSparsePred = semanticseg(ptCloudSparseForPrediction{1,1}, ... net,'OutputType','uint8'); % Use the helperInterpolate function, attached to this example as a % supporting file, to calculate labels for the dense point cloud, % using the sparse point cloud and labels predicted on the sparse point cloud. interpolatedLabels = helperInterpolate(ptCloudDenseNormalized, ... ptCloudSparseNormalized,labelsSparsePred,numNearestNeighbors, ... radius,maxLabel,numClasses); labelsDensePred(idx) = interpolatedLabels; end
Starting parallel pool (parpool) using the 'local' profile ... Connected to the parallel pool (number of workers: 6).
Для лучшей визуализации выберите необходимую область из данных об облаке точек. Измените пределы в roi
переменная согласно данным об облаке точек.
roi = [edgesX(5) edgesX(8) edgesY(8) edgesY(11) pc.ZLimits]; indices = findPointsInROI(pc,roi); figure; ax = pcshow(select(pc,indices).Location, labelsDensePred(indices)); axis off; zoom(ax,1.5); helperLabelColorbar(ax,classNames); title('Point Cloud Overlaid with Detected Semantic Labels');
Оцените производительность сети на тестовых данных. Используйте evaluateSemanticSegmentation
(Computer Vision Toolbox) функция, чтобы вычислить метрики семантической сегментации из результатов набора тестов. Цель и предсказанные метки вычисляются ранее и хранятся в labelsDensePred
и labelsDenseTarget
переменные соответственно.
confusionMatrix = segmentationConfusionMatrix(labelsDensePred, ... double(labelsDenseTarget),'Classes',1:numClasses); metrics = evaluateSemanticSegmentation({confusionMatrix},classNames,'Verbose',false);
Можно измерить сумму перекрытия для класса с помощью метрики пересечения по объединению (IoU).
evaluateSemanticSegmentation
(Computer Vision Toolbox) функция возвращает метрики для целого набора данных для отдельных классов, и для каждого тестового изображения. Чтобы видеть метрики на уровне набора данных, используйте metrics.DataSetMetrics
свойство.
metrics.DataSetMetrics
ans=1×4 table
GlobalAccuracy MeanAccuracy MeanIoU WeightedIoU
______________ ____________ _______ ___________
0.93191 0.64238 0.52709 0.88198
Метрики набора данных предоставляют общий обзор производительности сети. Чтобы видеть удар, каждый класс имеет на общей производительности, смотрите метрики для каждого класса с помощью metrics.ClassMetrics
свойство.
metrics.ClassMetrics
ans=8×2 table
Accuracy IoU
________ _________
ground 0.98874 0.93499
vegetation 0.85948 0.81865
cars 0.61847 0.36659
trucks 0.018676 0.0070006
powerlines 0.7758 0.6904
fences 0.3753 0.21718
poles 0.5741 0.28528
buildings 0.92843 0.89662
Несмотря на то, что полная производительность сети хороша, метрики класса для некоторых классов как Trucks
укажите, что больше обучающих данных требуется для лучшей эффективности.
helperLabelColorbar
функция добавляет шкалу палитры в текущую ось. Шкала палитры отформатирована, чтобы отобразить имена классов с цветом.
function helperLabelColorbar(ax,classNames) % Colormap for the original classes. cmap = [[0,0,255]; [0,255,0]; [255,192,203]; [255,255,0]; [255,0,255]; [255,165,0]; [139,0,150]; [255,0,0]]; cmap = cmap./255; cmap = cmap(1:numel(classNames),:); colormap(ax,cmap); % Add colorbar to current figure. c = colorbar(ax); c.Color = 'w'; % Center tick labels and use class names for tick marks. numClasses = size(classNames, 1); c.Ticks = 1:1:numClasses; c.TickLabels = classNames; % Remove tick mark. c.TickLength = 0; end
helperConvertPointCloud
функция преобразует облако точек в массив ячеек и переставляет размерности облака точек, чтобы сделать его совместимым с входным слоем сети.
function data = helperConvertPointCloud(data) if ~iscell(data) data = {data}; end numObservations = size(data,1); for i = 1:numObservations tmp = data{i,1}.Location; data{i,1} = permute(tmp,[1,3,2]); end end
[1] Varney, Нина, Вияйян К. Азари и Квинн Грэехлинг. "ДОЛИНЫ: набор данных Large-Scale Aerial LiDAR для Семантической Сегментации". ArXiv:2004.11985 [Cs, Статистика], 14 апреля 2020. https://arxiv.org/abs/2004.11985.
[2] Ци, Чарльз Р., Ли И, Хао Су и Леонидас Дж. Гуибас. "PointNet ++: Глубоко Иерархическая Функция, Учащаяся на Наборах Точки в Метрическом пространстве". ArXiv:1706.02413 [Cs], 7 июня 2017. https://arxiv.org/abs/1706.02413.