exponenta event banner

Семантическая сегментация Lidar Point Cloud с использованием Deep Learning Network

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

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

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

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

Этот код используется для загрузки набора данных сцены магистрали. Набор данных содержит 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 ® до завершения обработки .

Создать ImageDatastore и PixelLabelDatastore

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

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

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

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 , определенной в разделе «Вспомогательные функции» данного примера.

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

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

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

Создайте сеть, используя 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), позволяющая обучать сеть, если doTraining является true. В противном случае загрузите предварительно подготовленную сеть.

При обучении сети можно использовать ЦП или графический процессор. Для использования графического процессора требуются параллельные вычислительные Toolbox™ и графический процессор NVIDIA ® с поддержкой CUDA ®. Дополнительные сведения см. в разделе Поддержка графического процессора по выпуску (Панель инструментов параллельных вычислений).

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

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 функция случайным образом разворачивает 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-м сферическом изображении

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

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

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

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

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.