Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

SqueezeSegV2 深層学習ネットワークを使用した LiDAR 点群のセマンティック セグメンテーション

この例では、オーガナイズド 3 次元 LiDAR 点群データで SqueezeSegV2 セマンティック セグメンテーション ネットワークに学習させる方法を示します。

SqueezeSegV2 [1] は、オーガナイズド LiDAR 点群に対してエンドツーエンドのセマンティック セグメンテーションを実行するための畳み込みニューラル ネットワーク (CNN) です。この例に示す学習手順では、深層学習ネットワークへの入力として 2 次元球面投影イメージが必要です。

この例では、Hesai and Scale の PandaSet データ セット [2] を使用します。PandaSet には、Pandar 64 センサーを使用して取得された、都市のさまざまなシーンに関するアンオーガナイズド LiDAR 点群スキャンが 4800 個格納されています。このデータ セットでは、42 個の異なるクラス (自動車、道路、歩行者など) のセマンティック セグメンテーション ラベルが用意されています。

LiDAR データ セットのダウンロード

この例では、PandaSet のサブセットを使用します。このサブセットには、前処理済みでオーガナイズド点群が 2560 個格納されています。各点群は、64 行 1856 列の行列として指定されます。対応するグラウンド トゥルースには、12 個のクラスのセマンティック セグメンテーション ラベルが含まれています。この点群は PCD 形式で保存されており、グラウンド トゥルース データは PNG 形式で保存されています。このデータ セットのサイズは 5.2 GB です。このコードを実行してデータ セットをダウンロードします。

url = "https://ssd.mathworks.com/supportfiles/lidar/data/Pandaset_LidarData.tar.gz";
outputFolder = fullfile(tempdir,"Pandaset");
lidarDataTarFile = fullfile(outputFolder,"Pandaset_LidarData.tar.gz");
if ~exist(lidarDataTarFile,"file")
    mkdir(outputFolder);
    disp("Downloading Pandaset Lidar driving data (5.2 GB)...");
    websave(lidarDataTarFile,url);
    untar(lidarDataTarFile,outputFolder);
end
% Check if tar.gz file is downloaded, but not uncompressed.
if (~exist(fullfile(outputFolder,"Lidar"),"file"))...
        &&(~exist(fullfile(outputFolder,"semanticLabels"),"file"))
    untar(lidarDataTarFile,outputFolder);
end
lidarData =  fullfile(outputFolder,"Lidar");
labelsFolder = fullfile(outputFolder,"semanticLabels");

インターネット接続の速度によっては、ダウンロード プロセスに時間がかかることがあります。このコードは、ダウンロード プロセスが完了するまで、MATLAB® の実行を一時停止します。または、Web ブラウザーを使用してデータ セットをローカル ディスクにダウンロードした後、Pandaset_LidarData フォルダーを抽出することもできます。Pandaset_LidarData には、点群の情報をもつ Lidar フォルダー、直方体ラベルの情報をもつ Cuboids フォルダー、およびセマンティック ラベルの情報をもつ semanticLabels フォルダーがそれぞれ含まれています。Web からダウンロードしたファイルを使用するには、コード内の変数 outputFolder をダウンロードしたファイルの場所に変更します。

この例の学習手順は、オーガナイズド点群に関するものです。アンオーガナイズド点群をオーガナイズド点群に変換する方法を示す例については、球面投影の使用による点群のアンオーガナイズドからオーガナイズドへの変換 (Lidar Toolbox)を参照してください。

事前学習済みのネットワークのダウンロード

学習の完了を待たなくて済むように、事前学習済みのネットワークをダウンロードします。ネットワークに学習させる場合は、変数 doTrainingtrue に設定します。

doTraining = false;
pretrainedNetURL = ...
"https://ssd.mathworks.com/supportfiles/lidar/data/trainedSqueezeSegV2PandasetNet.zip";
if ~doTraining
    downloadPretrainedSqueezeSegV2Net(outputFolder,pretrainedNetURL);
end
Downloading pretrained model (5 MB)...

学習用データの準備

LiDAR 点群とクラス ラベルの読み込み

この例に添付されているサポート関数 helperTransformOrganizedPointCloudToTrainingData を使用して、LiDAR 点群から学習データを生成します。この関数は、点群を使用して 5 チャネルの入力イメージを作成します。各学習イメージは、次の 64 x 1856 x 5 の配列として指定されます。

  • 各イメージの高さは 64 ピクセル。

  • 各イメージの幅は 1856 ピクセル。

  • 各イメージには 5 つのチャネルがあります。5 つのチャネルは、点群の 3 次元座標、強度、および範囲を r=x2 +y2+z2 で指定します。

学習データの視覚的表現は次のようになります。

Grouped.png

5 チャネルの学習イメージを生成します。

imagesFolder = fullfile(outputFolder,"images");
helperTransformOrganizedPointCloudToTrainingData(lidarData,imagesFolder);
Preprocessing data 100% complete

5 チャネルのイメージは MAT ファイルとして保存されます。

処理には時間がかかる場合があります。このコードは、処理が完了するまで MATLAB® の実行を一時停止します。

imageDatastore と pixelLabelDatastore の作成

imageDatastoreを作成し、カスタム MAT ファイル リーダーであるサポート関数 imageDatastore と helperImageMatReader を使用して、2 次元球面イメージの 5 つのチャネルを抽出して保存します。関数は、サポート ファイルとしてこの例に添付されています。

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

pixelLabelDatastore (Computer Vision Toolbox)を使用してピクセル ラベル データストアを作成し、ピクセル ラベル イメージからピクセル単位のラベルを保存します。オブジェクトは、各ピクセル ラベルをクラス名にマッピングします。この例では、植生、地面、道路、路面標示、歩道、自動車、トラック、その他の車両、歩行者、ガードレール、標識、および建物を対象オブジェクトとし、他のすべてのピクセルは背景とします。それらのクラスを指定し、各クラスに一意のラベル ID を割り当てます。

classNames = ["unlabelled"
              "Vegetation"
              "Ground"
              "Road"
              "RoadMarkings"
              "SideWalk"
              "Car"
              "Truck"
              "OtherVehicle"
              "Pedestrian"
              "RoadBarriers"
              "Signs"
              "Buildings"];
numClasses = numel(classNames);
% Specify label IDs from 1 to the number of classes.
labelIDs = 1 : numClasses;
pxds = pixelLabelDatastore(labelsFolder,classNames,labelIDs);

この例のサポート関数の節で定義されている関数 helperDisplayLidarOverlaidImage を使用して、対応する強度イメージに重ね合わせることにより、ラベル付けされたイメージの 1 つを読み込んで表示します。

% Point cloud (channels 1, 2, and 3 are for location, channel 4 is for intensity, and channel 5 is for range).
I = read(imds);
labelMap = read(pxds);
figure;
helperDisplayLidarOverlaidImage(I,labelMap{1,1},classNames);
title("Ground Truth");

学習セット、検証セット、およびテスト セットの準備

この例に添付されているサポート関数 helperPartitionLidarSegmentationDataset を使用して、データを学習セット、検証セット、テスト セットに分割します。trainingDataPercentage で指定された割合に従って、学習データを分割できます。残りのデータを 2:1 の比率で検証データとテスト データに分割します。trainingDataPercentage の既定値は 0.7 です。

[imdsTrain,imdsVal,imdsTest,pxdsTrain,pxdsVal,pxdsTest] = ...
helperPartitionLidarSegmentationDataset(imds,pxds,"trainingDataPercentage",0.75);

関数combineを使用して、学習データおよび検証データのピクセル ラベル データストアとイメージ データストアを結合します。

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

データ拡張

データ拡張は、学習中に元のデータをランダムに変換してネットワークの精度を高めるために使用されます。データ拡張を使用すると、ラベル付き学習サンプルの数を実際に増やさずに、学習データをさらに多様化させることができます。

この例のサポート関数の節で定義されている関数 helperAugmentData でカスタム前処理操作を指定し、関数transformを使用して学習データを拡張します。この関数は、マルチチャネル 2 次元イメージと関連するラベルを水平方向にランダムに反転します。データ拡張を学習データ セットにのみ適用します。

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

ネットワーク アーキテクチャの定義

関数squeezesegv2Layers (Lidar Toolbox)を使用して、標準の SqueezeSegV2 [1] ネットワークを作成します。SqueezeSegV2 ネットワークでは、符号化器サブネットワークが、最大プーリング層が点在する Fire モジュールで構成されます。この配置では、入力イメージの解像度を逐次下げていきます。さらに、SqueezeSegV2 ネットワークは、"焦点損失" 関数を使用して、不均衡なクラス分布がネットワークの精度に及ぼす影響を軽減します。セマンティック セグメンテーションで焦点損失関数を使用する方法の詳細については、focalLossLayer (Computer Vision Toolbox)を参照してください。

次のコードを実行して、ネットワークに学習させるために使用できる層グラフを作成します。

inputSize = [64 1856 5];
lgraph = squeezesegv2Layers(inputSize, ...
numClasses,"NumEncoderModules",4,"NumContextAggregationModules",2);

関数 analyzeNetwork を使用して、ネットワーク アーキテクチャを対話的に可視化して表示します。

analyzeNetwork(lgraph);

学習オプションの指定

Adam 最適化アルゴリズムを使用してネットワークに学習させます。関数trainingOptionsを使用して、ハイパーパラメーターを指定します。

maxEpochs = 30;
initialLearningRate = 1e-3;
miniBatchSize = 8;
l2reg = 2e-4;
options = trainingOptions("adam", ...
    "InitialLearnRate",initialLearningRate, ...
    "L2Regularization",l2reg, ...
    "MaxEpochs",maxEpochs, ...
    "MiniBatchSize",miniBatchSize, ...
    "LearnRateSchedule","piecewise", ...
    "LearnRateDropFactor",0.1, ...
    "LearnRateDropPeriod",10, ...
    "ValidationData",validationData, ...
    "Plots","training-progress", ...
    "VerboseFrequency",20);

メモ: miniBatchSize の値を小さくして、学習時のメモリ使用量を制御します。

ネットワークの学習

doTraining 引数を true に設定することで、手動でネットワークに学習させることができます。ネットワークに学習させるのに CPU または GPU を使用できます。GPU を使用するには、Parallel Computing Toolbox™ とサポートされている GPU デバイスが必要です。サポートされているデバイスについては、GPU 計算の要件 (Parallel Computing Toolbox)を参照してください。そうでない場合は、事前学習済みのネットワークを読み込みます。

if doTraining
    [net,info] = trainNetwork(trainingData,lgraph,options);
else
    load(fullfile(outputFolder,"trainedSqueezeSegV2PandasetNet.mat"),"net");
end

テスト点群での結果の予測

学習済みのネットワークを使用して、テスト点群での結果を予測し、セグメンテーション結果を表示します。まず、5 チャネルの入力イメージを読み取り、学習済みのネットワークを使用してラベルを予測します。

セグメンテーションを重ね合わせて Figure を表示します。

I = read(imdsTest);
predictedResult = semanticseg(I,net);
figure;
helperDisplayLidarOverlaidImage(I,predictedResult,classNames);
title("Semantic Segmentation Result");

この例のサポート関数の節で定義されている関数 helperDisplayLabelOverlaidPointCloud を使用して、点群にセグメンテーション結果を表示します。

figure;
helperDisplayLabelOverlaidPointCloud(I,predictedResult);                          
view([39.2 90.0 60]);
title("Semantic Segmentation Result on Point Cloud");

ネットワークの評価

関数evaluateSemanticSegmentation (Computer Vision Toolbox)を使用して、テスト セットの結果からセマンティック セグメンテーション メトリクスを計算します。

outputLocation = fullfile(tempdir,"output");
if ~exist(outputLocation,"dir")
    mkdir(outputLocation);
end
pxdsResults = semanticseg(imdsTest,net, ...
    "MiniBatchSize",4, ...
    "WriteLocation",outputLocation, ...
    "Verbose",false);
metrics = evaluateSemanticSegmentation(pxdsResults,pxdsTest,"Verbose",false);

Intersection over Union (IoU) メトリクスを使用して、クラスごとのオーバーラップの量を測定できます。

関数evaluateSemanticSegmentation (Computer Vision Toolbox)は、データ セット全体、個々のクラス、および各テスト イメージのメトリクスを返します。データ セット レベルでメトリクスを表示するには、metrics.DataSetMetrics プロパティを使用します。

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

       0.89724          0.61685       0.54431      0.81806        0.74537  

データ セット メトリクスは、ネットワーク パフォーマンスの概要を提供します。各クラスが全体的なパフォーマンスに与える影響を確認するには、metrics.ClassMetrics プロパティを使用して各クラスのメトリクスを検査します。

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

    unlabelled         0.94      0.9005      0.99911  
    Vegetation      0.77873     0.64819      0.95466  
    Ground          0.69019     0.59089      0.60657  
    Road            0.94045     0.83663      0.99084  
    RoadMarkings    0.37802     0.34149      0.77073  
    SideWalk         0.7874     0.65668      0.93687  
    Car              0.9334     0.81065      0.95448  
    Truck           0.30352     0.27401      0.37273  
    OtherVehicle    0.64397     0.58108      0.47253  
    Pedestrian      0.26214     0.20896      0.45918  
    RoadBarriers    0.23955     0.21971      0.19433  
    Signs           0.17276     0.15613      0.44275  
    Buildings       0.94891     0.85117      0.96929  

全体的なネットワークのパフォーマンスは良好ですが、RoadMarkingsTruck などのクラスのクラス メトリクスは、パフォーマンスを改善するため学習データがさらに必要であることを示しています。

サポート関数

データを拡張する関数

関数 helperAugmentData は、球面イメージと関連するラベルを水平方向にランダムに反転します。

function out = helperAugmentData(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

2 次元球面イメージに LiDAR セグメンテーション マップを重ねて表示する関数

関数 helperDisplayLidarOverlaidImage は、セマンティック セグメンテーション マップを 2 次元球面イメージの強度チャネルに重ね合わせます。この関数は、より見やすくなるように、重ね合わされたイメージのサイズも変更します。

function helperDisplayLidarOverlaidImage(lidarImage,labelMap,classNames)
%  helperDisplayLidarOverlaidImage(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 = helperPandasetColorMap;
% 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

3 次元点群に LiDAR セグメンテーション マップを重ねて表示する関数

関数 helperDisplayLabelOverlaidPointCloud は、オーガナイズド 3 次元点群にセグメンテーション結果を重ね合わせます。

function helperDisplayLabelOverlaidPointCloud(I,predictedResult)
%  helperDisplayLabelOverlaidPointCloud(I, predictedResult)
%  displays the overlaid pointCloud object. I is the 5 channels organized
%  input image. predictedResult contains pixel labels.
ptCloud = pointCloud(I(:,:,1:3),"Intensity",I(:,:,4));
cmap = helperPandasetColorMap;
B = ...
labeloverlay(uint8(ptCloud.Intensity),predictedResult,"Colormap",cmap,"Transparency",0.4);
pc = pointCloud(ptCloud.Location,"Color",B);
figure;
ax = pcshow(pc);
set(ax,"XLim",[-70 70],"YLim",[-70 70]);
zoom(ax,3.5);
end

LiDAR カラーマップを定義する関数

関数 helperPandasetColorMap は、LiDAR データ セットで使用されるカラーマップを定義します。

function cmap = helperPandasetColorMap
cmap = [[30 30 30];      % Unlabeled
        [0 255 0];       % Vegetation
        [255 150 255]; % Ground
        [255 0 255];     % Road
        [255 0 0];       % Road Markings
        [90 30 150];   % Sidewalk
        [245 150 100];   % Car
        [250 80 100];  % Truck
        [150 60 30];   % Other Vehicle
        [255 255 0];   % Pedestrian
        [0 200 255];   % Road Barriers
        [170 100 150];   % Signs
        [30 30 255]];  % Building
cmap = cmap./255;
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

事前学習済みのモデルをダウンロードする関数

関数 downloadPretrainedSqueezeSegV2Net は、事前学習済みのモデルをダウンロードします。

function downloadPretrainedSqueezeSegV2Net(outputFolder,pretrainedNetURL)
    preTrainedMATFile = fullfile(outputFolder,"trainedSqueezeSegV2PandasetNet.mat");
    preTrainedZipFile = fullfile(outputFolder,"trainedSqueezeSegV2PandasetNet.zip");
    
    if ~exist(preTrainedMATFile,"file")
        if ~exist(preTrainedZipFile,"file")
            disp("Downloading pretrained model (5 MB)...");
            websave(preTrainedZipFile,pretrainedNetURL);
        end
        unzip(preTrainedZipFile,outputFolder);   
    end       
end

参考文献

[1] Wu, Bichen, Xuanyu Zhou, Sicheng Zhao, Xiangyu Yue, and Kurt Keutzer. “SqueezeSegV2: Improved Model Structure and Unsupervised Domain Adaptation for Road-Object Segmentation from a LiDAR Point Cloud.” In 2019 International Conference on Robotics and Automation (ICRA), 4376–82. Montreal, QC, Canada: IEEE, 2019.https://doi.org/10.1109/ICRA.2019.8793495.

[2] Hesai and Scale.PandaSet. https://scale.com/open-datasets/pandaset