RandLANet 深層学習を使用した航空 LiDAR のセマンティック セグメンテーション
この例では、航空 LiDAR データのセマンティック セグメンテーションを実行するために RandLANet 深層学習ネットワークに学習させる方法を示します。
航空レーザー スキャン システムから取得した LiDAR データは、地形図作成、都市モデリング、バイオマス測定、災害管理などの用途に使用できます。このデータから意味のある情報を抽出するには、点群の各点に一意のクラス ラベルを割り当てるセマンティック セグメンテーションのプロセスが必要です。
この例では、セマンティック セグメンテーションを実行するために、Dayton Annotated Lidar Earth Scan (DALES) データ セット [2] を使用して、RandLANet [1] ネットワークに学習させます。このデータ セットには、都市部、郊外、農村地域、および商業地域のシーンについて、ラベルが付けられた高密度の航空 LiDAR データが含まれています。データ セットには、建物、自動車、トラック、ポール、電力線、フェンス、地面、植生の 8 つのクラスに対するセマンティック セグメンテーション ラベルがあります。
DALES データの読み込み
DALES データ セットには、40 個のシーンの航空 LiDAR データが含まれています。40 個のシーンのうち、29 個のシーンを学習に使用し、残りの 11 個のシーンをテストに使用します。DALES Web サイトの指示に従って、変数 dataFolder
で指定するフォルダーにデータ セットをダウンロードします。学習データとテスト データの保存先のフォルダーを作成します。
dataFolder = fullfile(tempdir,"DALES"); trainDataFolder = fullfile(dataFolder,"dales_las","train"); testDataFolder = fullfile(dataFolder,"dales_las","test");
学習データから点群をプレビューします。
lasReader = lasFileReader(fullfile(trainDataFolder,"5080_54435.las")); [pc,attr] = readPointCloud(lasReader,"Attributes","Classification"); labels = attr.Classification; % Select only labeled data. pc = select(pc,labels~=0); labels = labels(labels~=0); classNames = ["ground"; ... "vegetation"; ... "cars"; ... "trucks"; ... "powerlines"; ... "fences"; ... "poles"; ... "buildings"]; figure ax = pcshow(pc.Location,labels); helperLabelColorbar(ax,classNames) title("Point Cloud with Overlaid Semantic Labels")
学習データの前処理
学習データを前処理するために、まず学習データのすべての点群ファイルを収集するためのfileDatastore
オブジェクトを作成します。
fds = fileDatastore(trainDataFolder,"ReadFcn",@lasFileReader);
DALES データ セットの各点群は、500×500 メートルの領域をカバーし、約 1000 万点を含みます。このような多数の点を処理すると、ネットワーク処理が遅くなることがあります。メモリ処理を効率的にするために、helperTransformData
補助関数 (この例の最後で定義) を使用して、オーバーラップしない 50×50 メートルのサイズの小さなブロックに点群を分割します。
ブロックの寸法を定義します。
blockSize = [50 50];
学習データストアに変換を適用します。
ldsTransformed = transform(fds,@(x) helperTransformData(x,blockSize));
この例にサポート ファイルとして添付されている helperDatastoreWriteFcn
補助関数を使用して、前処理済みの点群とセマンティック ラベルをそれぞれ PCD ファイルと PNG ファイルとして保存します。前処理済みの学習データが outputFolder
に既に存在する場合は、writeFiles
を false
に設定できます。
writeFiles = true; outputFolder = fullfile(dataFolder,"PreprocessedTrainData"); if writeFiles writeall(ldsTransformed,outputFolder,WriteFcn = @helperDatastoreWriteFcn,FolderLayout = "flatten"); end
Processing done for file: 5190_54400.las
メモ: 処理には時間がかかる場合があります。このコードは、処理が完了するまで MATLAB® の実行を一時停止します。
コードを再実行する場合は、まず "clear helperDatastoreWriteFcn"
コマンドを使用する必要があります。
学習用のデータストア オブジェクトの作成
pcread
関数を使用して、PCD ファイルを読み取るfileDatastore
オブジェクトを作成します。
pcCropTrainPath = fullfile(outputFolder,"PointCloud"); ldsTrain = fileDatastore(pcCropTrainPath,"ReadFcn",@(x) pcread(x));
pixelLabelDatastore
オブジェクトを使用して、ピクセル ラベル イメージからピクセル単位のラベルを保存します。オブジェクトは、各ピクセル ラベルをクラス名にマッピングし、各クラスに一意のラベル ID を割り当てます。
% Specify label IDs from 1 to the number of classes. numClasses = numel(classNames); labelIDs = 1 : numClasses; labelCropTrainPath = fullfile(outputFolder,"Labels"); pxdsTrain = pixelLabelDatastore(labelCropTrainPath,classNames,labelIDs);
combine
関数を使用して、点群とラベルを単一の学習用データストアに統合します。
cds = combine(ldsTrain,pxdsTrain);
RandLANet オブジェクトの作成
RandLANet は、LiDAR 点群のセマンティック セグメンテーションによく使用されるニューラル ネットワークです。このネットワークでは、スキップ接続を含む符号化器-復号化器アーキテクチャを使用します。各点の特徴を学習させるために、共有多層パーセプトロン層と 4 つの符号化および復号化層に順に入力を与えます。最後に、3 つの全結合層と 1 つのドロップアウト層により、3 次元点群の各点が自動車、トラック、地面、植生などのクラス ラベルに関連付けられます。
次の図は、RandLANet のアーキテクチャを示しています。ここで、
FC — 全結合層
LFA — 局所特徴集約
RS — ランダム サンプリング
MLP — 共有多層パーセプトロン
US — アップサンプリング
DP — ドロップアウト
各符号化層が点群をランダムにダウンサンプリングし、点密度を大幅に低減します。ダウンサンプリングした点群の主な特徴を保持するために、ネットワークでは各点に局所特徴集約モジュールを使用します。
各復号化層が最近傍内挿により、特徴点セットをアップサンプリングします。ネットワークはそれらのアップサンプリングされたマップと、スキップ接続された符号化層により生成された特徴マップを連結し、連結された特徴マップに共有 MLP を適用します。
randlanet
関数を使用して、RandLANet セグメンテーション ネットワークを作成します。
segmenter = randlanet("none",classNames);
学習オプションの指定
Adam 最適化アルゴリズムを使用してネットワークに学習させます。関数trainingOptions
(Deep Learning Toolbox)を使用して、ハイパーパラメーターを指定します。
learningRate = 0.01; numEpochs = 200; miniBatchSize = 8; learnRateDropFactor = 0.9886; executionEnvironment = "auto"; if canUseParallelPool dispatchInBackground = true; else dispatchInBackground = false; end options = trainingOptions("adam", ... InitialLearnRate = learningRate, ... MaxEpochs = numEpochs, ... MiniBatchSize = miniBatchSize, ... LearnRateSchedule = "piecewise", ... LearnRateDropPeriod = 1,... LearnRateDropFactor = learnRateDropFactor, ... Plots = "training-progress", ... ExecutionEnvironment = executionEnvironment, ... dispatchInBackground = dispatchInBackground, ... ResetInputNormalization = false, ... CheckpointFrequencyUnit = "epoch", ... CheckpointFrequency = 10, ... CheckpointPath = tempdir, ... BatchNormalizationStatistics="moving");
メモ: 学習時のメモリ使用量を制御するには、miniBatchSize
の値を増減します。
モデルの学習
RandLANet セグメンテーション ネットワークに学習させるには、trainRandlanet
関数を使用し、doTraining
引数を true
に設定します。そうしない場合は、事前学習済みのセグメンテーション ネットワークを読み込みます。ネットワークの学習には CPU または GPU を使用できます。GPU を使用するには、Parallel Computing Toolbox™、および CUDA® 対応の NVIDIA® GPU が必要です。詳細については、GPU 計算の要件 (Parallel Computing Toolbox)を参照してください。
doTraining = false; if doTraining segmenter = trainRandlanet(cds,segmenter,options); else segmenter = randlanet("dales"); end
航空点群のセグメント化
複数のテスト点群を読み取るためのfileDatastore
オブジェクトを作成します。ここでは、デモンストレーションの目的で単一の点群を読み取ります。
fdsTest = fileDatastore(fullfile(testDataFolder,"5080_54470.las"),"ReadFcn",@lasFileReader);
学習データで使用したのと同様の変換をテスト データに適用します。
点群と対応するラベルを抽出する。
オーバーラップしない 50×50 メートルのサイズのブロックに点群をトリミングする。
ldsTestTransformed = transform(fdsTest,@(x) helperTransformData(x,blockSize));
テスト点群のすべてのブロックを読み取ります。X には、テスト点群のすべてのブロックの pointCloud オブジェクトおよび対応するラベルが含まれていることに注意してください。
X = read(ldsTestTransformed);
サイズが M 行 1 列の pointClouds のブロックの配列を含む、pointCloud オブジェクトを作成します。segmentObjects は入力としてサイズが M 行 1 列の pointCloud 配列のみを受け入れることに注意してください。
ptCloudArray = vertcat(X{:,1});
segmentObjects
関数を使用して推定を実行し、予測ラベルを計算します。
[labels,scores] = segmentObjects(segmenter,ptCloudArray);
点群の予測を表示します。
% Concatenate blocks of point cloud. pc = pccat(ptCloudArray); % Concatenate corresponding labels of blocks of point cloud. labelsDensePred = vertcat(labels{:}); % Convert labels from categorical to numeric for display. labelsDensePred = single(categorical(labelsDensePred,classNames,cellstr(string(1:numClasses)))); figure ax = pcshow(pc.Location,labelsDensePred); axis off helperLabelColorbar(ax,classNames) title("Point Cloud Overlaid with Detected Semantic Labels")
ネットワークの評価
テスト データで評価を実行するには、テスト点群からラベルを取得します。計算して labelsDensePred
に保存した予測ラベルを使用します。
グラウンド トゥルース ラベルを抽出します。
labelsDenseTarget = vertcat(X{:,2});
関数evaluateSemanticSegmentation
を使用して、テスト セットの結果からセマンティック セグメンテーション メトリクスを計算します。
confusionMatrix = segmentationConfusionMatrix(double(labelsDensePred), ...
double(labelsDenseTarget),Classes = 1:numClasses);
metrics = evaluateSemanticSegmentation({confusionMatrix},classNames,Verbose = false);
Intersection over Union (IoU) メトリクスを使用して、クラスごとのオーバーラップの量を測定できます。
関数evaluateSemanticSegmentation
は、データ セット全体、個々のクラス、および各テスト イメージのメトリクスを返します。データ セット レベルでメトリクスを表示するには、metrics.DataSetMetrics
プロパティを検査します。
metrics.DataSetMetrics
ans=1×4 table
GlobalAccuracy MeanAccuracy MeanIoU WeightedIoU
______________ ____________ _______ ___________
0.95846 0.8537 0.70479 0.92468
データ セット メトリクスは、ネットワーク パフォーマンスの概要を提供します。全体のパフォーマンスに対する各クラスの影響を確認するには、metrics.ClassMetrics
プロパティを検査して各クラスのメトリクスを表示します。
metrics.ClassMetrics
ans=8×2 table
Accuracy IoU
________ _______
ground 0.97714 0.95606
vegetation 0.91161 0.89449
cars 0.95408 0.73238
trucks 0.34457 0.23184
powerlines 0.90194 0.85462
fences 0.89157 0.48109
poles 0.87155 0.56866
buildings 0.97715 0.91921
サポート関数
helperTransformData
関数は、データに次の変換を適用します。
点群と対応するラベルを抽出する。
DALES データ セットの各点群は、500×500 メートルの領域をカバーします。これは、回転式地上 LiDAR の点群でカバーされる一般的な領域よりもはるかに広いものです。メモリ処理を効率的にするために、オーバーラップしない 50×50 メートルのサイズの小さなブロックに点群を分割します。
function out = helperTransformData(lasReader,blocksize) [ptCloud,attr] = readPointCloud(lasReader,"Attributes","Classification"); labels = attr.Classification; % Select only labeled data. ptCloud = select(ptCloud,labels~=0); labels = labels(labels~=0); % Block the input point cloud. numGridsX = round((diff(ptCloud.XLimits)+eps)/blocksize(1)); numGridsY = round((diff(ptCloud.YLimits)+eps)/blocksize(2)); [~,~,~,indx,indy] = histcounts2(ptCloud.Location(:,1),ptCloud.Location(:,2), ... [numGridsX, numGridsY],XBinLimits = ptCloud.XLimits, YBinLimits = ptCloud.YLimits); ind = sub2ind([numGridsX, numGridsY],indx,indy); out = cell(numGridsX*numGridsY,2); for num = 1:numGridsX*numGridsY idx = ind == num; if(any(idx)) out{num,1} = select(ptCloud,idx); out{num,2} = labels(idx); end end end
参考文献
[1] Hu, Qingyong, Bo Yang, Linhai Xie, Stefano Rosa, Yulan Guo, Zhihua Wang, Niki Trigoni, and Andrew Markham. “RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds.”In 2020 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 11105–14. Seattle, WA, USA: IEEE, 2020. https://doi.org/10.1109/CVPR42600.2020.01112.
[2] Varney, Nina, Vijayan K. Asari, and Quinn Graehling. “DALES: A Large-Scale Aerial LiDAR Data Set for Semantic Segmentation.”In 2020 IEEE/CVF Conference on Computer Vision and Pattern Recognition Workshops (CVPRW), 717–26.Seattle, WA, USA: IEEE, 2020. https://doi.org/10.1109/CVPRW50498.2020.00101.
参考
オブジェクト
関数
アプリ
- ディープ ネットワーク デザイナー (Deep Learning Toolbox) | LiDAR ビューアー | LiDAR ラベラー