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

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

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

В этом примере вы обучаете сеть PointNet ++, чтобы выполнить семантическую сегментацию при помощи набора данных Дейтона аннотировал наземный скан лидара (DALES) [1], который содержит сцены плотных, помеченных данных об антенне лидара из городских, пригородных, сельских, и коммерческих настроек. Из 40 сцен 29 сцен используются для обучения, и остающиеся 11 последовательностей используются для тестирования. Набор данных обеспечивает метки семантической сегментации для 8 таких как создания, автомобили, грузовики, полюса, линии электропередачи, заборы, земля и растительность.

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

Набор данных DALES содержит 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 + 1;
classNames = [
    "background"
    "ground"
    "vegetation"
    "cars"
    "trucks"
    "powerlines"
    "poles"
    "fences"
    "buildings"
    ];
cmap = labelColorMap();
figure;
colormap(cmap);
ax = pcshow(pc.Location,labels);
labelColorbar(ax,cmap,classNames);
title("PointCloud with overlaid semantic labels");

Предварительная Обработка Данных

Каждое облако точек в наборе данных DALES покрывает область 500 500 метров, которая намного больше, чем типичная область, покрытая наземными облаками точек лидара вращения. Для эффективной обработки памяти разделите облако точек на маленькие, неперекрывающиеся сетки. Кроме того, фокусируйтесь на трех доминирующих классах — земле, созданиях, и растительности — чтобы избежать проблем из-за неустойчивости класса.

Используйте helperCropPointCloudsAndMergeLabels функция, присоединенная к этому примеру как вспомогательный файл, к:

  • Обрежьте облака точек в неперекрывающиеся сетки размера 50 50 метры.

  • Объедините все классы кроме доминирующих классов (земля, создания и растительность) в один фоновый класс так, чтобы данные содержали только четыре класса.

  • Сохраните обрезанные сетки и семантические метки как PCD и файлы PNG, соответственно.

Задайте размерности сетки и определите постоянный номер точек на сетку, чтобы включить более быстрое обучение.

gridSize = [50,50];
numPoints = 115000;

Если вы используете этот рабочий процесс для своих собственных данных, устанавливаете writeFiles ко "лжи", если обучающие данные разделен на сетки. Обратите внимание на то, что обучающие данные должны быть в формате, поддержанном pcread функция.

writeFiles = true;
[pcCropTrainPath,labelsCropTrainPath] = helperCropPointCloudsAndMergeLabels(gridSize,trainDataFolder,numPoints,writeFiles);
[pcCropTestPath,labelsCropTestPath] = helperCropPointCloudsAndMergeLabels(gridSize,testDataFolder,numPoints,writeFiles);

Обработка может занять время. Код приостанавливает выполнение MATLAB®, пока обработка не завершена.

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

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

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

Используйте pixelLabelDatastore возразите, чтобы сохранить мудрые пикселем метки от пиксельных изображений метки. Объект сопоставляет каждую пиксельную метку с именем класса. В этом примере земля, растительность и создания являются единственными классами интереса; все другие пиксели являются фоном. Задайте эти классы и присвойте уникальную метку ID каждому классу.

classNames = classNames([1,2,3,9],:)
classNames = 4×1 string
    "background"
    "ground"
    "vegetation"
    "buildings"

numClasses = numel(classNames);

% Specify label IDs from 1 to the number of classes.
labelIDs = 1 : numClasses;
pxdsTrain = pixelLabelDatastore(labelsCropTrainPath,classNames,labelIDs);

Загрузите и отобразите облака точек.

ptcld = preview(ldsTrain);
labels = preview(pxdsTrain);
cmap = cmap([1,2,3,9],:);
figure;
ax1 = pcshow(ptcld.Location,uint8(labels));
labelColorbar(ax1,cmap,classNames);
title("Cropped point cloud with overlaid semantic labels");

Используйте normalizePointCloud функция, заданная в конце примера, чтобы нормировать облако точек между 0 и 1.

ldsTransformed = transform(ldsTrain,@(x) normalizePointCloud(x));

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

pxdsTransformed = transform(pxdsTrain,@(x) onehotEncodeLabels(x,classNames));

Используйте combine функционируйте, чтобы объединить облака точек и пиксельные метки в один datastore для обучения.

dsTrain = combine(ldsTransformed,pxdsTransformed);

Задайте PointNet ++ модель

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

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

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

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

Модуль абстракции набора реализован с помощью sampleAndGroup и sharedMLP поддерживание функций и модуля распространения функции реализовано с помощью featurePropagation и sharedMLP вспомогательные Функции. sharedMLP модуль является единственным learnable модулем, который состоит из свертки и слоев нормализации экземпляра. Используйте функцию поддержки pointNetplusModel задавать целый PointNet ++ модель сегментации. Функции поддержки перечислены в конце этого примера.

Задайте PointNet ++ параметры модели

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

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

Установите параметры первого модуля абстракции набора. Определите номер кластеров к 1 024, количество выборок в каждом кластере к 128, и радиус каждого кластера к 0,1. Также установите входной размер канала модели PointNet на три и скрытые размеры канала к 32, 32, и 64.

inputChannelSize = 3;
hiddenChannelSize = [32,32,64];
nontrainable.nClusters1 = 1024;
nontrainable.nSamples1 = 128;
nontrainable.radius1 = 0.1;
trainable.SharedMLP1 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);  

Установите параметры второго модуля абстракции набора. Определите номер кластеров к 256, количество выборок в каждом кластере к 64, и радиус каждого кластера к 0,2. Также установите входной размер канала модели PointNet на 67 и скрытые размеры канала к 64, 64, и 128.

inputChannelSize = 67;
hiddenChannelSize = [64,64,128];
nontrainable.nClusters2 = 256;
nontrainable.nSamples2 = 64;
nontrainable.radius2 = 0.2;
trainable.SharedMLP2 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);  

Установите параметры третьего модуля абстракции набора. Определите номер кластеров к 64, количество выборок в каждом кластере к 32, и радиус каждого кластера к 0,4. Также установите входной размер канала модели PointNet на 131 и скрытые размеры канала к 128, 128, и 256.

inputChannelSize = 131;
hiddenChannelSize = [128,128,256];
nontrainable.nClusters3 = 64;
nontrainable.nSamples3 = 32;
nontrainable.radius3 = 0.4;
trainable.SharedMLP3 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);  

Установите параметры четвертого модуля абстракции набора. Определите номер кластеров к 16, количество выборок в каждом кластере к 32, и радиус каждого кластера к 0,8. Также установите входной размер канала модели PointNet на 259 и скрытые размеры канала к 256, 256, и 512.

inputChannelSize = 259;
hiddenChannelSize = [256,256,512];
nontrainable.nClusters4 = 16;
nontrainable.nSamples4 = 32;
nontrainable.radius4 = 0.8;
trainable.SharedMLP4 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);  

Установите параметры первого модуля распространения функции. Установите пятый разделяемый входной размер канала модели MLP на 768 и скрытый размер канала к 256 и 256.

inputChannelSize = 768;
hiddenChannelSize = [256,256];
trainable.SharedMLP5 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);

Установите параметры второго модуля распространения функции. Установите разделяемый входной размер канала модели MLP на 384 и скрытый размер канала к 256 и 256.

inputChannelSize = 384;
hiddenChannelSize = [256,256];
trainable.SharedMLP6 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);  

Установите параметры третьего модуля распространения функции. Установите разделяемый входной размер канала модели MLP на 320 и скрытый размер канала к 256 и 128.

inputChannelSize = 320;
hiddenChannelSize = [256,128];
trainable.SharedMLP7 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);  

Установите параметры четвертого модуля распространения функции. Установите разделяемый входной размер канала модели MLP на 128 и скрытый размер канала к 128 и 128.

inputChannelSize = 128;
hiddenChannelSize = [128,128];
trainable.SharedMLP8 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);

Установите совместно использованный входной размер канала модели MLP финала на 128 и скрытый размер канала к 128 и numClasses.

inputChannelSize = 128;
hiddenChannelSize = [128,numClasses];
trainable.SharedMLP9 = initializeSharedMLP(inputChannelSize,hiddenChannelSize);
params.trainable = trainable;
params.nontrainable = nontrainable;

Задайте опции обучения

Обучайтесь в течение одной эпохи. Установите начальную скорость обучения на 2e-4 и фактор регуляризации L2 к 0,01.

numEpochs = 1;
miniBatchSize = 1;
learningRate = 2e-4;
l2Regularization = 0.01;

Инициализируйте опции для оптимизации Адама.

gradientDecayFactor = 0.9;
squaredGradientDecayFactor = 0.999;

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

trailingAvg = [];
trailingAvgSq = [];

Обучите модель

Обучите модель с помощью центрального процессора или графического процессора. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA® графический процессор. Для получения дополнительной информации смотрите Поддержку графического процессора Релизом (Parallel Computing Toolbox). Чтобы автоматически обнаружить, если вы имеете графический процессор в наличии, установите executionEnvironment к 'auto'. Если вы не имеете графического процессора или не хотите использовать один для обучения, устанавливать executionEnvironment к 'cpu'. Чтобы гарантировать использование графического процессора для обучения, установите executionEnvironment к 'gpu'.

Затем создайте minibatchqueue Объект (Deep Learning Toolbox) загрузить данные в пакетах miniBatchSize во время обучения.

executionEnvironment = "auto";
if canUseParallelPool
    dispatchInBackground = true;
else
    dispatchInBackground = false;
end

mbq = minibatchqueue(dsTrain,2,...
                     "MiniBatchSize",miniBatchSize,...
                     "OutputEnvironment",executionEnvironment,...
                     "MiniBatchFormat",["SSCB","SSCB"],...
                     "DispatchInBackground",dispatchInBackground);

Обучите модель с помощью пользовательского учебного цикла. Для каждой итерации:

  • Считайте пакет данных.

  • Оцените градиенты модели.

  • Примените регуляризацию веса L2.

  • Используйте adamupdate обновить параметры модели.

  • Обновите плоттер процесса обучения с помощью функции помощника configureTrainingProgressPlotter, заданный в конце этого примера.

doTraining = false;
if doTraining
 
    % Initialize plot.
    fig = figure;
    lossPlotter = configureTrainingProgressPlotter(fig);    
    iteration = 0;
    
    % Custom training loop.
    for epoch = 1:numEpochs
         
        % Reset datastore.
        reset(mbq);
        
        while(hasdata(mbq))
            iteration = iteration + 1;
            
            % Read batch of data.
            [ptCld, ptLabels] = next(mbq);
                        
            % Evaluate the model gradients and loss using dlfeval and the modelGradients function.
            [gradients, loss] = dlfeval(@modelGradients, ptCld, ptLabels, params);
                                        
            % L2 regularization.
            gradients = dlupdate(@(g,p) g + l2Regularization*p,gradients,params.trainable);
            
            % Update the network parameters using the Adam optimizer.
            [params.trainable, trailingAvg, trailingAvgSq] = adamupdate(params.trainable, gradients, ...
                                                       trailingAvg, trailingAvgSq, iteration,...
                                                       learningRate,gradientDecayFactor, squaredGradientDecayFactor);
            
            % Update training plot with new points.         
            addpoints(lossPlotter, iteration,double(gather(extractdata(loss))));
            title("Training Epoch " + epoch +" of " + numEpochs);
            drawnow;
        end      
    end
else
    % Download pretrained model parameters.
    pretrainedNetURL = 'https://www.mathworks.com/supportfiles/lidar/data/trainedPointNetplusplusNet.zip';
    
    preTrainedZipFile = fullfile(dataFolder,'trainedPointNetplusplusNet.zip');
    preTrainedMATFile = fullfile(dataFolder,'trainedPointNetplusplusNet.mat');
     if ~exist(preTrainedMATFile,'file')
            if ~exist(preTrainedZipFile,'file')
                disp('Downloading pretrained detector (3.2 MB)...');
                websave(preTrainedZipFile, pretrainedNetURL);
            end
            unzip(preTrainedZipFile,dataFolder);   
     end
    
    % Load pretrained model.
    load(preTrainedMATFile,'params');
    
    % Move model parameters to the GPU if possible and convert to a dlarray.
    params.trainable = prepareForPrediction(params.trainable,@(x)dlarray(toDevice(x,canUseGPU)));   
end

Облако точек антенны сегмента

Считайте полное облако тестовой точки.

lasReader = lasFileReader(fullfile(testDataFolder,'5175_54395.las'));
[pc,attr] = readPointCloud(lasReader,"Attributes", "Classification");

Вычислите количество неперекрывающейся сетки на основе gridSize'XLimits' , и YLimits из облака точек.

numGridsX = round(diff(pc.XLimits)/gridSize(1));
numGridsY = round(diff(pc.YLimits)/gridSize(2));
xRange = linspace(pc.XLimits(1),pc.XLimits(2),numGridsX+1);
yRange = linspace(pc.YLimits(1),pc.YLimits(2),numGridsY+1);

Выполните итерации по всем неперекрывающимся сеткам и предскажите метки.

pcFinal = [];
for r = 1:numGridsX
    for c = 1:numGridsY
        idx = (pc.Location(:,1) >= xRange(r)) & (pc.Location(:,1) < xRange(r+1))...
             & (pc.Location(:,2) >= yRange(c)) & (pc.Location(:,2) < yRange(c+1));
        ptCloudGrid = select(pc,idx);
        
        % Select fixed number of points and labels from the given point cloud.
        ptCloudGrid = selectPoints(ptCloudGrid,numPoints);
        
        % Apply the preprocessing.
        ptCloud = normalizePointCloud(ptCloudGrid);
        loc = ptCloud{1,1};
        X1 = dlarray(loc,'SSCB');
        if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
            X1 = gpuArray(X1);
        end
        
        % Get the output predictions.
        op = pointNetplusModel(X1,params.trainable,params.nontrainable);
        [~,opLabels] = max(gather(extractdata(op)),[],3);
        color = cmap(opLabels,:);
        ptCloudGrid.Color = uint8(color); 
        
        pcFinal = [pcFinal;ptCloudGrid];
    end
end

ptCloudOut = pccat(pcFinal);
color = 255.*ptCloudOut.Color;
figure;
pcshow(ptCloudOut.Location,color);
title('PointCloud overlaid with detected semantic labels');

Оцените сеть

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

confusionMatrix = {};

% Define the number of samples to evaluate the network. Set numSamplesToTest to
% 1100 to evaluate the model on the entire test data set.
numSamplesToTest = 50;

for i = 1:numSamplesToTest
    ptCldPath = fullfile(pcCropTestPath,sprintf("%03d.pcd",i));
    ptCloud = pcread(ptCldPath);
    ptCloud = normalizePointCloud(ptCloud);
    loc = ptCloud{1,1};
    
    X1 = dlarray(loc,'SSCB');
    if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
        X1 = gpuArray(X1);
    end
    
    % Get the output predictions.
    op = pointNetplusModel(X1,params.trainable,params.nontrainable);
    [~,predictionLabels] = max(gather(extractdata(op)),[],3);
  
    targetLabelPath = fullfile(labelsCropTestPath,sprintf("%03d.png",i));
    targetLabels = imread(targetLabelPath);
    targetLabels = squeeze(targetLabels);
    
    % Calculate the confusion matrix.
    confMatrix = segmentationConfusionMatrix(double(predictionLabels),...
                 double(targetLabels),"Classes",1:numClasses);
    if size(confMatrix,1) ~= numel(classNames)
        continue
    end
    confusionMatrix{i} = confMatrix;
end

confusionMatrix = confusionMatrix';
metrics = evaluateSemanticSegmentation(confusionMatrix,classNames)
Evaluating semantic segmentation results
----------------------------------------
* Selected metrics: global accuracy, class accuracy, IoU, weighted IoU.
* Processed 50 images.
* Finalizing... Done.
* Data set metrics:

    GlobalAccuracy    MeanAccuracy    MeanIoU    WeightedIoU
    ______________    ____________    _______    ___________

       0.76168          0.53687       0.42486      0.60851  
metrics = 
  semanticSegmentationMetrics with properties:

              ConfusionMatrix: [4×4 table]
    NormalizedConfusionMatrix: [4×4 table]
               DataSetMetrics: [1×4 table]
                 ClassMetrics: [4×2 table]
                 ImageMetrics: [50×4 table]

Градиенты модели

modelGradients функционируйте берет в качестве входа мини-пакет данных X, соответствующий целевой yTarget, и настраиваемые параметры, и возвращают градиенты потери относительно настраиваемых параметров и соответствующей потери. Чтобы вычислить градиенты, оцените modelGradients функция с помощью dlfeval функционирует в учебном цикле.

function [gradients, loss] = modelGradients(X,yTarget,params)

    % Execute the model function.
    yPred = pointNetplusModel(X,params.trainable,params.nontrainable);
    
    % Compute the loss.
    loss = focalCrossEntropy(yPred,yTarget,'TargetCategories','independent');
    
    % Compute the parameter gradients with respect to the loss. 
    gradients = dlgradient(loss, params.trainable);  
end

PointNet ++ функция модели

Функция модели берет в качестве входа параметры модели parameters and входные данные dlX. Сетевые выходные параметры предсказания для меток.

function dlY = pointNetplusModel(dlX,trainable,nontrainable)

    % Set abstraction module 1
   [points1, pointFeatures1] = sampleAndGroup(dlX,[],nontrainable.nClusters1,...
                                nontrainable.nSamples1,nontrainable.radius1);
    dlY = sharedMLP(pointFeatures1,trainable.SharedMLP1.Perceptron);
    pointNetFeatures1 = max(dlY,[],2);
    
    % Set abstraction module 2
    [points2, pointFeatures2] = sampleAndGroup(points1,pointNetFeatures1,nontrainable.nClusters2,...
                                nontrainable.nSamples2,nontrainable.radius2);
    dlY = sharedMLP(pointFeatures2,trainable.SharedMLP2.Perceptron);
    pointNetFeatures2 = max(dlY,[],2);
    
    % Set abstraction module 3
    [points3, pointFeatures3] = sampleAndGroup(points2,pointNetFeatures2,nontrainable.nClusters3,...
                                nontrainable.nSamples3,nontrainable.radius3);  
    dlY = sharedMLP(pointFeatures3,trainable.SharedMLP3.Perceptron);
    pointNetFeatures3 = max(dlY,[],2);
    
    % Set abstraction module 4
   [points4, pointFeatures4] = sampleAndGroup(points3,pointNetFeatures3,nontrainable.nClusters4,...
                                nontrainable.nSamples4,nontrainable.radius4); 
    dlY = sharedMLP(pointFeatures4,trainable.SharedMLP4.Perceptron);
    pointNetFeatures4 = max(dlY,[],2);
    
    % Feature propagation module 1
    pointsFP1 = featurePropagation(points3,points4,pointNetFeatures3,pointNetFeatures4);
    pointNetFP1 = sharedMLP(pointsFP1,trainable.SharedMLP5.Perceptron);
    
    % Feature propagation module 2
    pointsFP2 = featurePropagation(points2,points3,pointNetFeatures2,pointNetFP1);
    pointNetFP2 = sharedMLP(pointsFP2,trainable.SharedMLP6.Perceptron);
   
    % Feature propagation module 3
    pointsFP3 = featurePropagation(points1,points2,pointNetFeatures1,pointNetFP2);
    pointNetFP3 = sharedMLP(pointsFP3,trainable.SharedMLP7.Perceptron);
    
    % Feature propagation module 4
    pointsFP4 = featurePropagation(dlX,points1,[],pointNetFP3);
    dlY = sharedMLP(pointsFP4,trainable.SharedMLP8.Perceptron);
   
    % Shared MLP
    dlY = sharedMLP(dlY,trainable.SharedMLP9.Perceptron);
    dlY = softmax(dlY);
    dlY = dlarray(dlY,'SSCB');
end

Функции инициализации параметра модели

Инициализируйте разделяемую многоуровневую функцию Perceptron

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

function params = initializeSharedMLP(inputChannelSize,hiddenChannelSize)
weights = initializeWeightsHe([1 1 inputChannelSize hiddenChannelSize(1)]);
bias = zeros(hiddenChannelSize(1),1,"single");
p.Conv.Weights = dlarray(weights);
p.Conv.Bias = dlarray(bias);

p.InstanceNorm.Offset = dlarray(zeros(hiddenChannelSize(1),1,"single"));
p.InstanceNorm.Scale = dlarray(ones(hiddenChannelSize(1),1,"single"));

params.Perceptron(1) = p;

for k = 2:numel(hiddenChannelSize)
    weights = initializeWeightsHe([1 1 hiddenChannelSize(k-1) hiddenChannelSize(k)]);
    bias = zeros(hiddenChannelSize(k),1,"single");
    p.Conv.Weights = dlarray(weights);
    p.Conv.Bias = dlarray(bias);
    
    p.InstanceNorm.Offset = dlarray(zeros(hiddenChannelSize(k),1,"single"));
    p.InstanceNorm.Scale = dlarray(ones(hiddenChannelSize(k),1,"single"));
   
    params.Perceptron(k) = p;
end
end

function x = initializeWeightsHe(sz)
fanIn = prod(sz(1:3));
stddev = sqrt(2/fanIn);
x = stddev .* randn(sz);
end

function x = initializeWeightsGaussian(sz)
x = randn(sz,"single") .* 0.01;
end

Выборка и группировка

function [newClusters,newpointFeatures] = sampleAndGroup(points,pointFeatures,nClusters,nSamples,radius)
% The sampleAndGroup layer first samples the point cloud to a given number of
% clusters and then constructs local region sets by finding neighboring
% points around the centroids using the queryBallPoint function.

    points = extractdata(squeeze(points));
    if ~isempty(pointFeatures)
        pointFeatures = extractdata(squeeze(pointFeatures));
    end
        
    % Find the centroids for nClusters - nClusters*3.
    centroids = farthestPointSampling(points,nClusters);
    newClusters = points(centroids,:);
    
    % Find the neareset nSamples for nClusters - nClusters*nSamples*3.
    idx = queryBallPoint(points,newClusters,nClusters,nSamples,radius);
    newPoints = reshape(points(idx,:),[nClusters,nSamples,3]);
    
    % Normalize the points in relation to the cluster center.
    newpointFeatures = newPoints - reshape(newClusters,nClusters,1,3);
    
    if ~isempty(pointFeatures)
        groupFeatures = reshape(pointFeatures(idx,:),...
                       [nClusters,nSamples,size(pointFeatures,2)]);
        newpointFeatures = cat(3,newPoints,groupFeatures);
    end
    
    newpointFeatures = dlarray(newpointFeatures,'SSC');
    newClusters = dlarray(newClusters,'SSC');
end

Дальше всего укажите выборку

function centroids = farthestPointSampling(pointLocations,numPoints)
% The farthestPointSampling function selects a set of points from input
% points, which defines the centroids of local regions.
% pointLocations - PointCloud locations N-by-3.
% numPoints - Number of clusters to find.
% centroids - centroids of each farthest cluster.
    
    % Initialize initial indices as zeros.
    centroids = zeros(numPoints,1);
    
    % Distance from centroid to each point.
    distance = ones(size(pointLocations,1),1) .* 1e10; 
    
    % Random Initialization of the first point.
    farthest = randi([1,size(pointLocations,1)],1);
    
    for i = 1:numPoints
        centroids(i) = farthest;
        centroid = pointLocations(farthest,:);
        dist = sum(((pointLocations - centroid).^2),2);
        mask = dist < distance;
        distance(mask) = dist(mask);
        [~,farthest] = max(distance,[],1);
    end
end

Запросите шариковый наконечник

function groupIdx = queryBallPoint(XYZ,newXYZ,nClusters,nSamples,radius)
% Given the cluster center, the queryBallPoint finds all points that are
% within a radius to the query point.

    N = size(XYZ,1);
    groupIdx = reshape(1:N,[1,N]);
    groupIdx = repmat(groupIdx,[nClusters,1]);
    
    % Find the distance between the centroids and given points.
    sqDist = squareDistance(newXYZ,XYZ);    
    
    % Find the points that are inside the given radius.
    groupIdx(sqDist > (radius)^2) = N;
    groupIdx = sort(groupIdx,2,"ascend");
    
    % Find the closest nSamples points within the given radius.
    groupIdx = groupIdx(:,1:nSamples);
    groupFirst = repmat(groupIdx(:,1),1,nSamples);
    mask = (groupIdx == N);
    groupIdx(mask) = groupFirst(mask);
end

Покажите распространение

function newPoints = featurePropagation(points1,points2,pointNetFeatures1,pointNetFeatures2)
    % Use the inverse distance weighted average based on the k nearest neighbors to
    % interpolate features.
    
    points1 = extractdata(squeeze(points1));
    points2 = extractdata(squeeze(points2));
    if ~isempty(pointNetFeatures1)
    pointNetFeatures1 = extractdata(squeeze(pointNetFeatures1));
    end
    pointNetFeatures2 = extractdata(squeeze(pointNetFeatures2));
    
    % Find the K nearest neighbors for each point.
    dists = squareDistance(points1,points2);
    [dists,idx] = sort(dists,2,"ascend");
    dists = dists(:,1:3);
    idx = idx(:,1:3);
    
    % Calculate the weights for interpolation.
    dist_recip = 1./(dists+1e-8);
    normFactor = sum(dist_recip,2);
    weights = dist_recip./normFactor;
    
    % Perform weighted interpolation.
    interpolatedPoints = pointNetFeatures2(idx,:);
    interpolatedPoints = reshape(interpolatedPoints,[size(idx,1),size(idx,2),size(pointNetFeatures2,2)]);
    
    interpolatedPoints = interpolatedPoints .* weights;
    interpolatedPoints = squeeze(sum(interpolatedPoints,2));
    
    if ~isempty(pointNetFeatures1)
        % Calculate the new points.
        newPoints = cat(2,pointNetFeatures1,interpolatedPoints);
    else
        newPoints = interpolatedPoints;
    end
    
     newPoints = dlarray(newPoints,'SCS');
end

% Find the squared distance.
function dist = squareDistance(src,dst)
    dist = -2 * (src*permute(dst,[2,1,3]));
    tmp1 = sum(src.^2,2);
    tmp1 = reshape(tmp1,[size(src,1),1]);
    tmp2 = sum(dst.^2,2);
    tmp2 = reshape(tmp2,[1,size(dst,1)]);
    dist = dist + tmp1 + tmp2;
end

Разделяемая многоуровневая функция Perceptron

function dlY= sharedMLP(dlX,parameters)
% The shared multilayer perceptron function processes the input dlX using a
% series of perceptron operations and returns the result of the last
% perceptron.
    dlY = dlX;
    for k = 1:numel(parameters) 
        dlY = perceptron(dlY,parameters(k));
    end
end

Функция Perceptron

function dlY = perceptron(dlX,parameters)
% The perceptron function processes the input dlX using a convolution, a
% instance normalization, and a ReLU operation and returns the output of the
% ReLU operation.

    % Convolution.
    W = parameters.Conv.Weights;
    B = parameters.Conv.Bias;
    dlY = dlconv(dlX,W,B);
    
    % Instance normalization.
    offset = parameters.InstanceNorm.Offset;
    scale = parameters.InstanceNorm.Scale;
    dlY = instancenorm(dlY,offset,scale);
    
    % ReLU.
    dlY = relu(dlY);
end

Вспомогательные Функции

normalizePointCloud функционируйте извлекает эти X, Y, Z данные о точке из входных данных и нормирует данные между 0 и 1. Функция возвращает нормированный X, Y, Z данные.

function data = normalizePointCloud(data)
    if ~iscell(data)
        data = {data};
    end
    
    numObservations = size(data,1);
    for i = 1:numObservations
        % Scale points between 0 and 1.
        xlim = data{i,1}.XLimits;
        ylim = data{i,1}.YLimits;
        zlim = data{i,1}.ZLimits;
        
        xyzMin = [xlim(1) ylim(1) zlim(1)];
        xyzDiff = [diff(xlim) diff(ylim) diff(zlim)];
        
        tmp = (data{i,1}.Location - xyzMin) ./ xyzDiff;
        % Convert the data to SCSB format.
        data{i,1} = permute(tmp,[1,3,2]);
    end
end

function data = onehotEncodeLabels(data,classNames)
    numObservations = size(data,1);
    for i = 1:numObservations
       labels = data{i,1}';
       encodedLabels = onehotencode(labels,2,'ClassNames',classNames);
       data{i,1} = permute(encodedLabels,[1,3,2]);
    end
end

prepareForPrediction Функция

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

function p = prepareForPrediction(p,fcn)

for i = 1:numel(p)
    p(i) = structfun(@(x)invoke(fcn,x),p(i),'UniformOutput',0);
end

    function data = invoke(fcn,data)
        if isstruct(data)
            data = prepareForPrediction(data,fcn);
        else
            data = fcn(data);
        end
    end
end

% Move data to the GPU.
function x = toDevice(x,useGPU)
if useGPU
    x = gpuArray(x);
end
end

selectPoints Функция

selectPoints функциональные выборки желаемое число точек. Когда облако точек содержит больше, чем желаемое число точек, функция использует pcdownsample случайным образом выбрать точки. В противном случае функция реплицирует данные, чтобы произвести желаемое число точек.

function ptCloudOut = selectPoints(ptCloud,numPoints) 
    if ptCloud.Count > numPoints
        ind = 1:ptCloud.Count;
    else    
        replicationFactor = ceil(numPoints/ptCloud.Count);
        ind = repmat(1:ptCloud.Count,1,replicationFactor);
    end 
    
     ptCloudOut = select(ptCloud,ind(1:numPoints));
end

labelColorbar функция добавляет шкалу палитры в текущую ось. Шкала палитры отформатирована, чтобы отобразить имена классов с цветом.

function labelColorbar(ax, cmap, 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

function cmap = labelColorMap()
% Colormap for the original classes.
cmap = [[10,10,10];
        [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;
end

function lossPlotter = configureTrainingProgressPlotter(f)
% The configureTrainingProgressPlotter function configures training
% progress plots for various losses.
    figure(f);
    clf
    ylabel('Total Loss');
    xlabel('Iteration');
    lossPlotter = animatedline;
end

Ссылки

[1] Varney, Нина, Вияйян К. Азари и Квинн Грэехлинг. "ДОЛИНЫ: Крупномасштабная Антенна Набор данных 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.