В этом примере показано, как обучить сеть семантической сегментации SqueezeSegV2 на 3-D организованных данных об облаке точек лидара.
SqueezeSegV2 [1] является сверточной нейронной сетью (CNN) для выполнения сквозной семантической сегментации дорожных объектов на основе организованного облака точек лидара. Метод обучения, показанный в этом примере, требует 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
переменная к true
.
doTraining = false; if ~doTraining && ~exist('trainedSqueezeSegV2Net.mat','file') disp('Downloading pretrained network (2 MB)...'); pretrainedURL = 'https://www.mathworks.com/supportfiles/lidar/data/trainedSqueezeSegV2Net.mat'; websave('trainedSqueezeSegV2Net.mat', pretrainedURL); end
Используйте helperGenerateLidarData
поддерживание функции, присоединенной к этому примеру, чтобы сгенерировать обучающие данные от облаков точек лидара. Функция использует облако точек и данные об ограничительной рамке, чтобы создать входные изображения с пятью каналами и пиксельные изображения метки. Чтобы создать пиксельные изображения метки, функция выбирает точки в ограничительной рамке и помечает их ID класса ограничительной рамки. Каждое учебное изображение задано как 64 1 024 5 массивами:
Высота каждого изображения составляет 64 пикселя,
Ширина каждого изображения составляет 1 024 пикселя.
Каждое изображение имеет пять каналов. Пять каналов задают 3-D координаты облака точек, интенсивности и области значений: .
Визуальное представление обучающих данных следует.
Сгенерируйте учебные изображения с пятью каналами и пиксельные изображения метки.
imagesFolder = fullfile(outputFolder, 'images'); labelsFolder = fullfile(outputFolder, 'labels'); helperGenerateLidarData(lidarData, groundTruthData, imagesFolder, labelsFolder);
Preprocessing data 100% 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 = 228; % Point cloud (channels 1, 2, and 3 are for location, and 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));
Создайте стандартную сеть SqueezeSegV2 [1] при помощи squeezesegv2Layers
функция. В сети SqueezeSegV2 подсеть энкодера состоит из FireModules, вкрапленного макс. объединяющими слоями. Это расположение последовательно уменьшает разрешение входного изображения. Кроме того, сеть SqueezeSegV2 использует фокальную функцию потерь, чтобы смягчить последствие неустойчивого распределения класса на сетевой точности. Для получения дополнительной информации о том, как использовать фокальную функцию потерь в семантической сегментации, смотрите focalLossLayer
.
Выполните этот код, чтобы создать график слоев, который может использоваться, чтобы обучить сеть.
inputSize = [64 1024 5]; lgraph = squeezesegv2Layers(inputSize, numClasses);
Используйте analyzeNetwork
(Deep Learning Toolbox) функция, чтобы отобразить интерактивную визуализацию сетевой архитектуры.
analyzeNetwork(lgraph)
Используйте rmsprop
алгоритм оптимизации, чтобы обучить сеть. Используйте trainingOptions
функция, чтобы задать гиперпараметры для rmsprop
.
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
к использованию управляющей памяти, когда обучение.
doTraining
аргумент установлен в false
(значение по умолчанию), чтобы загрузить предварительно обученную сеть. Можно обучить сеть сами путем установки doTraining
аргумент к true
. Если вы обучаете сеть, можно использовать центральный процессор или графический процессор. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.0 или выше..
if doTraining [net, info] = trainNetwork(trainingData, lgraph, options); else pretrainedNetwork = 'trainedSqueezeSegV2Net.mat'; load(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 этого примера, чтобы отобразить результат сегментации на 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.99376 0.71176 0.63813 0.98912 0.90017
Метрики набора данных предоставляют общий обзор производительности сети. Чтобы видеть удар, каждый класс имеет на общей производительности, смотрите метрики для каждого класса с помощью metrics.ClassMetrics
свойство.
metrics.ClassMetrics
ans=3×3 table
Accuracy IoU MeanBFScore
________ _______ ___________
background 0.99756 0.99463 0.98424
car 0.71507 0.52999 0.77356
truck 0.42264 0.38977 0.75395
Несмотря на то, что полная производительность сети хороша, метрики класса указывают, что можно улучшать производительность сети путем обучения сети на большем количестве маркированных данных, содержащих классы автомобиля и грузовика.
augmentData
функционируйте случайным образом инвертирует сферическое изображение и сопоставленные метки в горизонтальном направлении.
function out = augmentData(inp) % 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
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); helperPixelLabelColorbar(cmap, classNames); end
helperDisplayLidarOverPointCloud
функционируйте накладывает результат сегментации по 3-D организованному облаку точек.
function helperDisplayLidarOverlayPointCloud(ptCloud, labelMap, numClasses) %helperDisplayLidarOverlayPointCloud Overlay labels over 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 of different classes. 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.0 8.0], ... '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] Ву, Bichen, Сюаньюй Чжоу, Сычэн Чжао, Сянюй Юэ и Курт Койцер. “SqueezeSegV2: Улучшенная Структура модели и Безнадзорная Доменная Адаптация к Дорожно-объектной Сегментации от Облака точек LiDAR”. На 2 019 Международных конференциях по вопросам Робототехники и Автоматизации (ICRA), 4376–82. Монреаль, Qc, Канада: IEEE, 2019.https://doi.org/10.1109/ICRA.2019.8793495.