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

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

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

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

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

Выполните этот код, чтобы загрузить магистральный набор данных сцен. Набор данных содержит 1 617 облаков точек, сохраненных как 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 переменная к истине.

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 поддерживание функции, присоединенной к этому примеру, чтобы сгенерировать обучающие данные от облаков точек лидара. Функция использует облако точек и данные об ограничительной рамке, чтобы создать входные изображения с пятью каналами и пиксельные изображения метки. Чтобы создать пиксельные изображения метки, функция выбирает точки в ограничительной рамке и помечает их ID класса ограничительной рамки. Каждое учебное изображение задано как 64 1 024 5 массивами:

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

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

  • Каждое изображение имеет 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®, пока обработка не завершена.

Создайте ImageDatastore и PixelLabelDatastore

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

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

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

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 функция, заданная в разделе Supporting 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 функция, заданная в разделе Supporting Functions этого примера. Эта функция случайным образом инвертирует сферическое 2D изображение и сопоставленные метки в горизонтальном направлении. Примените увеличение данных только к обучающему набору данных.

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

Сбалансируйте классы Используя взвешивание класса

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

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

Архитектура сети Define

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

inputSize = [64 1024 5];

lgraph = createPointSeg(inputSize, classNames, classWeights);

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

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 (Deep Learning Toolbox) функция, чтобы обучить сеть PointSeg, если doTraining true. В противном случае загрузите предварительно обученную сеть.

Если вы обучаете сеть, можно использовать центральный процессор или графический процессор. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.0 или выше.

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 функция помощника, заданная в разделе Supporting Functions этого примера, чтобы отобразить результат сегментации по 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);

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

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

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

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

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  

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

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

Функция, чтобы увеличить данные

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

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

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

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

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 облаке точек

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

Функция, чтобы задать палитру лидара

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

Функция, чтобы отобразить пиксельную шкалу палитры метки

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”. ArXiv:1807.06288 [Cs], 25 сентября 2018. http://arxiv.org/abs/1807.06288.