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

В этом примере показано, как обучить сеть семантической сегментации PointSeg на 3-D данных облака точек лидара.

PointSeg [1] является сверточной нейронной сетью (CNN) для выполнения сквозной семантической сегментации дорожных объектов на основе организованного лидара облака точек. При использовании таких методов, как atrous spatial pyramid uling (ASPP) и блоки сжатия и возбуждения, сеть обеспечивает улучшенные результаты сегментации. Процедура обучения, показанная в этом примере, требует 2-D сферических проективных изображений в качестве входов для нейронной сети для глубокого обучения.

Этот пример использует набор данных сцены шоссе, собранный с помощью датчика OS1 Ouster. Он содержит организованные сканы облака точек лидара сцен шоссе и соответствующие основные истины для объектов автомобиля и грузовика. Размер файла данных составляет приблизительно 760 МБ.

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

Выполните этот код, чтобы загрузить набор данных сцены шоссе. Набор данных содержит 1617 облаков точек, сохраненных как pointCloud объекты в массиве ячеек. Соответствующие достоверные данные, который прилагается к примеру, содержат информацию ограничивающего прямоугольника автомобилей и грузовиков в каждом облаке точек.

url = 'https://www.mathworks.com/supportfiles/lidar/data/WPI_LidarData.tar.gz';

outputFolder = fullfile(tempdir,'WPI');
lidarDataTarFile = fullfile(outputFolder,'WPI_LidarData.tar.gz');

if ~exist(lidarDataTarFile, 'file') 
    mkdir(outputFolder);
    
    disp('Downloading WPI Lidar driving data (760 MB)...');
    websave(lidarDataTarFile, url);
    untar(lidarDataTarFile,outputFolder); 
end

% Check if tar.gz file is downloaded, but not uncompressed.
if ~exist(fullfile(outputFolder, 'WPI_LidarData.mat'), 'file')
    untar(lidarDataTarFile,outputFolder);
end
lidarData = load(fullfile(outputFolder, 'WPI_LidarData.mat'));

groundTruthData = load('WPI_LidarGroundTruth.mat');

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

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

Загрузите предварительно обученную сеть, чтобы не ждать завершения обучения. Если вы хотите обучить сеть, установите doTraining переменная - true.

doTraining = false;
if ~doTraining && ~exist('trainedPointSegNet.mat','file')
    disp('Downloading pretrained network (14 MB)...');
    pretrainedURL = 'https://www.mathworks.com/supportfiles/lidar/data/trainedPointSegNet.mat';
    websave('trainedPointSegNet.mat', pretrainedURL);
end
Downloading pretrained network (14 MB)...

Подготовка данных к обучению

Загрузка облаков точек лидара и меток классов

Используйте helperGenerateTrainingData вспомогательная функция, присоединенная к этому примеру, для генерации обучающих данных из лидара облаков точек. Функция использует облако точек и данные ограничивающего прямоугольника, чтобы создать пятиканальные входные изображения и изображения пиксельных меток. Чтобы создать изображения меток пикселей, функция выбирает точки внутри ограничивающего прямоугольника и помечает их идентификатором класса ограничивающих прямоугольников. Каждое обучающее изображение задается как массив 64 на 1024 на 5:

  • Высота каждого изображения составляет 64 пикселя.

  • Ширина каждого изображения составляет 1024 пикселя.

  • Каждое изображение имеет 5 каналов. Пять каналов определяют координаты 3-D облака точек, интенсивность и область значений: r=x2+y2+z2.

Далее приводится визуальное представление обучающих данных.

Сгенерируйте пятиканальные обучающие изображения и пиксельные изображения с меткой.

imagesFolder = fullfile(outputFolder, 'images');
labelsFolder = fullfile(outputFolder, 'labels');

helperGenerateTrainingData(lidarData, groundTruthData, imagesFolder, labelsFolder); 
Preprocessing data 100.00% complete

Пятиканальные изображения сохраняются как файлы MAT. Пиксельные метки сохраняются как файлы PNG.

Примечание: Обработка может занять некоторое время. Код приостанавливает выполнение MATLAB ® до завершения обработки .

Создайте mageDatastore и P ixelLabelDatastore

Используйте imageDatastore объект для извлечения и хранения пяти каналов 2-D сферических изображений с помощью helperImageMatReader вспомогательная функция, являющаяся пользовательской программой чтения файлов MAT. Эта функция присоединена к этому примеру как вспомогательный файл.

imds = imageDatastore(imagesFolder, ...
         'FileExtensions', '.mat', ...
         'ReadFcn', @helperImageMatReader);

Используйте pixelLabelDatastore (Computer Vision Toolbox) для хранения пиксельных меток с изображений меток. Объект сопоставляет каждую пиксельную метку с именем класса. В этом примере легковые и грузовые автомобили являются единственными объектами интереса; все другие пиксели являются фоном. Задайте эти классы (автомобиль, грузовик и фон) и присвойте уникальный идентификатор метки каждому классу.

classNames = [
    "background"
    "car"
    "truck"
];

numClasses = numel(classNames);

% Specify label IDs from 1 to the number of classes.
labelIDs = 1 : numClasses;

pxds = pixelLabelDatastore(labelsFolder, classNames, labelIDs);

Загрузите и отобразите одно из маркированных изображений путем наложения его на соответствующее изображение интенсивности с помощью helperDisplayLidarOverlayImage function, заданная в разделе Support Functions этого примера.

imageNumber = 225;

% Point cloud (channels 1, 2, and 3 are for location, channel 4 is for intensity).
I = readimage(imds, imageNumber);

labelMap = readimage(pxds, imageNumber);
figure;
helperDisplayLidarOverlayImage(I, labelMap, classNames);
title('Ground Truth');

Подготовка наборов для обучения, валидации и тестирования

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

[imdsTrain, imdsVal, imdsTest, pxdsTrain, pxdsVal, pxdsTest] = ...
    helperPartitionLidarData(imds, pxds);

Используйте combine функция для объединения хранилищ данных пикселей и изображений для наборов данных обучения и валидации.

trainingData = combine(imdsTrain, pxdsTrain); 
validationData = combine(imdsVal, pxdsVal);

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

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

Увеличение обучающих данных с помощью transform функция с пользовательскими операциями предварительной обработки, заданными augmentData function, заданная в разделе Support Functions этого примера. Эта функция случайным образом переворачивает сферическое 2-D изображение и связанные с ним метки в горизонтальном направлении. Применить увеличение данных только к набору обучающих данных.

augmentedTrainingData = transform(trainingData, @(x) augmentData(x));

Балансировочные классы с использованием взвешивания классов

Чтобы увидеть распределение меток классов в наборе данных, используйте countEachLabel (Computer Vision Toolbox) функция.

tbl = countEachLabel(pxds);
tbl(:,{'Name','PixelCount','ImagePixelCount'})
ans=3×3 table
         Name         PixelCount    ImagePixelCount
    ______________    __________    _______________

    {'background'}    1.0473e+08      1.0597e+08   
    {'car'       }    9.7839e+05      8.4738e+07   
    {'truck'     }    2.6017e+05      1.9726e+07   

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

Используйте эти веса, чтобы исправить дисбаланс классов. Используйте счетчики меток пикселей из tbl.PixelCount Свойство и вычислите веса классов медианной частоты.

imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount;
classWeights = median(imageFreq) ./ imageFreq
classWeights = 3×1

    0.0133
    1.1423
    1.0000

Определение сетевой архитектуры

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

inputSize = [64 1024 5];

lgraph = createPointSeg(inputSize, classNames, classWeights);

Используйте analyzeNetwork функция для отображения интерактивной визуализации сетевой архитектуры.

analyzeNetwork(lgraph)

Настройка опций обучения

Используйте rmsprop алгоритм оптимизации для обучения сети. Задайте гиперпараметры для алгоритма при помощи trainingOptions функция.

maxEpochs = 30;
initialLearningRate= 5e-4;
miniBatchSize = 8;
l2reg = 2e-4;

options = trainingOptions('rmsprop', ...
    'InitialLearnRate', initialLearningRate, ...
    'L2Regularization', l2reg, ...
    'MaxEpochs', maxEpochs, ...
    'MiniBatchSize', miniBatchSize, ...
    'LearnRateSchedule', 'piecewise', ...
    'LearnRateDropFactor', 0.1, ...
    'LearnRateDropPeriod', 10, ...
    'ValidationData', validationData, ...
    'Plots', 'training-progress', ...
    'VerboseFrequency', 20);

Примечание: Уменьшите miniBatchSize управлять использованием памяти при обучении.

Обучите сеть

Используйте trainNetwork функция для обучения сети PointSeg, если doTraining является true. В противном случае загружает предварительно обученную сеть

Если вы обучаете сеть, можно использовать CPU или GPU. Для использования GPU требуется Parallel Computing Toolbox™ и графический процессор с поддержкой CUDA ® NVIDIA ®. Для получения дополнительной информации смотрите Поддержку GPU by Release (Parallel Computing Toolbox).

if doTraining    
    [net, info] = trainNetwork(trainingData, lgraph, options);
else
    pretrainedNetwork = load('trainedPointSegNet.mat');
    net = pretrainedNetwork.net;
end

Предсказание результатов на облаке тестовых точек

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

Сначала прочтите PCD- файла и преобразуйте облако точек в пятиканальный вход изображение. Спрогнозируйте метки с помощью обученной сети. Отобразите рисунок с сегментацией как наложение.

ptCloud = pcread('ousterLidarDrivingData.pcd');
I = helperPointCloudToImage(ptCloud);
predictedResult = semanticseg(I, net);

figure;
helperDisplayLidarOverlayImage(I, predictedResult, classNames);
title('Semantic Segmentation Result');

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

figure;
helperDisplayLidarOverlayPointCloud(ptCloud, predictedResult, numClasses);
view([95.71 24.14])
title('Semantic Segmentation Result on Point Cloud');

Оценка сети

Запуск semanticseg функция на целом наборе тестов для измерения точности сети. Задайте MiniBatchSize значение 8 для уменьшения использования памяти при сегментации изображений. Вы можете увеличить или уменьшить это значение в зависимости от объема памяти графический процессор, имеющейся в вашей системе.

outputLocation = fullfile(tempdir, 'output');
if ~exist(outputLocation,'dir')
    mkdir(outputLocation);
end
pxdsResults = semanticseg(imdsTest, net, ...
                'MiniBatchSize', 8, ...
                'WriteLocation', outputLocation, ...
                'Verbose', false);

The semanticseg функция возвращает результаты сегментации на наборе тестовых данных как PixelLabelDatastore объект. Функция записывает фактические данные о пиксельных метках для каждого тестового изображения в imdsTest объект на диск в расположении, заданном 'WriteLocation' аргумент.

Используйте evaluateSemanticSegmentation (Computer Vision Toolbox), чтобы вычислить метрики семантической сегментации из результатов тестового набора.

metrics = evaluateSemanticSegmentation(pxdsResults, pxdsTest, 'Verbose', false);

Можно измерить сумму перекрытия для класса с помощью метрики «перекресток по соединению» (IoU).

The evaluateSemanticSegmentation функция возвращает метрики для всего набора данных, для отдельных классов и для каждого тестового изображения. Чтобы увидеть метрики на уровне набора данных, используйте metrics.DataSetMetrics свойство.

metrics.DataSetMetrics
ans=1×5 table
    GlobalAccuracy    MeanAccuracy    MeanIoU    WeightedIoU    MeanBFScore
    ______________    ____________    _______    ___________    ___________

       0.99209          0.83752       0.67895      0.98685        0.91654  

Метрики набора данных обеспечивают высокоуровневый обзор эффективности сети. Чтобы увидеть влияние каждого класса на общую эффективность, смотрите метрики для каждого класса, используя metrics.ClassMetrics свойство.

metrics.ClassMetrics
ans=3×3 table
                  Accuracy      IoU      MeanBFScore
                  ________    _______    ___________

    background    0.99466     0.99212      0.98529  
    car           0.75977     0.50096      0.82682  
    truck         0.75814     0.54378      0.77119  

Несмотря на то, что общая эффективность сети хороша, метрики класса показывают, что смещенные классы (автомобиль и грузовик) не сегментируются, как и классы с обильными данными (фон). Можно улучшить эффективность сети путем обучения сети на более маркированных данных, содержащих классы автомобилей и грузовиков.

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

Функция для увеличения данных

The augmentData функция случайным образом разворачивает 2-D сферическое изображение и связанные с ним метки в горизонтальном направлении.

function out = augmentData(inp)
%augmentData Apply random horizontal flipping.

out = cell(size(inp));

% Randomly flip the five-channel image and pixel labels horizontally.
I = inp{1};
sz = size(I);
tform = randomAffine2d('XReflection',true);
rout = affineOutputView(sz,tform,'BoundsStyle','centerOutput');

out{1} = imwarp(I,tform,'OutputView',rout);
out{2} = imwarp(inp{2},tform,'OutputView',rout);
end

Функция для отображения карты сегментации лидара, наложенной на 2-D сферическое изображение

The helperDisplayLidarOverlayImage функция накладывает карту семантической сегментации на канал интенсивности 2-D сферического изображения. Функция также изменяет размер наложенного изображения для лучшей визуализации.

function helperDisplayLidarOverlayImage(lidarImage, labelMap, classNames)
%helperDisplayLidarOverlayImage Overlay labels over the intensity image. 
% 
%  helperDisplayLidarOverlayImage(lidarImage, labelMap, classNames) 
%  displays the overlaid image. lidarImage is a five-channel lidar input. 
%  labelMap contains pixel labels and classNames is an array of label 
%  names.

% Read the intensity channel from the lidar image.
intensityChannel = uint8(lidarImage(:,:,4));

% Load the lidar color map.
cmap = helperLidarColorMap();

% Overlay the labels over the intensity image.
B = labeloverlay(intensityChannel,labelMap,'Colormap',cmap,'Transparency',0.4);

% Resize for better visualization.
B = imresize(B, 'Scale', [3 1], 'method', 'nearest');
imshow(B);

% Display the color bar.
helperPixelLabelColorbar(cmap, classNames); 
end

Функция для отображения карты сегментации лидара, наложенной на 3-D Облако Точек

The helperDisplayLidarOverPointCloud функция накладывает результат сегментации на 3-D организованное облако точек.

function helperDisplayLidarOverlayPointCloud(ptCloud, labelMap, numClasses)
%helperDisplayLidarOverlayPointCloud Overlay labels over a point cloud object. 
% 
%  helperDisplayLidarOverlayPointCloud(ptCloud, labelMap, numClasses)
%  displays the overlaid pointCloud object. ptCloud is the organized
%  3-D point cloud input. labelMap contains pixel labels and numClasses
%  is the number of predicted classes.

sz = size(labelMap);

% Apply the color red to cars.
carClassCar = zeros(sz(1), sz(2), numClasses, 'uint8');
carClassCar(:,:,1) = 255*ones(sz(1), sz(2), 'uint8');

% Apply the color blue to trucks.
truckClassColor = zeros(sz(1), sz(2), numClasses, 'uint8');
truckClassColor(:,:,3) = 255*ones(sz(1), sz(2), 'uint8');

% Apply the color gray to the background.
backgroundClassColor = 153*ones(sz(1), sz(2), numClasses, 'uint8');

% Extract indices from the labels.
carIndices = labelMap == 'car';
truckIndices = labelMap == 'truck';
backgroundIndices = labelMap == 'background';

% Extract a point cloud for each class.
carPointCloud = select(ptCloud, carIndices, 'OutputSize','full');
truckPointCloud = select(ptCloud, truckIndices, 'OutputSize','full');
backgroundPointCloud = select(ptCloud, backgroundIndices, 'OutputSize','full');

% Apply colors to different classes.
carPointCloud.Color = carClassCar;
truckPointCloud.Color = truckClassColor;
backgroundPointCloud.Color = backgroundClassColor;

% Merge and add all the processed point clouds with class information.
coloredCloud = pcmerge(carPointCloud, truckPointCloud, 0.01);
coloredCloud = pcmerge(coloredCloud, backgroundPointCloud, 0.01);

% Plot the colored point cloud. Set an ROI for better visualization.
ax = pcshow(coloredCloud);
set(ax,'XLim',[-35.0 35.0],'YLim',[-32.0 32.0],'ZLim',[-3 8], ...
    'XColor','none','YColor','none','ZColor','none');
set(get(ax,'parent'), 'units','normalized');
end

Функция для определения Палитры Лидара

The helperLidarColorMap функция задает палитру, используемую набором данных лидара.

function cmap = helperLidarColorMap()

cmap = [
   0.00  0.00   0.00  % background
   0.98  0.00   0.00  % car
   0.00  0.00   0.98  % truck
   ];
end

Функция для отображения пиксельных Шкал палитры о метке

The helperPixelLabelColorbar функция добавляет шкалу палитры к текущей оси. Шкала палитры форматирована, чтобы отображать имена классов с цветом.

function helperPixelLabelColorbar(cmap, classNames)

colormap(gca, cmap);

% Add a colorbar to the current figure.
c = colorbar('peer', gca);

% Use class names for tick marks.
c.TickLabels = classNames;
numClasses = size(classNames, 1);

% Center tick labels.
c.Ticks = 1/(numClasses * 2):1/numClasses:1;

% Remove tick marks.
c.TickLength = 0;
end

Ссылки

[1] Ван, Юань, Тяньюэ Ши, Пэн Юн, Лэй Тай и Мин Лю. PointSeg: семантическая сегментация в реальном времени на основе 3D LiDAR Point Cloud ". ArXiv:1807.06288 [Cs], 25 сентября 2018 года. http://arxiv.org/abs/1807.06288.

Для просмотра документации необходимо авторизоваться на сайте