メインコンテンツ

PointNet++ 深層学習を使用した航空機搭載 LiDAR セマンティック セグメンテーション用のコード生成

R2026b

この例では、LiDAR セマンティック セグメンテーション用に PointNet++ ネットワークの CUDA® MEX コードを生成する方法を示します。

この例では、8 つのクラス (buildings、cars、trucks、poles、power lines、fences、ground、vegetation) に属するアンオーガナイズド LiDAR 点群をセグメント化できる、事前学習済みの PointNet++ [1] ネットワークを使用します。PointNet++ ネットワークの詳細については、PointNet++ 入門 (Point Cloud Toolbox)を参照してください。

サードパーティの前提条件

必要

  • CUDA 対応 NVIDIA® GPU および互換性のあるドライバー。

オプション

スタティック ライブラリ、ダイナミック ライブラリ、または実行可能ファイルなどの MEX 以外のビルドについて、この例では以下の要件も適用されます。

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++ ネットワークを読み込みます。この事前学習済みのネットワークは dlnetwork になります。このネットワークに学習させる方法の詳細については、PointNet++ 深層学習を使用した航空 LiDAR のセマンティック セグメンテーション (Point Cloud Toolbox)の例を参照してください。

net = getPointnetplusNet
net = 
  dlnetwork with properties:

         Layers: [78×1 nnet.cnn.layer.Layer]
    Connections: [91×2 table]
     Learnables: [86×3 table]
          State: [42×3 table]
     InputNames: {'InputPoints'}
    OutputNames: {'Softmax'}
    Initialized: 1

  View summary with summary.

ネットワーク アーキテクチャを対話的に可視化し、ネットワーク層についての詳細情報を表示するには、deepNetworkDesigner(net) を使用します。詳細については、ディープ ネットワーク デザイナーを参照してください。

事前学習済みネットワークでは、関数functionLayerによって、サンプリング層とグループ化層、および内挿層が実装されます。アンオーガナイズド点群形式の pointCloudInputLayer、および関数 functionLayer は、コード生成をサポートしていません。コード生成をサポートするには、関数 helperReplaceInputAndFunctionLayers を使用して、関数層をカスタム層に置き換え、pointcloudInputLayer をオーガナイズド点群形式に置き換えます。この関数は、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 dlnetwork 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-23 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 {dlarray(randn(8192,1,3,"single"),"SSCB")} -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 (Point Cloud Toolbox)オブジェクトを使用して、オーバーラップしない小さなブロックに点群を分割します。

変数 blockSize でブロックの次元を定義します。データセット内の各点群のサイズは変化するため、ブロックの z 次元を Inf に設定して、z 軸に沿ってブロックが作成されないようにします。

blockSize = [51 51 Inf];

最初に、blockedPointCloud (Point Cloud Toolbox)オブジェクトを作成します。次に、blockedPointCloud (Point Cloud Toolbox)オブジェクトを使用してテスト データ上にblockedPointCloudDatastore (Point Cloud Toolbox)オブジェクトを作成します。

tbpc = blockedPointCloud(fullfile(testDataFolder,"5080_54470.las"),blockSize);
tbpcds = blockedPointCloudDatastore(tbpc);

ネットワークに学習させるために使用されるパラメーターを定義します。詳細については、PointNet++ 深層学習を使用した航空 LiDAR のセマンティック セグメンテーション (Point Cloud 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(dlarray(single(ptCloudSparseForPrediction{1,1}),"SSCB"));
    [~,labelsSparsePred] = max(scoresPred,[],3);
    labelsSparsePred = uint8(extractdata(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 ...
20-May-2026 11:58:36: Job Running. Waiting for parallel pool workers to connect ...
Connected to parallel pool with 6 workers.

より見やすくするために、点群データから推定された単一のブロックを表示します。

figure;
ax = pcshow(ptCloudDense.Location,interpolatedLabels);
axis off;
helperLabelColorbar(ax,classNames);
title("Point Cloud Overlaid with Detected Semantic Labels");

Figure contains an axes object. The hidden axes object with title Point Cloud Overlaid with Detected Semantic Labels contains an object of type scatter.

サポート関数

補助関数 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.