Лоцируйте 3-D обнаружение объектов Используя глубокое обучение PointPillars

В этом примере показано, как обучить сеть PointPillars для обнаружения объектов в облаках точек.

Данные об облаке точек лидара могут быть получены множеством датчиков лидара, включая Velodyne®, Pandar и датчики Изгнания. Эти датчики получают 3-D информацию о положении об объектах в сцене, которая полезна для многих приложений в автономном управлении автомобилем и дополненной реальности. Однако учебные устойчивые детекторы с данными об облаке точек сложны из-за разреженности данных на объект, объектные поглощения газов и шум датчика. Методы глубокого обучения, как показывали, обратились ко многим из этих проблем путем изучения устойчивых представлений функции непосредственно от данных об облаке точек. Один метод глубокого обучения для 3-D обнаружения объектов является PointPillars [1]. Используя подобную архитектуру к PointNet, извлечения сети PointPillars плотные, устойчивые функции от разреженных облаков точек вызвали столбы, затем используют 2D нейронную сеть для глубокого обучения с модифицированной сетью обнаружения объектов SSD, чтобы оценить объединенные 3-D ограничительные рамки, ориентации и предсказания класса.

Этот пример использует набор данных PandaSet [2] от Hesai и Scale. PandaSet содержит 8 240 неорганизованных сканов облака точек лидара различных городских сцен, полученных с помощью датчика Pandar64. Набор данных обеспечивает 3-D метки ограничительной рамки для 18 различных классов объектов, включая автомобиль, грузовик и пешехода.

Загрузите набор данных лидара

Этот пример использует подмножество PandaSet, который содержит 2 560 предварительно обработанных организованных облаков точек. Каждое облако точек покрытия 360o из представления, и задан как 64 1856 матрица. Облака точек хранятся в формате PCD, и их соответствующие достоверные данные хранится в PandaSetLidarGroundTruth.mat файл. Файл содержит 3-D информацию об ограничительной рамке для трех классов, которые являются автомобилем, грузовиком и пешеходом. Размер набора данных составляет 5,2 Гбайт.

Загрузите набор данных Pandaset с данного URL с помощью helperDownloadPandasetData функция помощника, заданная в конце этого примера.

outputFolder = fullfile(tempdir,'Pandaset');
lidarURL = ['https://ssd.mathworks.com/supportfiles/lidar/data/' ...
    'Pandaset_LidarData.tar.gz'];
helperDownloadPandasetData(outputFolder,lidarURL);

В зависимости от вашего Интернет-соединения может занять время процесс загрузки. Код приостанавливает выполнение MATLAB®, пока процесс загрузки не завершен. В качестве альтернативы можно загрузить набор данных на локальный диск с помощью веб-браузера и извлечь файл. Если вы делаете так, изменяете outputFolder переменная в коде к местоположению загруженного файла.

Загрузите предварительно обученную сеть

Загрузите предварительно обученную сеть с данного URL с помощью helperDownloadPretrainedPointPillarsNet функция помощника, заданная в конце этого примера. Предварительно обученная модель позволяет вам запускать целый пример, не имея необходимость ожидать обучения завершиться. Если вы хотите обучить сеть, установите doTraining переменная к true.

pretrainedNetURL = ['https://ssd.mathworks.com/supportfiles/lidar/data/' ...
    'trainedPointPillarsPandasetNet.zip'];

doTraining = false;
if ~doTraining
    helperDownloadPretrainedPointPillarsNet(outputFolder,pretrainedNetURL);
end

Загрузка данных

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

path = fullfile(outputFolder,'Lidar');
lidarData = fileDatastore(path,'ReadFcn',@(x) pcread(x));

Загрузите 3-D метки ограничительной рамки объектов автомобиля и грузовика.

gtPath = fullfile(outputFolder,'Cuboids','PandaSetLidarGroundTruth.mat');
data = load(gtPath,'lidarGtLabels');
Labels = timetable2table(data.lidarGtLabels);
boxLabels = Labels(:,2:3);

Отобразите облако полной точки наблюдения.

figure
ptCld = read(lidarData);
ax = pcshow(ptCld.Location);
set(ax,'XLim',[-50 50],'YLim',[-40 40]);
zoom(ax,2.5);
axis off;

reset(lidarData);

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

Данные PandaSet состоят из облаков полной точки наблюдения. В данном примере обрежьте облака полной точки наблюдения к облакам точек вида спереди с помощью стандартных параметров [1]. Эти параметры решают, что размер входа передал сети. Выбирание меньшей области значений облаков точек вдоль x, y, и оси z помогает обнаружить объекты, которые более близки началу координат, и также уменьшает полное учебное время сети.

xMin = 0.0;     % Minimum value along X-axis.
yMin = -39.68;  % Minimum value along Y-axis.
zMin = -5.0;    % Minimum value along Z-axis.
xMax = 69.12;   % Maximum value along X-axis.
yMax = 39.68;   % Maximum value along Y-axis.
zMax = 5.0;     % Maximum value along Z-axis.
xStep = 0.16;   % Resolution along X-axis.
yStep = 0.16;   % Resolution along Y-axis.
dsFactor = 2.0; % Downsampling factor.

% Calculate the dimensions for the pseudo-image.
Xn = round(((xMax - xMin) / xStep));
Yn = round(((yMax - yMin) / yStep));

% Define the pillar extraction parameters.
gridParams = {{xMin,yMin,zMin},{xMax,yMax,zMax},{xStep,yStep,dsFactor},{Xn,Yn}};

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

  • Обрежьте вид спереди от входного облака полной точки наблюдения.

  • Выберите метки поля, которые являются в ROI, заданном gridParams.

[croppedPointCloudObj,processedLabels] = cropFrontViewFromLidarData(...
    lidarData,boxLabels,gridParams);
Processing data 100% complete

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

pc = croppedPointCloudObj{1,1};
gtLabelsCar = processedLabels.Car{1};
gtLabelsTruck = processedLabels.Truck{1};

helperDisplay3DBoxesOverlaidPointCloud(pc.Location,gtLabelsCar,...
   'green',gtLabelsTruck,'magenta','Cropped Point Cloud');

reset(lidarData);

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

Разделите набор данных в наборы обучающих данных и наборы тестов. Выберите 70% данных для того, чтобы обучить сеть и остальных для оценки.

rng(1);
shuffledIndices = randperm(size(processedLabels,1));
idx = floor(0.7 * length(shuffledIndices));

trainData = croppedPointCloudObj(shuffledIndices(1:idx),:);
testData = croppedPointCloudObj(shuffledIndices(idx+1:end),:);

trainLabels = processedLabels(shuffledIndices(1:idx),:);
testLabels = processedLabels(shuffledIndices(idx+1:end),:);

Так, чтобы можно было легко получить доступ к хранилищам данных, сохраните обучающие данные как файлы PCD при помощи saveptCldToPCD функция помощника, присоединенная к этому примеру как вспомогательный файл. Можно установить writeFiles к "false" если ваши обучающие данные сохраняются в папке и поддерживаются pcread функция.

writeFiles = true;
dataLocation = fullfile(outputFolder,'InputData');
[trainData,trainLabels] = saveptCldToPCD(trainData,trainLabels,...
    dataLocation,writeFiles);
Processing data 100% complete

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

lds = fileDatastore(dataLocation,'ReadFcn',@(x) pcread(x));

Поле Createa помечает datastore с помощью boxLabelDatastore для загрузки 3-D меток ограничительной рамки.

bds = boxLabelDatastore(trainLabels);

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

cds = combine(lds,bds);

Увеличение данных

Этот пример использует увеличение достоверных данных и несколько других методов увеличения глобальных данных, чтобы добавить больше разнообразия в обучающие данные и соответствующие поля. Для получения дополнительной информации о типичных методах увеличения данных, используемых в 3-D рабочих процессах обнаружения объектов с данными о лидаре, смотрите Увеличения Данных для Обнаружения объектов Лидара Используя Глубокое обучение.

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

augData = read(cds);
augptCld = augData{1,1};
augLabels = augData{1,2};
augClass = augData{1,3};

labelsCar = augLabels(augClass=='Car',:);
labelsTruck = augLabels(augClass=='Truck',:);

helperDisplay3DBoxesOverlaidPointCloud(augptCld.Location,labelsCar,'green',...
    labelsTruck,'magenta','Before Data Augmentation');

reset(cds);

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

gtData = generateGTDataForAugmentation(trainData,trainLabels);

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

samplesToAdd = struct('Car',10,'Truck',10);
cdsAugmented = transform(cds,@(x) groundTruthDataAugmenation(x,gtData,samplesToAdd));

Кроме того, примените следующие увеличения данных к каждому облаку точек.

  • Случайное зеркальное отражение вдоль оси X

  • Случайное масштабирование на 5 процентов

  • Случайное вращение вдоль оси z от [-pi/4, пи/4]

  • Случайный перевод [0.2, 0.2, 0.1] метры вдоль x-, y-, и ось z соответственно

cdsAugmented = transform(cdsAugmented,@(x) augmentData(x));

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

augData = read(cdsAugmented);
augptCld = augData{1,1};
augLabels = augData{1,2};
augClass = augData{1,3};

labelsCar = augLabels(augClass=='Car',:);
labelsTruck = augLabels(augClass=='Truck',:);

helperDisplay3DBoxesOverlaidPointCloud(augptCld(:,1:3),labelsCar,'green',...
    labelsTruck,'magenta','After Data Augmentation');

reset(cdsAugmented);

Извлеките информацию о столбе

Можно применить 2D архитектуру свертки к облакам точек для более быстрой обработки. Для этого сначала преобразуйте 3-D облака точек в 2D представление. Используйте transform функция с createPillars функция помощника, присоединенная к этому примеру как вспомогательный файл, чтобы создать функции столба и индексы столба от облаков точек. Функция помощника выполняет следующие операции:

  • Дискретизируйте 3-D облака точек в равномерно расположенные с интервалами сетки в x-y плоскости, чтобы создать набор названных столбов вертикальных столбцов.

  • Выберите видные столбы (P) на основе числа точек на столб (N).

  • Вычислите расстояние до среднего арифметического всех точек в столбе.

  • Вычислите смещение из центра столба.

  • Используйте x, y, z местоположение, интенсивность, расстояние, и возместите значения, чтобы создать девять размерных (9-D) векторов для каждой точки в столбе.

% Define number of prominent pillars.
P = 12000; 

% Define number of points per pillar.
N = 100;   
cdsTransformed = transform(cdsAugmented,@(x) createPillars(x,gridParams,P,N));

Сеть Define

Сеть PointPillars использует упрощенную версию сети PointNet, которая берет функции столба в качестве входа. Для каждой функции столба сеть применяет линейный слой, сопровождаемый слоями ReLU и нормализацией партии. Наконец, сеть применяет макс. объединяющую операцию по каналам, чтобы получить высокоуровневые закодированные функции. Эти закодированные функции рассеиваются назад к исходным местоположениям столба, чтобы создать псевдоизображение с помощью пользовательского слоя helperscatterLayer, присоединенный к этому примеру как вспомогательный файл. Сеть затем обрабатывает псевдоизображение с 2D сверточной магистралью, сопровождаемой различными головами обнаружения SSD, чтобы предсказать 3-D ограничительные рамки наряду с его классами.

Задайте размерности поля привязки на основе классов, чтобы обнаружить. Как правило, эти размерности являются средними значениями всех значений ограничительной рамки в наборе обучающих данных [1]. В качестве альтернативы можно также использовать calculateAnchorBoxes функция помощника, присоединенная к примеру, для получения соответствующих полей привязки от любого набора обучающих данных. Поля привязки заданы в формате {длина, ширина, высота, z-центр, угол рыскания}.

anchorBoxes = calculateAnchorsPointPillars(trainLabels);
numAnchors = size(anchorBoxes,2);
classNames = trainLabels.Properties.VariableNames;
numClasses = numel(classNames);

Затем создайте сеть PointPillars с помощью pointpillarNetwork функция помощника, присоединенная к этому примеру как вспомогательный файл.

lgraph = pointpillarNetwork(numAnchors,gridParams,P,N,numClasses);

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

Задайте следующие опции обучения.

  • Определите номер эпох к 60.

  • Установите мини-пакетный размер на 2. Можно установить мини-пакетный размер на более высокое значение, если у вас есть более доступная память.

  • Установите скорость обучения на 0,0002.

  • Установите learnRateDropPeriod к 15. Этот параметр обозначает номер эпох, после которых можно пропустить скорость обучения на основе формулы learningRate×(iteration%learnRateDropPeriod)×learnRateDropFactor.

  • Установите learnRateDropFactor к 0,8. Этот параметр обозначает уровень, которым можно пропустить скорость обучения после каждого learnRateDropPeriod.

  • Установитесь коэффициент затухания градиента на 0,9.

  • Установитесь коэффициент затухания градиента в квадрате на 0,999.

  • Инициализируйте среднее значение градиентов к []. Это используется оптимизатором Адама.

  • Инициализируйте среднее значение градиентов в квадрате к []. Это используется оптимизатором Адама.

numEpochs = 60;
miniBatchSize = 2;
learningRate = 0.0002;
learnRateDropPeriod = 15;
learnRateDropFactor = 0.8;
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(...
    cdsTransformed,3,...
    "MiniBatchSize",miniBatchSize,...
    "OutputEnvironment",executionEnvironment,...
    "MiniBatchFcn",@(features,indices,boxes,labels) ...
    helperCreateBatchData(features,indices,boxes,labels,classNames),...
    "MiniBatchFormat",["SSCB","SSCB",""],...
    "DispatchInBackground",true);

Чтобы обучить сеть с пользовательским учебным циклом и включить автоматическое дифференцирование, преобразуйте график слоев в dlnetwork Объект (Deep Learning Toolbox). Затем создайте плоттер процесса обучения с помощью helperConfigureTrainingProgressPlotter функция помощника, заданная в конце этого примера.

Наконец, задайте пользовательский учебный цикл. Для каждой итерации:

  • Считайте облака точек и основные блоки истинности от minibatchqueue Объект (Deep Learning Toolbox) с помощью next (Deep Learning Toolbox) функция.

  • Оцените градиенты модели с помощью dlfeval (Deep Learning Toolbox) и modelGradients функция. modelGradients функция помощника, заданная в конце примера, возвращает градиенты потери относительно настраиваемых параметров в net, соответствующая мини-пакетная потеря и состояние текущего пакета.

  • Обновите сетевые параметры с помощью adamupdate (Deep Learning Toolbox) функция.

  • Обновите параметры состояния net.

  • Обновите график процесса обучения.

if doTraining
    % Convert layer graph to dlnetwork.
    net = dlnetwork(lgraph);
    
    % Initialize plot.
    fig = figure;
    lossPlotter = helperConfigureTrainingProgressPlotter(fig);    
    iteration = 0;
       
    % Custom training loop.
    for epoch = 1:numEpochs
        
        % Reset datastore.
        reset(mbq);
        while(hasdata(mbq))
            iteration = iteration + 1;
            
            % Read batch of data.
            [pillarFeatures,pillarIndices,boxLabels] = next(mbq);
                        
            % Evaluate the model gradients and loss using dlfeval 
            % and the modelGradients function.
            [gradients,loss,state] = dlfeval(@modelGradients,net,...
                pillarFeatures,pillarIndices,boxLabels,gridParams,...
                anchorBoxes,numClasses,executionEnvironment);
            
            % Do not update the network learnable parameters if NaN values
            % are present in gradients or loss values.
            if helperCheckForNaN(gradients,loss)
                continue;
            end
                    
            % Update the state parameters of dlnetwork.
            net.State = state;
            
            % Update the network learnable parameters using the Adam
            % optimizer.
            [net.Learnables,trailingAvg,trailingAvgSq] = ...
                adamupdate(net.Learnables,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
        
        % Update the learning rate after every learnRateDropPeriod.
        if mod(epoch,learnRateDropPeriod) == 0
            learningRate = learningRate * learnRateDropFactor;
        end
    end
else
    preTrainedMATFile = fullfile(outputFolder,'trainedPointPillarsPandasetNet.mat');
    pretrainedNetwork = load(preTrainedMATFile,'net');
    net = pretrainedNetwork.net;
end

Сгенерируйте обнаружения

Используйте обучивший сеть, чтобы обнаружить объекты в тестовых данных:

  • Считайте облако точек из тестовых данных.

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

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

ptCloud = testData{45,1};
gtLabels = testLabels(45,:);

% The generatePointPillarDetections function detects the 
% bounding boxes, and scores for a given point cloud.
confidenceThreshold = 0.5;
overlapThreshold = 0.1;
[box,score,labels] = generatePointPillarDetections(net,ptCloud,anchorBoxes,...
    gridParams,classNames,confidenceThreshold,overlapThreshold,P,N,...
    executionEnvironment);

boxlabelsCar = box(labels'=='Car',:);
boxlabelsTruck = box(labels'=='Truck',:);

% Display the predictions on the point cloud.
helperDisplay3DBoxesOverlaidPointCloud(ptCloud.Location,boxlabelsCar,'green',...
    boxlabelsTruck,'magenta','Predicted Bounding Boxes');

Оцените модель

Computer Vision Toolbox™ обеспечивает функции оценки детектора объектов, чтобы измерить общие метрики, такие как средняя точность (evaluateDetectionAOS). В данном примере используйте среднюю метрику точности. Средняя точность обеспечивает один номер, который включает способность детектора сделать правильные классификации (точность) и способность детектора найти все соответствующие объекты (отзыв).

Оцените обученный dlnetwork Объект (Deep Learning Toolbox) net на тестовых данных путем выполнения этих шагов.

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

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

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

  • Вызовите evaluateDetectionAOS с detectionResults и groundTruthData в качестве аргументов.

numInputs = numel(testData);

% Generate rotated rectangles from the cuboid labels.
bds = boxLabelDatastore(testLabels);
groundTruthData = transform(bds,@(x) createRotRect(x));

% Set the threshold values.
nmsPositiveIoUThreshold = 0.5;
confidenceThreshold = 0.25;
overlapThreshold = 0.1;

% Set numSamplesToTest to numInputs to evaluate the model on the entire
% test data set.
numSamplesToTest = 50;
detectionResults = table('Size',[numSamplesToTest 3],...
                        'VariableTypes',{'cell','cell','cell'},...
                        'VariableNames',{'Boxes','Scores','Labels'});

for num = 1:numSamplesToTest
    ptCloud = testData{num,1};
    
    [box,score,labels] = generatePointPillarDetections(net,ptCloud,anchorBoxes,...
        gridParams,classNames,confidenceThreshold,overlapThreshold,...
        P,N,executionEnvironment);
 
    % Convert the detected boxes to rotated rectangle format.
    if ~isempty(box)
        detectionResults.Boxes{num} = box(:,[1,2,4,5,7]);
    else
        detectionResults.Boxes{num} = box;
    end
    detectionResults.Scores{num} = score;
    detectionResults.Labels{num} = labels;
end

metrics = evaluateDetectionAOS(detectionResults,groundTruthData,...
    nmsPositiveIoUThreshold);
disp(metrics(:,1:2))
               AOS        AP   
             _______    _______

    Car      0.86746    0.86746
    Truck    0.61463    0.61463

Функции помощника

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

Функциональный modelGradients берет в качестве входа dlnetwork Объект (Deep Learning Toolbox) net и мини-пакет входных данных pillarFeatures и pillarIndices с соответствующими основными блоками истинности, полями привязки и параметрами сетки. Функция возвращает градиенты потери относительно настраиваемых параметров в net, соответствующая мини-пакетная потеря и состояние текущего пакета.

Функция градиентов модели вычисляет общую сумму убытков и градиенты путем выполнения этих операций.

  • Извлеките предсказания из сети с помощью forward (Deep Learning Toolbox) функция.

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

  • Вычислите функцию потерь для всех шести предсказаний от сети.

  • Вычислите общую сумму убытков как сумму всех потерь.

  • Вычислите градиенты learnables относительно общей суммы убытков.

function [gradients,loss,state] = modelGradients(net,pillarFeatures,...
    pillarIndices,boxLabels,gridParams,anchorBoxes,...
    numClasses,executionEnvironment)
      
    numAnchors = size(anchorBoxes,2);
    
    % Extract the predictions from the network.
    YPredictions = cell(size(net.OutputNames));
    [YPredictions{:},state] = forward(net,pillarIndices,pillarFeatures);
    
    % Generate target for predictions from the ground truth data.
    YTargets = generatePointPillarTargets(YPredictions,boxLabels,pillarIndices,...
        gridParams,anchorBoxes,numClasses);
    YTargets = cellfun(@ dlarray,YTargets,'UniformOutput',false);
    if (executionEnvironment=="auto" && canUseGPU) || executionEnvironment=="gpu"
        YTargets = cellfun(@ gpuArray,YTargets,'UniformOutput',false);
    end
     
    [angLoss,occLoss,locLoss,szLoss,hdLoss,clfLoss] = ...
        computePointPillarLoss(YPredictions,YTargets,gridParams,...
        numClasses,numAnchors);
    
    % Compute the total loss.
    loss = angLoss + occLoss + locLoss + szLoss + hdLoss + clfLoss;
    
    % Compute the gradients of the learnables with regard to the loss.
    gradients = dlgradient(loss,net.Learnables);
 
end

function [pillarFeatures,pillarIndices,labels] = helperCreateBatchData(...
    features,indices,groundTruthBoxes,groundTruthClasses,classNames)
% Return pillar features and indices combined along the batch dimension
% and bounding boxes concatenated along batch dimension in labels.
    
    % Concatenate features and indices along batch dimension.
    pillarFeatures = cat(4,features{:,1});
    pillarIndices = cat(4,indices{:,1});
    
    % Get class IDs from the class names.
    classNames = repmat({categorical(classNames')},size(groundTruthClasses));
    [~,classIndices] = cellfun(@(a,b)ismember(a,b),groundTruthClasses,...
        classNames,'UniformOutput',false);
    
    % Append the class indices and create a single array of responses.
    combinedResponses = cellfun(@(bbox,classid) [bbox,classid],...
        groundTruthBoxes,classIndices,'UniformOutput',false);
    len = max(cellfun(@(x)size(x,1),combinedResponses));
    paddedBBoxes = cellfun(@(v) padarray(v,[len-size(v,1),0],0,'post'),...
        combinedResponses,'UniformOutput',false);
    labels = cat(4,paddedBBoxes{:,1});
end

function helperDownloadPandasetData(outputFolder,lidarURL)
% Download the data set from the given URL to the output folder.

    lidarDataTarFile = fullfile(outputFolder,'Pandaset_LidarData.tar.gz');
    
    if ~exist(lidarDataTarFile,'file')
        mkdir(outputFolder);
        
        disp('Downloading PandaSet Lidar driving data (5.2 GB)...');
        websave(lidarDataTarFile,lidarURL);
        untar(lidarDataTarFile,outputFolder);
    end
    
    % Extract the file.
    if (~exist(fullfile(outputFolder,'Lidar'),'dir'))...
            &&(~exist(fullfile(outputFolder,'Cuboids'),'dir'))
        untar(lidarDataTarFile,outputFolder);
    end

end

function helperDownloadPretrainedPointPillarsNet(outputFolder,pretrainedNetURL)
% Download the pretrained PointPillars network.

    preTrainedMATFile = fullfile(outputFolder,'trainedPointPillarsPandasetNet.mat');
    preTrainedZipFile = fullfile(outputFolder,'trainedPointPillarsPandasetNet.zip');
    
    if ~exist(preTrainedMATFile,'file')
        if ~exist(preTrainedZipFile,'file')
            disp('Downloading pretrained detector (8.4 MB)...');
            websave(preTrainedZipFile,pretrainedNetURL);
        end
        unzip(preTrainedZipFile,outputFolder);   
    end       
end

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

function retValue = helperCheckForNaN(gradients,loss)
% The last convolution head 'occupancy|conv2d' is known to contain NaNs 
% the gradients. This function checks whether gradient values contain 
% NaNs. Add other convolution head values to the condition if NaNs 
% are present in the gradients. 
    gradValue = gradients.Value((gradients.Layer == 'occupancy|conv2d') & ...
        (gradients.Parameter == 'Bias'));
    if (sum(isnan(extractdata(loss)),'all') > 0) || ...
            (sum(isnan(extractdata(gradValue{1,1})),'all') > 0)
        retValue = true;
    else
        retValue = false;
    end
end

function helperDisplay3DBoxesOverlaidPointCloud(ptCld,labelsCar,carColor,...
    labelsTruck,truckColor,titleForFigure)
% Display the point cloud with different colored bounding boxes for different
% classes.
    figure;
    ax = pcshow(ptCld);
    showShape('cuboid',labelsCar,'Parent',ax,'Opacity',0.1,...
        'Color',carColor,'LineWidth',0.5);
    hold on;
    showShape('cuboid',labelsTruck,'Parent',ax,'Opacity',0.1,...
        'Color',truckColor,'LineWidth',0.5);
    title(titleForFigure);
    zoom(ax,1.5);
end

Ссылки

[1] Ленг, Алекс Х., Sourabh Vora, Хольгер Цезарь, Лубин Чжоу, Цзюн Ян и Оскар Бейджбом. "PointPillars: Быстрые Энкодеры для Обнаружения объектов От Облаков точек". На 2019 Конференциях IEEE/CVF по Компьютерному зрению и Распознаванию образов (CVPR), 12689-12697. Лонг-Бич, CA, США: IEEE, 2019. https://doi.org/10.1109/CVPR.2019.01298.

[2] Hesai и Scale. PandaSet. https://scale.com/open-datasets/pandaset.