Воздушная Семантическая Сегментация лидара Используя PointNet ++ глубокое обучение

В этом примере показано, как обучить PointNet ++ нейронная сеть для глубокого обучения выполнять семантическую сегментацию на воздушных данных о лидаре.

Данные о лидаре, полученные от бортовых лазерных систем сканирования, используются в приложениях, таких как топографическое отображение, городское моделирование, измерение биомассы и борьба со стихийными бедствиями. Извлечение значимой информации из этих данных требует семантической сегментации, процесс, где каждая точка в облаке точек присвоена уникальная метка класса.

В этом примере вы обучаете сеть PointNet ++, чтобы выполнить семантическую сегментацию при помощи набора данных Dayton Annotated Lidar Earth Scan (DALES) [1]. Набор данных содержит сцены плотных, пометил воздушные данные о лидаре из городских, пригородных, сельских, и коммерческих настроек. Набор данных обеспечивает метки семантической сегментации для 8 классов, таких как создания, автомобили, грузовики, полюса, линии электропередачи, заборы, земля и растительность.

Загрузите данные DALES

Набор данных 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 функция.

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);

Создайте объекты Datastore для обучения

Создайте fileDatastore возразите, чтобы загрузить файлы PCD с помощью pcread функция.

ldsTrain = fileDatastore(pcCropTrainPath,'ReadFcn',@(x) pcread(x));

Используйте pixelLabelDatastore возразите, чтобы сохранить мудрые пикселем метки от пиксельных изображений метки. Объект сопоставляет каждую пиксельную метку с именем класса и присваивает уникальную метку 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 ++ модель

PointNet ++ [2] модель сегментации состоит из двух основных компонентов:

  • Установите модули абстракции

  • Покажите модули распространения

Серия модулей абстракции набора прогрессивно поддемонстрационные интересные места путем иерархической группировки точек и использования пользовательская архитектура PointNet, чтобы закодировать точки в характеристические векторы. Поскольку задачи семантической сегментации требуют функций точки всех исходных точек, серия модулей распространения функции используются, чтобы иерархически интерполировать функции к исходным точкам с помощью основанной на обратном расстоянии схемы интерполяции.

Задайте PointNet ++ архитектура с помощью pointnetplusLayers функция.

lgraph = pointnetplusLayers(numPoints,3,numClasses);

Обрабатывать неустойчивость класса на наборе данных DALES, взвешенной потере перекрестной энтропии от pixelClassificationLayer функция используется. Это оштрафует сеть больше, если точка, принадлежащая классу с более низким весом, будет неправильно классифицирована.

% 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 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 функция, чтобы вычислить метрики семантической сегментации из результатов набора тестов. Цель и предсказанные метки вычисляются ранее и хранятся в labelsDensePred и labelsDenseTarget переменные соответственно.

confusionMatrix = segmentationConfusionMatrix(labelsDensePred, ...
    double(labelsDenseTarget),'Classes',1:numClasses);
metrics = evaluateSemanticSegmentation({confusionMatrix},classNames,'Verbose',false);

Можно измерить сумму перекрытия для класса с помощью метрики пересечения по объединению (IoU).

evaluateSemanticSegmentation функция возвращает метрики для целого набора данных для отдельных классов, и для каждого тестового изображения. Чтобы видеть метрики на уровне набора данных, используйте 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.

Для просмотра документации необходимо авторизоваться на сайте