メインコンテンツ

LiDAR 点群セグメンテーション ネットワーク用のコード生成

この例では、LiDAR セマンティック セグメンテーション用の深層学習ネットワークの CUDA® MEX コードを生成する方法を示します。この例では、3 つのクラス (backgroundcartruck) に属しているオーガナイズド LiDAR 点群をセグメント化することができる、事前学習済みの SqueezeSegV2 [1] ネットワークが使用されます。ネットワークに学習させる手順については、SqueezeSegV2 深層学習ネットワークを使用した LiDAR 点群のセマンティック セグメンテーション (Lidar Toolbox)を参照してください。生成された MEX コードは、入力としてひとまとまりの点群を取り、SqueezeSegV2 ネットワーク用の DAGNetwork オブジェクトを使用して点群に対する予測を実行します。

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

必要

この例では、CUDA MEX を生成します。以下のサードパーティ要件が適用されます。

  • 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);

セグメンテーション ネットワーク

SqueezeSegV2 は、オーガナイズド LiDAR 点群のセマンティック セグメンテーション用に設計された畳み込みニューラル ネットワーク (CNN) です。このネットワークは、LiDAR データ セットで学習させた符号化器/復号化器セグメンテーション深層ネットワークで、推論のために MATLAB® にインポートされています。SqueezeSegV2 では、符号化器サブネットワークが、最大プーリング層が点在する畳み込み層で構成されます。この配置では、入力イメージの解像度を逐次下げていきます。復号化器サブネットワークは一連の転置畳み込み層で構成され、入力イメージの解像度を逐次上げていきます。加えて、SqueezeSegV2 ネットワークは、コンテキスト集約モジュール (CAM) を含めることによって欠損データの影響を軽減します。CAM は、大きな受容野からコンテキスト情報を集約する、filterSize の値が [7, 7] の畳み込みサブネットワークです。これにより、欠損データに対するネットワークの堅牢さが向上します。この例の SqueezeSegV2 ネットワークは、3 つのクラス (背景、自動車、トラック) に属する点をセグメント化するように学習されています。

Mathworks® LiDAR データセットを使用して MATLAB® でセマンティック セグメンテーション ネットワークに学習させる方法の詳細については、PointSeg 深層学習ネットワークを使用した LIDAR 点群のセマンティック セグメンテーション (Lidar Toolbox)を参照してください。

事前学習済みの SqueezeSegV2 ネットワークをダウンロードします。

net = getSqueezeSegV2Net();
Downloading pretrained SqueezeSegV2 (2 MB)...

DAG ネットワークには、畳み込み層、ReLU 層、バッチ正規化層、焦点損失出力層など、238 個の層が含まれています。深層学習ネットワーク アーキテクチャを対話的に可視化して表示するには、関数 analyzeNetwork を使用します。

analyzeNetwork(net);

エントリポイント関数 squeezesegv2_predict

この例に添付されているエントリポイント関数 squeezesegv2_predict.m は、入力としてひとまとまりの点群を取り、SqueezeSegV2Net.mat ファイルに保存された深層学習ネットワークを使用して点群に対する予測を実行します。この関数は、ネットワーク オブジェクトを SqueezeSegV2Net.mat ファイルから永続変数 mynet に読み込み、後続の予測呼び出しでその永続変数を再利用します。

type('squeezesegv2_predict.m');
function out = squeezesegv2_predict(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 2020 The MathWorks, Inc.

persistent mynet;

if isempty(mynet)
    mynet = coder.loadDeepLearningNetwork('SqueezeSegV2Net.mat');
end

% pass in input
out = predict(mynet,in);

CUDA MEX コードの生成

エントリポイント関数 squeezesegv2_predict.m 用の CUDA MEX コードを生成するには、MEX ターゲットの GPU コード構成オブジェクトを作成し、ターゲット言語を C++ に設定します。関数 coder.DeepLearningConfig (GPU Coder) を使用して CuDNN 深層学習構成オブジェクトを作成し、それを GPU コード構成オブジェクトの DeepLearningConfig プロパティに割り当てます。入力サイズ [64, 1024, 5] を指定して codegen コマンドを実行します。この値は SqueezeSegV2 ネットワークの入力層のサイズに対応します。

cfg = coder.gpuConfig('mex');
cfg.TargetLang = 'C++';
cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn');
codegen -config cfg squeezesegv2_predict -args {ones(64,1024,5,'uint8')} -report
Code generation successful: View report

NVIDIA TensorRT ライブラリを利用する CUDA C++ コードを生成するには、コードで、coder.DeepLearningConfig('cudnn') の代わりに coder.DeepLearningConfig('tensorrt') を指定します。

Intel® プロセッサで深層学習ネットワーク用の MEX コードを生成する方法については、MKL-DNN を使用した、深層学習ネットワークのためのコード生成 (MATLAB Coder)を参照してください。

データの準備

オーガナイズド テスト点群を MATLAB® に読み込みます。点群を予測用の 5 チャネル イメージに変換します。

ptCloud = pcread('ousterLidarDrivingData.pcd');
I = pointCloudToImage(ptCloud);

% Examine converted data
whos I
  Name       Size                 Bytes  Class    Attributes

  I         64x1024x5            327680  uint8              

イメージには 5 つのチャネルがあります。(x,y,z) 点座標が、最初の 3 つのチャネルを構成します。4 つ目のチャネルには、LiDAR 強度測定値が格納されます。5 つ目のチャネルには、r=x2+y2+z2 として計算される範囲情報が格納されます。

イメージの強度チャネルを可視化します。

intensityChannel = I(:,:,4);    

figure;
imshow(intensityChannel);
title('Intensity Image');

Figure contains an axes object. The hidden axes object with title Intensity Image contains an object of type image.

データに対する生成された MEX の実行

5 チャネル イメージに対して squeezesegv2_predict_mex を呼び出します。

predict_scores = squeezesegv2_predict_mex(I);

変数 predict_scores は、各クラスのピクセル単位の予測スコアに対応する 3 つのチャネルをもつ 3 次元行列です。最大予測スコアを使用してチャネルを計算し、ピクセル単位のラベルを取得します。

[~,argmax] = max(predict_scores,[],3);

セグメント化されたラベルを強度チャネル イメージに重ね合わせ、セグメント化された領域を表示します。セグメント化された出力のサイズを変更し、カラー バーを追加してより見やすくします。

classes = [
    "background"
    "car"
    "truck"
    ];

cmap = lidarColorMap();
SegmentedImage = labeloverlay(intensityChannel,argmax,'ColorMap',cmap);
SegmentedImage = imresize(SegmentedImage, 'Scale', [2 1], 'method', 'nearest');
figure;
imshow(SegmentedImage);

N = numel(classes);
ticks = 1/(N*2):1/N:1;
colorbar('TickLabels',cellstr(classes),'Ticks',ticks,'TickLength',0,'TickLabelInterpreter','none');
colormap(cmap)
title('Semantic Segmentation Result');

Figure contains an axes object. The hidden axes object with title Semantic Segmentation Result contains an object of type image.

点群シーケンスに対する生成された MEX コードの実行

入力点群シーケンスを読み取ります。このシーケンスには、Ouster OS1 LiDAR センサーを使用して収集された 10 個のオーガナイズド pointCloud フレームが含まれています。入力データは高さ 64、幅 1024 で構成されるため、各 pointCloud オブジェクトのサイズは 64 行 1024 列になります。

dataFile = 'highwaySceneData.mat';

% Load data in workspace.
load(dataFile);

関心が異なるクラスごとに別々の色を設定して、点単位のラベルを可視化します。

% Apply the color red to cars.
carClassCar = zeros(64, 1024, 3, 'uint8');
carClassCar(:,:,1) = 255*ones(64, 1024, 'uint8');

% Apply the color blue to trucks.
truckClassColor = zeros(64, 1024, 3, 'uint8');
truckClassColor(:,:,3) = 255*ones(64, 1024, 'uint8');

% Apply the color gray to background.
backgroundClassColor = 153*ones(64, 1024, 3, 'uint8');

関数 pcplayer のプロパティを設定して、シーケンスと出力予測を表示します。入力シーケンスをフレームごとに読み取り、モデルを使用して関心クラスを検出します。

xlimits = [0 120.0];
ylimits = [-80.7 80.7];
zlimits = [-8.4 27];

player = pcplayer(xlimits, ylimits, zlimits);
set(get(player.Axes,'parent'), 'units','normalized','outerposition',[0 0 1 1]);
zoom(get(player.Axes,'parent'),2);
set(player.Axes,'XColor','none','YColor','none','ZColor','none');

for i = 1 : numel(inputData)
    ptCloud = inputData{i};
    
    % Convert point cloud to five-channel image for prediction.
    I = pointCloudToImage(ptCloud);
    
    % Call squeezesegv2_predict_mex on the 5-channel image.
    predict_scores = squeezesegv2_predict_mex(I);
    
    % Convert the numeric output values to categorical labels.
    [~,predictedOutput] = max(predict_scores,[],3);
    predictedOutput = categorical(predictedOutput, 1:3, classes);
    
    % Extract the indices from labels.
    carIndices = predictedOutput == 'car';
    truckIndices = predictedOutput == 'truck';
    backgroundIndices = predictedOutput == 'background';
    
    % Extract a point cloud for each class.
    carPointCloud = select(ptCloud, carIndices, 'OutputSize','full');
    truckPointCloud = select(ptCloud, truckIndices, 'OutputSize','full');
    backgroundPointCloud = select(ptCloud, backgroundIndices, 'OutputSize','full');
    
    % Fill the colors to different classes.
    carPointCloud.Color = carClassCar;
    truckPointCloud.Color = truckClassColor;
    backgroundPointCloud.Color = backgroundClassColor;
    
    % Merge and add all the processed point clouds with class information.
    coloredCloud = pcmerge(carPointCloud, truckPointCloud, 0.01);
    coloredCloud = pcmerge(coloredCloud, backgroundPointCloud, 0.01);
    
    % View the output.
view(player, coloredCloud);
    drawnow;
end

補助関数

この例で使用されている補助関数は次のとおりです。

type pointCloudToImage.m
function image = pointCloudToImage(ptcloud)
%pointCloudToImage Converts organized 3-D point cloud to 5-channel 
%   2-D image.

image = ptcloud.Location;
image(:,:,4) = ptcloud.Intensity;
rangeData = iComputeRangeData(image(:,:,1),image(:,:,2),image(:,:,3));
image(:,:,5) = rangeData;

% Cast to uint8.
image = uint8(image);
end

%--------------------------------------------------------------------------
function rangeData = iComputeRangeData(xChannel,yChannel,zChannel)
rangeData = sqrt(xChannel.*xChannel+yChannel.*yChannel+zChannel.*zChannel);
end
type lidarColorMap.m
function cmap = lidarColorMap()

cmap = [
   0.00  0.00   0.00  % background
   0.98  0.00   0.00  % car
   0.00  0.00   0.98  % truck
   ];
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.” Preprint, submitted September 22, 2018. http://arxiv.org/abs/1809.08495.