3-D Lidar Обнаружения объектов с использованием глубокого обучения PointPillars

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

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

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

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

Этот пример использует подмножество PandaSet, которое содержит 2560 предварительно обработанных организованных облаков точек. Каждое облако точек покрывает 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-адреса с помощью helperD- ownloadPretrainedPointPillarsNet вспомогательная функция, заданная в конце этого примера. Предварительно обученная модель позволяет вам запустить весь пример, не дожидаясь завершения обучения. Если вы хотите обучить сеть, установите doTraining переменная в true.

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

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

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

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

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}};

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

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

  • Выберите прямоугольные метки, которые находятся внутри информация только для чтения, заданный как 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 (Computer Vision Toolbox) функция.

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

Createa box label datastore с использованием boxLabelDatastore (Computer Vision Toolbox) для загрузки 3-D меток ограничивающих прямоугольников.

bds = boxLabelDatastore(trainLabels);

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

cds = combine(lds,bds);

Увеличение количества данных

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

Чтение и отображение облака точек перед увеличением с помощью 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, pi/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);

Извлечение информации о компоненте

Можно применить 2-D архитектуру свертки к облакам точек для более быстрой обработки. Для этого сначала преобразуйте облака точек 3-D в 2-D представление. Используйте 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));

Определение сети

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

Определите размерности анкерного ящика на основе классов, которые нужно обнаружить. Как правило, эти размеры являются средствами всех значений ограничивающих прямоугольников в набор обучающих данных [1]. Также можно использовать calculateAnchorBoxes вспомогательная функция, присоединенная к примеру, для получения соответствующих якорных коробок из любого набора обучающих данных. Анкерные рамки заданы в формате {length, width, height, z-center, yaw angle}.

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.

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

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

numEpochs = 60;
miniBatchSize = 2;
learningRate = 0.0002;
learnRateDropPeriod = 15;
learnRateDropFactor = 0.8;
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 для загрузки данных пакетами 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 объект. Затем создайте график процесса обучения с помощью helperConfigureTrainingProgressPlotter вспомогательная функция, заданная в конце этого примера.

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

  • Считайте облака точек и основные блоки истинности из minibatchqueue объект с использованием next функция.

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

  • Обновляйте параметры сети с помощью adamupdate функция.

  • Обновление параметров состояния 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 (Computer Vision Toolbox) ). В данном примере используйте среднюю метрику точности. Средняя точность обеспечивает одно число, которое включает в себя способность детектора делать правильные классификации (точность) и способность детектора находить все релевантные объекты (отзыв).

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

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

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

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

  • Функции evaluateDetectionAOS (Computer Vision Toolbox) с 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 net объекта и мини-пакет входных данных pillarFeatures и pillarIndices с соответствующими основными блоками истинности, анкерными полями и параметрами сетки. Функция возвращает градиенты потерь относительно настраиваемых параметров в net, соответствующие мини-потери партии и состояние текущей партии.

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

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

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

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

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

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

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] Lang, Alex H., Sourabh Vora, Holger Caesar, Lubing Zhou, Jiong Yang, and Oscar Beijbom. «PointPillars: быстрые энкодеры для обнаружения объектов из облаков точек». В 2019 году IEEE/CVF Conference on Компьютерное Зрение and Pattern Recognition (CVPR), 12689-12697. Лонг Бич, Калифорния, США: IEEE, 2019. https://doi.org/10.1109/CVPR.2019.01298.

[2] Хесаи и шкала. PandaSet. https://scale.com/open-datasets/pandaset.