PointNet++ 深層学習を使用した航空機搭載 LiDAR セマンティック セグメンテーション用のコード生成
この例では、LiDAR セマンティック セグメンテーション用に PointNet++ ネットワークの CUDA® MEX コードを生成する方法を示します。
この例では、8 つのクラス (buildings、cars、trucks、poles、power lines、fences、ground、vegetation) に属するアンオーガナイズド LiDAR 点群をセグメント化できる、事前学習済みの PointNet++[1] ネットワークを使用します。PointNet++ ネットワークの詳細については、PointNet++ 入門 (Lidar Toolbox)を参照してください。
サードパーティの必要条件
必須
CUDA 対応 NVIDIA® GPU および互換性のあるドライバー。
オプション
スタティック ライブラリ、ダイナミック ライブラリ、または実行可能ファイルなどの MEX 以外のビルドについて、この例では以下の要件も適用されます。
NVIDIA Toolkit。
NVIDIA cuDNN ライブラリ。
コンパイラおよびライブラリの環境変数。詳細については、サードパーティ ハードウェア (GPU Coder)と前提条件となる製品の設定 (GPU Coder)を参照してください。
GPU 環境の検証
この例を実行するためのコンパイラおよびライブラリが正しく設定されていることを確認するために、関数coder.checkGpuInstall
(GPU Coder)を使用します。
envCfg = coder.gpuEnvConfig('host'); envCfg.DeepLibTarget = 'cudnn'; envCfg.DeepCodegen = 1; envCfg.Quiet = 1; coder.checkGpuInstall(envCfg);
coder.gpuEnvConfig
オブジェクトの Quiet
プロパティが true に設定されている場合、関数 coder.checkGpuInstall
は警告メッセージまたはエラー メッセージのみを返します。
PointNet++ ネットワークの読み込み
この例にサポート ファイルとして添付されている関数 getPointnetplusNet
を使用して、事前学習済みの PointNet++ ネットワークを読み込みます。事前学習済みネットワークは DAG ネットワークになります。このネットワークに学習させる方法の詳細については、PointNet++ 深層学習を使用した航空 LiDAR のセマンティック セグメンテーション (Lidar Toolbox)の例を参照してください。
net = getPointnetplusNet
ネットワーク アーキテクチャの対話的な可視化とネットワーク層に関する詳細情報を表示するには、deepNetworkDesigner(net).
を使用します。詳細については、ディープ ネットワーク デザイナーを参照してください。
事前学習済みネットワークでは、関数functionLayer
によって、サンプリング層とグループ化層、および内挿層が実装されます。関数 pointCloudInputLayer
と関数 functionLayer
はコード生成をサポートしていません。コード生成をサポートするには、関数 helperReplaceInputAndFunctionLayers
を使用して、関数層をカスタム層に、pointCloudInputLayer
を imageInputLayer
に置き換えます。この関数は、pointnetplusCodegenNet.mat
という名前の MAT ファイルとしてネットワークを保存します。
net = helperReplaceInputAndFunctionLayers(net);
エントリポイント関数 pointnetplusPredict
エントリポイント関数 pointnetplusPredict
は、入力としてひとまとまりの点群データ行列を取り、pointnetplusCodegenNet.mat
ファイルに保存された深層学習ネットワークを使用して点群に対する予測を実行します。この関数は、ネットワーク オブジェクトを pointnetplusCodegenNet.mat
ファイルから永続変数 mynet
に読み込み、後続の予測呼び出しでその永続変数を再利用します。
type('pointnetplusPredict.m');
function out = pointnetplusPredict(in) %#codegen % A persistent object mynet is used to load the DAG network object. At % the first call to this function, the persistent object is constructed and % setup. When the function is called subsequent times, the same object is % reused to call predict on inputs, thus avoiding reconstructing and % reloading the network object. % Copyright 2021 The MathWorks, Inc. persistent mynet; if isempty(mynet) mynet = coder.loadDeepLearningNetwork('pointnetplusCodegenNet.mat'); end % pass in input out = predict(mynet,in);
CUDA MEX コードの生成
エントリポイント関数 pointnetplusPredict
用の CUDA コードを生成するには、MEX ターゲットの GPU コード構成オブジェクトを作成し、ターゲット言語を C++ に設定します。関数coder.DeepLearningConfig
(GPU Coder)を使用して CuDNN 深層学習構成オブジェクトを作成し、それを GPU コード構成オブジェクトの DeepLearningConfig
プロパティに割り当てます。ネットワークの入力層の点群データのサイズ (この場合は [8192 1 3]) を使用して codegen
コマンドを実行します。
cfg = coder.gpuConfig('mex'); cfg.TargetLang = 'C++'; cfg.DeepLearningConfig = coder.DeepLearningConfig(TargetLibrary='cudnn'); codegen -config cfg pointnetplusPredict -args {randn(8192,1,3,'single')} -report
Code generation successful: View report
TensorRT ターゲット用の CUDA コードを生成するには、CuDNN 構成オブジェクトではなく、TensorRT 深層学習構成オブジェクトを使用します。
生成された MEX コードを使用した航空測量による点群のセグメント化
この例のネットワークは、DALES データ セット [2] で学習させています。DALES Web サイトの指示に従って、変数 dataFolder
で指定するフォルダーにデータ セットをダウンロードします。テスト データを保存するフォルダーを作成します。
dataFolder = fullfile(tempdir,'DALES'); testDataFolder = fullfile(dataFolder,'dales_las','test');
DALES データセットの各点群は、500×500 メートルの領域をカバーします。これは、地上型 LIDAR の点群がカバーする一般的な領域よりもはるかに広いものです。効率的なメモリ処理のために、blockedPointCloud
(Lidar Toolbox)オブジェクトを使用して、オーバーラップしない小さなブロックに点群を分割します。
変数 blockSize
でブロックの次元を定義します。データセット内の各点群のサイズは変化するため、ブロックの z 次元を Inf
に設定して、z 軸に沿ってブロックが作成されないようにします。
blockSize = [51 51 Inf];
最初に、blockedPointCloud
(Lidar Toolbox)オブジェクトを作成します。次に、blockedPointCloud
(Lidar Toolbox)オブジェクトを使用してテスト データ上にblockedPointCloudDatastore
(Lidar Toolbox)オブジェクトを作成します。
tbpc = blockedPointCloud(fullfile(testDataFolder,'5080_54470.las'),blockSize);
tbpcds = blockedPointCloudDatastore(tbpc);
ネットワークに学習させるために使用されるパラメーターを定義します。詳細については、PointNet++ 深層学習を使用した航空 LiDAR のセマンティック セグメンテーション (Lidar Toolbox)の例を参照してください。
numNearestNeighbors = 20; radius = 0.05; numPoints = 8192; maxLabel = 1; classNames = [ "ground" "vegetation" "cars" "trucks" "powerlines" "fences" "poles" "buildings" ]; numClasses = numel(classNames);
予測ラベルとターゲット ラベルのプレースホルダーを初期化します。
labelsDensePred = []; labelsDenseTarget = [];
学習データで使用したのと同じ変換をテスト データ tbpcds
に適用するには、次の手順に従います。
点群を抽出します。
点群を指定された数
numPoints
にダウンサンプリング。点群を [0 1] の範囲に正規化。
ネットワークの入力層と互換性をもつように点群を変換。
テスト点群データで推論を実行して、予測ラベルを計算します。関数 pointnetplusPredict_mex
を使用して、疎な点群のラベルを予測します。次に、疎な点群の予測ラベルを内挿して、稠密な点群の予測ラベルを取得し、オーバーラップしていないすべてのブロックでこのプロセスを反復します。
while hasdata(tbpcds) % Read the block along with block information. [ptCloudDense,infoDense] = read(tbpcds); % Extract the labels from the block information. labelsDense = infoDense.PointAttributes.Classification; % Select only labeled data. ptCloudDense = select(ptCloudDense{1},labelsDense~=0); labelsDense = labelsDense(labelsDense~=0); % Use the helperDownsamplePoints function, attached to this example as a % supporting file, to extract a downsampled point cloud from the % dense point cloud. ptCloudSparse = helperDownsamplePoints(ptCloudDense, ... labelsDense,numPoints); % Make the spatial extent of the dense point cloud equal to the sparse % point cloud. limits = [ptCloudDense.XLimits;ptCloudDense.YLimits;ptCloudDense.ZLimits]; ptCloudSparseLocation = ptCloudSparse.Location; ptCloudSparseLocation(1:2,:) = limits(:,1:2)'; ptCloudSparse = pointCloud(ptCloudSparseLocation,Color=ptCloudSparse.Color, ... Intensity=ptCloudSparse.Intensity, Normal=ptCloudSparse.Normal); % Use the helperNormalizePointCloud function, attached to this example as % a supporting file, to normalize the point cloud between 0 and 1. ptCloudSparseNormalized = helperNormalizePointCloud(ptCloudSparse); ptCloudDenseNormalized = helperNormalizePointCloud(ptCloudDense); % Use the helperTransformToTestData function, defined at the end of this % example, to convert the point cloud to a cell array and to permute the % dimensions of the point cloud to make it compatible with the input layer % of the network. ptCloudSparseForPrediction = helperTransformToTestData(ptCloudSparseNormalized); % Get the output predictions. scoresPred = pointnetplusPredict_mex(single(ptCloudSparseForPrediction{1,1})); [~,labelsSparsePred] = max(scoresPred,[],3); labelsSparsePred = uint8(labelsSparsePred); % Use the helperInterpolate function, attached to this example as a % supporting file, to calculate labels for the dense point cloud, % using the sparse point cloud and labels predicted on the sparse point cloud. interpolatedLabels = helperInterpolate(ptCloudDenseNormalized, ... ptCloudSparseNormalized,labelsSparsePred,numNearestNeighbors, ... radius,maxLabel,numClasses); % Concatenate the predicted and target labels from the blocks. labelsDensePred = vertcat(labelsDensePred,interpolatedLabels); labelsDenseTarget = vertcat(labelsDenseTarget,labelsDense); end
Starting parallel pool (parpool) using the 'Processes' profile ... Connected to the parallel pool (number of workers: 6).
より見やすくするために、点群データから推定された単一のブロックを表示します。
figure; ax = pcshow(ptCloudDense.Location,interpolatedLabels); axis off; helperLabelColorbar(ax,classNames); title("Point Cloud Overlaid with Detected Semantic Labels");
サポート関数
補助関数 helperLabelColorbar
は、現在の軸にカラー バーを追加します。カラー バーはクラス名を色で表示します。
function helperLabelColorbar(ax,classNames) % Colormap for the original classes. cmap = [[0,0,255]; [0,255,0]; [255,192,203]; [255,255,0]; [255,0,255]; [255,165,0]; [139,0,150]; [255,0,0]]; cmap = cmap./255; cmap = cmap(1:numel(classNames),:); colormap(ax,cmap); % Add colorbar to current figure. c = colorbar(ax); c.Color = 'w'; % Center tick labels and use class names for tick marks. numClasses = size(classNames, 1); c.Ticks = 1:1:numClasses; c.TickLabels = classNames; % Remove tick mark. c.TickLength = 0; end
補助関数 helperTransformToTestData
は、点群を cell 配列に変換し、点群の次元を並べ替えて、ネットワークの入力層と互換性をもたせます。
function data = helperTransformToTestData(data) if ~iscell(data) data = {data}; end numObservations = size(data,1); for i = 1:numObservations tmp = data{i,1}.Location; data{i,1} = permute(tmp,[1 3 2]); end end
参考文献
[1] Qi, Charles R., Li Yi, Hao Su, and Leonidas J. Guibas. "PointNet++: Deep Hierarchical Feature Learning on Point Sets in a Metric Space." ArXiv:1706.02413 [Cs], June 7, 2017. https://arxiv.org/abs/1706.02413.
[2] Varney, Nina, Vijayan K. Asari, and Quinn Graehling. "DALES: A Large-Scale Aerial LiDAR Data Set for Semantic Segmentation." ArXiv:2004.11985 [Cs, Stat], April 14, 2020. https://arxiv.org/abs/2004.11985.