Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

PointPillars 深層学習を使用した 3 次元 LIDAR オブジェクトの検出

この例では、点群でオブジェクトを検出するために PointPillars ネットワークに学習させる方法を示します。

LIDAR 点群データは、Velodyne®、Pandar、Ouster といったさまざまな LIDAR センサーで取得できます。これらのセンサーは、シーン内にあるオブジェクトの 3 次元位置情報を取得します。この情報は、自動運転や拡張現実におけるさまざまなアプリケーションで役立ちます。しかし、オブジェクト当たりのデータのスパース性、オブジェクトのオクルージョン、センサーのノイズの問題があるため、点群データを使用してロバストな検出器に学習させることは困難です。これらのさまざまな課題に対処するため、点群データからロバストな特徴表現を直接学習する深層学習手法が提案されています。3 次元オブジェクト検出の深層学習手法の 1 つは、PointPillars [1] です。PointNet と同様のアーキテクチャを使用して、PointPillars ネットワークは、柱と呼ばれるスパース点群から高密度でロバストな特徴を抽出し、変更された SSD オブジェクト検出ネットワークと共に 2 次元深層学習ネットワークを使用して、ジョイント 3 次元境界ボックス、方向、およびクラス予測を推定します。

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

この例では、PandaSet [2] のサブセットを使用します。このサブセットには、前処理済みの整理された点群が 2560 個格納されています。各点群は、360o の視界をカバーし、64 行 1,856 列の行列として指定されます。この点群は PCD 形式で保存されており、対応するグラウンド トゥルース データは PandaSetLidarGroundTruth.mat ファイルに保存されています。このファイルには、3 つのクラス (自動車、トラック、歩行者) の 3 次元境界ボックスの情報が格納されています。このデータ セットのサイズは 5.2 GB です。

この例の終わりに定義されている補助関数 helperDownloadPandasetData を使用して、指定された URL から PandaSet データセットをダウンロードします。

doTraining = false;

outputFolder = fullfile(tempdir,'Pandaset');

lidarURL = ['https://ssd.mathworks.com/supportfiles/lidar/data/' ...
    'Pandaset_LidarData.tar.gz'];
helperDownloadPandasetData(outputFolder,lidarURL);

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

データの読み込み

関数pcread (Computer Vision Toolbox)を使用して、指定されたパスから PCD ファイルを読み込むためのファイル データストアを作成します。

path = fullfile(outputFolder,'Lidar');
lidarData = fileDatastore(path,'ReadFcn',@(x) pcread(x));

自動車とトラックのオブジェクトの 3 次元境界ボックス ラベルを読み込みます。

gtPath = fullfile(outputFolder,'Cuboids','PandaSetLidarGroundTruth.mat');
data = load(gtPath,'lidarGtLabels');
Labels = timetable2table(data.lidarGtLabels);
boxLabels = Labels(:,2:3);

フルビューの点群を表示します。

figure
ptCld = read(lidarData);
ax = pcshow(ptCld.Location);
set(ax,'XLim',[-50 50],'YLim',[-40 40]);
zoom(ax,2.5);
axis off;

reset(lidarData);

データの前処理

PandaSet データは、フルビューの点群で構成されています。この例では、標準パラメーター [1] を使用して、フルビューの点群をフロントビューの点群にトリミングします。これらのパラメーターは、ネットワークに渡される入力のサイズを決定します。x、y、z 軸に沿ってより狭い点群の範囲を選択して、原点に近いオブジェクトを検出します。これによって、ネットワークの全体的な学習時間も短縮されます。

xMin = 0.0;     % Minimum value along X-axis.
yMin = -39.68;  % Minimum value along Y-axis.
zMin = -5.0;    % Minimum value along Z-axis.
xMax = 69.12;   % Maximum value along X-axis.
yMax = 39.68;   % Maximum value along Y-axis.
zMax = 5.0;     % Maximum value along Z-axis.
xStep = 0.16;   % Resolution along X-axis.
yStep = 0.16;   % Resolution along Y-axis.
dsFactor = 2.0; % Downsampling factor.

% Calculate the dimensions for the pseudo-image.
Xn = round(((xMax - xMin)/xStep));
Yn = round(((yMax - yMin)/yStep));

% Define point cloud parameters.
pointCloudRange = [xMin xMax yMin yMax zMin zMax];
voxelSize = [xStep yStep];

この例にサポート ファイルとして添付されている補助関数 cropFrontViewFromLidarData を使用して、次のようにします。

  • 入力のフルビューの点群をトリミングしてフロント ビューを作成。

  • gridParams で指定された ROI の内側にあるボックス ラベルを選択。

[croppedPointCloudObj,processedLabels] = cropFrontViewFromLidarData(...
    lidarData,boxLabels,pointCloudRange);
Processing data 100% complete

この例の終わりに定義されている補助関数 helperDisplay3DBoxesOverlaidPointCloud を使用して、トリミングされた点群とグラウンド トゥルース ボックス ラベルを表示します。

pc = croppedPointCloudObj{1,1};
gtLabelsCar = processedLabels.Car{1};
gtLabelsTruck = processedLabels.Truck{1};

helperDisplay3DBoxesOverlaidPointCloud(pc.Location,gtLabelsCar,...
   'green',gtLabelsTruck,'magenta','Cropped Point Cloud');

reset(lidarData);

学習用のデータストア オブジェクトの作成

データ セットを学習セットとテスト セットに分割します。データの 70% をネットワークの学習用に選択し、残りを評価用に選択します。

rng(1);
shuffledIndices = randperm(size(processedLabels,1));
idx = floor(0.7 * length(shuffledIndices));

trainData = croppedPointCloudObj(shuffledIndices(1:idx),:);
testData = croppedPointCloudObj(shuffledIndices(idx+1:end),:);

trainLabels = processedLabels(shuffledIndices(1:idx),:);
testLabels = processedLabels(shuffledIndices(idx+1:end),:);

データストアに容易にアクセスできるように、この例にサポート ファイルとして添付されている補助関数 saveptCldToPCD を使用して、学習データを PCD ファイルとして保存します。学習データがフォルダーに保存されており、そのデータが関数pcread (Computer Vision Toolbox)によってサポートされている場合、writeFiles を "false" に設定できます。

writeFiles = true;
dataLocation = fullfile(outputFolder,'InputData');
[trainData,trainLabels] = saveptCldToPCD(trainData,trainLabels,...
    dataLocation,writeFiles);
Processing data 100% complete

fileDatastoreを使用して、関数pcread (Computer Vision Toolbox)を使用して PCD ファイルを読み込むためのファイル データストアを作成します。

lds = fileDatastore(dataLocation,'ReadFcn',@(x) pcread(x));

boxLabelDatastore (Computer Vision Toolbox)を使用して、3 次元境界ボックス ラベルを読み込むためのボックス ラベル データストアを作成します。

bds = boxLabelDatastore(trainLabels);

関数combineを使用して、学習用に点群と 3 次元境界ボックス ラベルを単一のデータストアに統合します。

cds = combine(lds,bds);

データ拡張

この例では、グラウンド トゥルース データ拡張と他のいくつかのグローバル データ拡張手法を使用して、学習データと対応するボックスにさらに多様性を付加します。LIDAR データによる 3 次元オブジェクト検出ワークフローで使用される典型的なデータ拡張手法の詳細については、Data Augmentations for Lidar Object Detection Using Deep Learning (Lidar Toolbox)を参照してください。

この例の終わりに定義されている補助関数 helperDisplay3DBoxesOverlaidPointCloud を使用して、拡張前の点群を読み取って表示します。

augData = read(cds);
augptCld = augData{1,1};
augLabels = augData{1,2};
augClass = augData{1,3};

labelsCar = augLabels(augClass=='Car',:);
labelsTruck = augLabels(augClass=='Truck',:);

helperDisplay3DBoxesOverlaidPointCloud(augptCld.Location,labelsCar,'green',...
    labelsTruck,'magenta','Before Data Augmentation');

reset(cds);

関数 sampleLidarData を使用して、3 次元境界ボックスとそれに対応する点を学習データからサンプリングします。

classNames = {'Car','Truck'};
sampleLocation = fullfile(outputFolder,'GTsamples');
[ldsSampled,bdsSampled] = sampleLidarData(cds,classNames,'MinPoints',20,...                  
                            'Verbose',false,'WriteLocation',sampleLocation);
cdsSampled = combine(ldsSampled,bdsSampled);

関数 pcBboxOversample を使用して、固定数の自動車クラスとトラック クラスのオブジェクトをすべての点群にランダムに追加します。関数transformを使用して、グラウンド トゥルース データとカスタム データの拡張を学習データに適用します。

numObjects = [10 10];
cdsAugmented = transform(cds,@(x)pcBboxOversample(x,cdsSampled,classNames,numObjects));

以下の追加のデータ拡張手法をすべての点群に適用します。

  • x 軸に沿ってランダムに反転

  • 5% のランダムなスケーリング

  • [-pi/4, pi/4] の範囲で z 軸に沿ってランダムに回転

  • x 軸、y 軸、z 軸に沿って、それぞれ [0.2, 0.2, 0.1] メートルずつランダムに平行移動

cdsAugmented = transform(cdsAugmented,@(x)augmentData(x));

この例の終わりに定義されている補助関数 helperDisplay3DBoxesOverlaidPointCloud を使用して、拡張された点群と拡張されたグラウンド トゥルース ボックスを表示します。

augData = read(cdsAugmented);
augptCld = augData{1,1};
augLabels = augData{1,2};
augClass = augData{1,3};

labelsCar = augLabels(augClass=='Car',:);
labelsTruck = augLabels(augClass=='Truck',:);

helperDisplay3DBoxesOverlaidPointCloud(augptCld.Location,labelsCar,'green',...
    labelsTruck,'magenta','After Data Augmentation');

reset(cdsAugmented);

PointPillars オブジェクト検出器の作成

関数pointPillarsObjectDetector (Lidar Toolbox)を使用して、PointPillars オブジェクト検出ネットワークを作成します。PointPillars ネットワークの詳細については、Getting Started with PointPillars (Lidar Toolbox)を参照してください。

図は、PointPillars オブジェクト検出器のネットワーク アーキテクチャを示しています。PointPillars ネットワークはディープ ネットワーク デザイナーアプリを使用して作成できます。

関数pointPillarsObjectDetector (Lidar Toolbox)では、PointPillars ネットワークをパラメーター化する以下のそれぞれの入力を指定しなければなりません。

  • クラス名

  • アンカー ボックス

  • 点群の範囲

  • ボクセル サイズ

  • 突出する柱の数

  • 柱 1 本あたりの点の数

% Define the number of prominent pillars.
P = 12000; 

% Define the number of points per pillar.
N = 100;   

この例にサポート ファイルとして添付されている補助関数 calculateAnchorsPointPillars を使用して、学習データのアンカー ボックスを推定します。

anchorBoxes = calculateAnchorsPointPillars(trainLabels);
classNames = trainLabels.Properties.VariableNames;

PointPillars 検出器を定義します。

detector = pointPillarsObjectDetector(pointCloudRange,classNames,anchorBoxes,...
    'VoxelSize',voxelSize,'NumPillars',P,'NumPointsPerPillar',N);

PointPillars オブジェクト検出器の学習

関数trainingOptionsを使用して、ネットワーク学習パラメーターを指定します。学習プロセス中に部分的に学習させた検出器を保存できるように、'CheckpointPath' を一時的な場所に設定します。学習が中断された場合に、保存したチェックポイントから学習を再開できます。

CPU または GPU を使用して検出器に学習させます。GPU を使用するには、Parallel Computing Toolbox™、および CUDA® 対応の NVIDIA® GPU が必要です。詳細については、リリース別の GPU サポート (Parallel Computing Toolbox)を参照してください。使用できる GPU が存在するか自動的に検出するには、executionEnvironment"auto" に設定します。GPU がない場合、または学習で GPU を使用しない場合は、executionEnvironment"cpu" に設定します。学習に GPU が使用されるようにするために、executionEnvironment"gpu" に設定します。

executionEnvironment = "auto";
if canUseParallelPool
    dispatchInBackground = true;
else
    dispatchInBackground = false;
end

options = trainingOptions('adam',...
    'Plots',"none",...
    'MaxEpochs',60,...
    'MiniBatchSize',3,...
    'GradientDecayFactor',0.9,...
    'SquaredGradientDecayFactor',0.999,...
    'LearnRateSchedule',"piecewise",...
    'InitialLearnRate',0.0002,...
    'LearnRateDropPeriod',15,...
    'LearnRateDropFactor',0.8,...
    'ExecutionEnvironment',executionEnvironment,...
    'DispatchInBackground',dispatchInBackground,...
    'BatchNormalizationStatistics','moving',...
    'ResetInputNormalization',false,...
    'CheckpointPath',tempdir);

doTraining が "true" の場合、関数trainPointPillarsObjectDetector (Lidar Toolbox)を使用して PointPillars オブジェクト検出器に学習させます。そうでない場合は、事前学習済みの検出器を読み込みます。

if doTraining    
    [detector,info] = trainPointPillarsObjectDetector(cdsAugmented,detector,options);
else
    pretrainedDetector = load('pretrainedPointPillarsDetector.mat','detector');
    detector = pretrainedDetector.detector;
end

検出の生成

学習済みネットワークを使用して、テスト データに含まれるオブジェクトを検出します。

  • テスト データから点群を読み取る。

  • テスト用の点群に対して検出器を実行し、予測された境界ボックスと信頼スコアを取得する。

  • この例の終わりに定義されている補助関数 helperDisplay3DBoxesOverlaidPointCloud を使用して、境界ボックス付きの点群を表示する。

ptCloud = testData{45,1};
gtLabels = testLabels(45,:);

% Specify the confidence threshold to use only detections with
% confidence scores above this value.
confidenceThreshold = 0.5;
[box,score,labels] = detect(detector,ptCloud,'Threshold',confidenceThreshold);

boxlabelsCar = box(labels'=='Car',:);
boxlabelsTruck = box(labels'=='Truck',:);

% Display the predictions on the point cloud.
helperDisplay3DBoxesOverlaidPointCloud(ptCloud.Location,boxlabelsCar,'green',...
    boxlabelsTruck,'magenta','Predicted Bounding Boxes');

テスト セットを使用した検出器の評価

大規模な点群データ セットで学習済みのオブジェクト検出器を評価し、パフォーマンスを測定します。

numInputs = 50;

% Generate rotated rectangles from the cuboid labels.
bds = boxLabelDatastore(testLabels(1:numInputs,:));
groundTruthData = transform(bds,@(x)createRotRect(x));

% Set the threshold values.
nmsPositiveIoUThreshold = 0.5;
confidenceThreshold = 0.25;

detectionResults = detect(detector,testData(1:numInputs,:),...
    'Threshold',confidenceThreshold);

% Convert the bounding boxes to rotated rectangles format and calculate
% the evaluation metrics.
for i = 1:height(detectionResults)
    box = detectionResults.Boxes{i};
    detectionResults.Boxes{i} = box(:,[1,2,4,5,7]);
end

metrics = evaluateDetectionAOS(detectionResults,groundTruthData,...
    nmsPositiveIoUThreshold);
disp(metrics(:,1:2))
               AOS        AP   
             _______    _______

    Car      0.89666    0.89666
    Truck    0.76047    0.76047

補助関数

function helperDownloadPandasetData(outputFolder,lidarURL)
% Download the data set from the given URL to the output folder.

    lidarDataTarFile = fullfile(outputFolder,'Pandaset_LidarData.tar.gz');
    
    if ~exist(lidarDataTarFile,'file')
        mkdir(outputFolder);
        
        disp('Downloading PandaSet Lidar driving data (5.2 GB)...');
        websave(lidarDataTarFile,lidarURL);
        untar(lidarDataTarFile,outputFolder);
    end
    
    % Extract the file.
    if (~exist(fullfile(outputFolder,'Lidar'),'dir'))...
            &&(~exist(fullfile(outputFolder,'Cuboids'),'dir'))
        untar(lidarDataTarFile,outputFolder);
    end

end

function helperDisplay3DBoxesOverlaidPointCloud(ptCld,labelsCar,carColor,...
    labelsTruck,truckColor,titleForFigure)
% Display the point cloud with different colored bounding boxes for different
% classes.
    figure;
    ax = pcshow(ptCld);
    showShape('cuboid',labelsCar,'Parent',ax,'Opacity',0.1,...
        'Color',carColor,'LineWidth',0.5);
    hold on;
    showShape('cuboid',labelsTruck,'Parent',ax,'Opacity',0.1,...
        'Color',truckColor,'LineWidth',0.5);
    title(titleForFigure);
    zoom(ax,1.5);
end

参考文献

[1] Lang, Alex H., Sourabh Vora, Holger Caesar, Lubing Zhou, Jiong Yang, and Oscar Beijbom. "PointPillars: Fast Encoders for Object Detection From Point Clouds." In 2019 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 12689-12697. Long Beach, CA, USA: IEEE, 2019. https://doi.org/10.1109/CVPR.2019.01298.

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