В этом примере показано, как сгенерировать код CUDA® MEX для сети PointNet ++ [1] для семантической сегментации лидара. Этот пример использует предварительно обученную сеть PointNet ++, которая может сегментировать неорганизованные облака точек лидара, принадлежащие восьми классам (создания, автомобили, грузовики, полюса, линии электропередачи, заборы, земля и растительность). Для получения дополнительной информации о сети PointNet ++ смотрите Начало работы с PointNet ++ (Lidar Toolbox).
CUDA включил NVIDIA® графический процессор и совместимый драйвер.
Для сборок неMEX, таких как статические библиотеки, динамические библиотеки или исполняемые файлы, этот пример имеет следующие дополнительные требования.
Инструментарий NVIDIA.
Библиотека NVIDIA cuDNN.
Переменные окружения для компиляторов и библиотек. Для получения дополнительной информации смотрите Стороннее Оборудование (GPU Coder) и Подготовка Необходимых как условие продуктов (GPU Coder).
Чтобы проверить, что компиляторы и библиотеки для выполнения этого примера настраиваются правильно, используйте coder.checkGpuInstall
(GPU Coder) функция.
envCfg = coder.gpuEnvConfig('host'); envCfg.DeepLibTarget = 'cudnn'; envCfg.DeepCodegen = 1; envCfg.Quiet = 1; coder.checkGpuInstall(envCfg);
Используйте функцию getPointnetplusNet, присоединенную как вспомогательный файл к этому примеру, чтобы загрузить предварительно обученную сеть PointNet ++. Для получения дополнительной информации о том, как обучить эту сеть, смотрите, что Воздушная Семантическая Сегментация Лидара Использует PointNet ++ Глубокое обучение (Lidar Toolbox).
net = getPointnetplusNet;
Предварительно обученная сеть является сетью DAG. Чтобы отобразить интерактивную визуализацию сетевой архитектуры, используйте analyzeNetwork
функция.
Выборка и группировка слоя и слоя интерполяции реализованы с помощью functionLayer
функция, которая не поддерживает генерацию кода. Так, замените функциональные слои в сети с пользовательскими слоями, которые поддерживают генерацию кода, использующую 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);
Сгенерировать код 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.
Сеть в этом примере обучена на наборе данных 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.