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

В этом примере показано, как сгенерировать код CUDA® MEX для сети PointNet ++ [1] для семантической сегментации лидара. Этот пример использует предварительно обученную сеть PointNet ++, которая может сегментировать неорганизованные облака точек лидара, принадлежащие восьми классам (создания, автомобили, грузовики, полюса, линии электропередачи, заборы, земля и растительность). Для получения дополнительной информации о сети PointNet ++ смотрите Начало работы с PointNet ++.

Сторонние необходимые условия

Необходимый

  • CUDA включил NVIDIA® графический процессор и совместимый драйвер.

Дополнительный

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

Проверьте среду графического процессора

Чтобы проверить, что компиляторы и библиотеки для выполнения этого примера настраиваются правильно, используйте coder.checkGpuInstall (GPU Coder) функция.

envCfg = coder.gpuEnvConfig('host');
envCfg.DeepLibTarget = 'cudnn';
envCfg.DeepCodegen = 1;
envCfg.Quiet = 1;
coder.checkGpuInstall(envCfg);

Загрузите PointNet ++ сеть

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

net = getPointnetplusNet;

Предварительно обученная сеть является сетью DAG. Чтобы отобразить интерактивную визуализацию сетевой архитектуры, используйте analyzeNetwork (Deep Learning Toolbox) функция.

Выборка и группировка слоя и слоя интерполяции реализованы с помощью functionLayer Функция (Deep Learning Toolbox), которая не поддерживает генерацию кода. Так, замените функциональные слои в сети с пользовательскими слоями, которые поддерживают генерацию кода, использующую helperReplaceFunctionLayers функция помощника и сохраняет сеть как файл MAT с именем pointnetplusCodegenNet.mat.

net = helperReplaceFunctionLayers(net);

pointnetplusPredict Функция точки входа

pointnetplusPredict функция точки входа берет матрицу данных облака точек, как введено и выполняет предсказание на нем при помощи нейронной сети для глубокого обучения, сохраненной в pointnetplusCodegenNet.mat файл. Функция загружает сетевой объект от pointnetplusCodegenNet.mat файл в персистентную переменную mynet и повторные использования персистентная переменная в последующих вызовах предсказания.

type('pointnetplusPredict.m');
function out = pointnetplusPredict(in)
%#codegen

% A persistent object mynet is used to load the DAG network object. At
% the first call to this function, the persistent object is constructed and
% setup. When the function is called subsequent times, the same object is
% reused to call predict on inputs, thus avoiding reconstructing and
% reloading the network object.

% Copyright 2021 The MathWorks, Inc.

persistent mynet;

if isempty(mynet)
    mynet = coder.loadDeepLearningNetwork('pointnetplusCodegenNet.mat');
end

% pass in input
out = predict(mynet,in);

Сгенерируйте код MEX CUDA

Сгенерировать код CUDA® для pointnetplusPredict функция точки входа, создайте объект настройки графического процессора кода для цели MEX и установите выходной язык на C++. Используйте coder.DeepLearningConfig (GPU Coder) функция, чтобы создать настройку глубокого обучения CuDNN возражает и присвоить ее DeepLearningConfig свойство объекта настройки графического процессора кода. Запустите codegen команда с размером данных об облаке точек во входном слое сети, которая в этом случае является [8192 1 3].

cfg = coder.gpuConfig('mex');
cfg.TargetLang = 'C++';
cfg.DeepLearningConfig = coder.DeepLearningConfig(TargetLibrary='cudnn');
codegen -config cfg pointnetplusPredict -args {randn(8192,1,3,'single')} -report
Code generation successful: View report

Чтобы сгенерировать код CUDA® для цели TensorRT, создайте и используйте объект настройки глубокого обучения TensorRT вместо объекта настройки CuDNN.

Облако точек антенны сегмента Используя сгенерированный код MEX

Сеть в этом примере обучена на наборе данных DALES [2]. Следуйте инструкциям на веб-сайте DALES, чтобы загрузить набор данных на папку, заданную dataFolder переменная. Создайте папки, чтобы сохранить обучение и тестовые данные.

dataFolder = fullfile(tempdir,'DALES');
testDataFolder = fullfile(dataFolder,'dales_las','test');

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

Задайте numNearestNeighbors и radius найти самые близкие точки в прореженном облаке точек для каждой точки в плотном облаке точек и выполнить интерполяцию эффективно. Задайте параметры gridSize, numPoints, и maxLabel, используемый, чтобы обучить сеть.

numNearestNeighbors = 20;
radius = 0.05;
gridSize = [50,50];
numPoints = 8192;
maxLabel = 1;

Сохраните интерполированные метки и целевые метки в DensePredictedLabels, и DenseTargetLabels директории, соответственно.

densePcTargetLabelsPath = fullfile(testDataFolder,'DenseTargetLabels');
densePcPredLabelsPath =  fullfile(testDataFolder,'DensePredictedLabels');

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

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));
classNames = [
    "ground"
    "vegetation"
    "cars"
    "trucks"
    "powerlines"
    "fences"
    "poles"
    "buildings"
    ];
numClasses = numel(classNames);

Вычислите количество неперекрывающихся сеток на основе 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);

Выполните итерации по всем неперекрывающимся сеткам и предскажите метки с помощью pointnetplusPredict_mex 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 the 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.
    scoresPred = pointnetplusPredict_mex(ptCloudSparseForPrediction{1,1});
    [~,labelsSparsePred] = max(scoresPred,[],3);
    labelsSparsePred = uint8(labelsSparsePred);

    % 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: 8).

Для лучшей визуализации выберите необходимую область из данных об облаке точек. Измените пределы в 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');

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

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] Ци, Чарльз Р., Ли И, Хао Су и Леонидас Дж. Гуибас. "PointNet ++: Глубоко Иерархическая Функция, Учащаяся на Наборах Точки в Метрическом пространстве". ArXiv:1706.02413 [Cs], 7 июня 2017. https://arxiv.org/abs/1706.02413.

[2] Varney, Нина, Вияйян К. Азари и Квинн Грэехлинг. "ДОЛИНЫ: Крупномасштабная Антенна Набор данных LiDAR для Семантической Сегментации". ArXiv:2004.11985 [Cs, Статистика], 14 апреля 2020. https://arxiv.org/abs/2004.11985.