В этом примере показано, как обучить нейронную сеть для глубокого обучения PointNet++ для выполнения семантической сегментации на данных о воздушном лидаре.
Данные лидар, полученные из бортовых лазерных систем сканирования, все чаще используются в таких приложениях, как топографическое отображение, создание и городов, измерение биомассы и управление стихийными бедствиями. Извлечение содержательной информации из этих данных требует семантической сегментации, процесса, в котором каждому пикселю в облаке точек присваивается метка класса.
В этом примере вы обучаете сеть PointNet++ для выполнения семантической сегментации с помощью набора данных [1] Dayton annotated lidar скан (DALES), который содержит сцены плотных, маркированных лидарными воздушными данными из городских, пригородных, сельских и коммерческих настроек. Из 40 сцен 29 сцен используются для обучения, а остальные 11 последовательностей используются для проверки. Набор данных обеспечивает семантические метки сегментации для 8, таких как создания, автомобили, грузовики, столбы, линии степеней, ограждения, земля и растительность.
Набор данных 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 ® до завершения обработки.
Создайте fileDatastore
объект для загрузки файлов PCD с помощью pcread
функция.
ldsTrain = fileDatastore(pcCropTrainPath,'ReadFcn',@(x) pcread(x));
Использование pixelLabelDatastore
объект для хранения пиксельных меток из изображений меток пикселей. Объект сопоставляет каждую пиксельную метку с именем класса. В этом примере земля, растительность и создания являются единственными интересующими классами; все другие пиксели являются фоном. Задайте эти классы и присвойте уникальный идентификатор метки каждому классу.
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++ [2] состоит из двух основных компонентов:
Установите абстракционные модули
Модули распространения функций
Серия модулей абстракции набора постепенно подсеменяет интересующие точки иерархически группированием точек и использует пользовательскую архитектуру PointNet, чтобы закодировать точки в векторы функций. Поскольку семантические задачи сегментации требуют функций точек для всех исходных точек, этот пример использует серию модулей распространения функций для иерархической интерполяции функций к исходным точкам с помощью схемы интерполяции на основе обратного расстояния.
Модуль абстракции набора реализован с помощью sampleAndGroup
и sharedMLP
вспомогательные функции и модуль распространения признаков реализован с помощью featurePropagation
и sharedMLP
вспомогательные функции. The sharedMLP
модуль является единственным обучаемым модулем, который состоит из слоев свертки и нормализации образца. Используйте вспомогательную функцию pointNetplusModel
для определения всей модели сегментации PointNet++. Вспомогательные функции перечислены в конце этого примера.
The sampleAndGroup
функция модуля абстракции набора параметризована количеством кластеров, количеством выборок на кластер и радиусом каждого кластера, тогда как sharedMLP
функция реализует пользовательскую архитектуру PointNet, которая параметризована с помощью количества входа каналов и скрытых размеров каналов.
В этом примере используются значения гиперзначений параметров, настроенные на наборе данных DALES. Если вы хотите применить PointNet++ к другому набору данных, необходимо выполнить дополнительную настройку гиперпараметра.
Установите параметры первого модуля абстракции набора. Установите количество кластеров равное 1024, количество выборок в каждом кластере равное 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 = [];
Обучите модель с помощью CPU или GPU. Для использования GPU требуется Parallel Computing Toolbox™ и графический процессор с поддержкой CUDA ® NVIDIA ®. Для получения дополнительной информации смотрите Поддержку GPU by Release (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
для обновления параметров модели.
Обновите график процесса обучения с помощью функции helper 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]
The 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
Функция model принимает как вход параметры модели 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
The initializeSharedMLP
функция принимает за вход размер канала и скрытый размер канала, и возвращает инициализированные параметры в структуре. Параметры инициализируются с помощью инициализации веса He.
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
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
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
The 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
ФункцияThe 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
ФункцияThe 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
The 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] Варней, Нина, Виджаян К. Асари и Куинн Грелинг. DALES: Крупномасштабный набор данных воздушного ЛИДАРА для семантической сегментации. ArXiv:2004.11985 [Cs, Stat], 14 апреля 2020 года. https://arxiv.org/abs/2004.11985.
[2] Qi, Charles R., Li Yi, Hao Su, and Leonidas J. Guibas. «PointNet++: Глубокое иерархическое обучение функций на наборах точек в метрическом пространстве». ArXiv:1706.02413 [Cs], 7 июня 2017 года. https://arxiv.org/abs/1706.02413.