Main Content

GPU Coder により最適化された車線検出

この例では、NVIDIA® GPU で実行される、深層学習を使用した車線検出アプリケーションを開発する方法を説明します。

事前学習済みの車線検出ネットワークは、AlexNet ネットワークをベースとしており、イメージに含まれる車線マーカーの境界を検出して出力することができます。AlexNet ネットワークの最後の数層が、規模の小さい全結合層と回帰出力層に置き換えられています。この例では、ホスト マシンに搭載された CUDA 対応 GPU で実行される CUDA® 実行可能ファイルを生成します。

サードパーティの必要条件

  • CUDA 対応 NVIDIA GPU。

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

事前学習済みの車線検出ネットワークの取得

この例では、事前学習済みの車線検出ネットワークを含む trainedLaneNet MAT ファイルを使用します。このファイルのサイズは約 143 MB です。MathWorks® の Web サイトからファイルをダウンロードします。

laneNetFile = matlab.internal.examples.downloadSupportFile('gpucoder/cnn_models/lane_detection', ...
    'trainedLaneNet.mat');

このネットワークは入力としてイメージを取り、エゴ ビークルの左右の車線に対応する 2 つの車線境界線を出力します。各車線境界線は、放物線方程式 y=ax2+bx+c によって表すことができます。ここで、y は横方向オフセット、x は車両からの縦方向の距離です。このネットワークは、車線ごとに 3 つのパラメーター a、b、c を出力します。ネットワーク アーキテクチャは AlexNet に似ていますが、最後の数層は、規模の小さい全結合層と回帰出力層に置き換えられています。

load(laneNetFile);
disp(laneNet)
  SeriesNetwork with properties:

         Layers: [23×1 nnet.cnn.layer.Layer]
     InputNames: {'data'}
    OutputNames: {'output'}

SeriesNetwork オブジェクトを dlnetwork オブジェクトに変換し、別の MAT ファイルに保存します。

dlLaneNet = dag2dlnetwork(laneNet);
dlLaneNetFile = 'trainedDlLaneNet.mat';
save(dlLaneNetFile,'dlLaneNet');

テスト ビデオのダウンロード

モデルをテストするために、この例では、Caltech Lanes Dataset のビデオ ファイルを使用します。ファイルのサイズは約 8 MB です。MathWorks の Web サイトからファイルをダウンロードします。

videoFile = matlab.internal.examples.downloadSupportFile('gpucoder/media','caltech_cordova1.avi');

メイン エントリポイント関数

detectLanesInVideo.m ファイルは、コード生成用のメイン エントリポイント関数です。関数 detectLanesInVideo は、VideoReaderオブジェクトを使用して入力ビデオからフレームを読み取り、LaneNet ネットワーク オブジェクトの predict メソッドを呼び出して、検出された車線を入力ビデオ上に描画します。vision.DeployableVideoPlayer (Computer Vision Toolbox)System object は、検出された車線を含むビデオ出力を表示するために使用されます。

type detectLanesInVideo.m
function detectLanesInVideo(videoFile,net,laneCoeffMeans,laneCoeffsStds)
%   Copyright 2022-2024 The MathWorks, Inc.

%#codegen

%% Create Video Reader and Video Player Object 
videoFReader   = VideoReader(videoFile);
depVideoPlayer = vision.DeployableVideoPlayer(Name='Lane Detection on GPU');
videoHeight = videoFReader.Height;
videoWidth = videoFReader.Width;
%% Video Frame Processing Loop
while hasFrame(videoFReader)
    videoFrame = imresize(readFrame(videoFReader),[videoHeight videoWidth]);
    scaledFrame = imresize(videoFrame,[227 227]);

    [laneFound,ltPts,rtPts] = laneNetPredict(net,scaledFrame, ...
        laneCoeffMeans,laneCoeffsStds);
    lnaeFound = 1;
    if(laneFound)
        pts = [reshape(ltPts',1,[]);reshape(rtPts',1,[])];
        videoFrame = insertShape(videoFrame, 'Line', pts, 'LineWidth', 4);
    
    depVideoPlayer(videoFrame);
    end
end

LaneNet 予測関数

関数 laneNetPredict は、1 つのビデオ フレームに含まれる右車線と左車線の位置を計算します。この laneNet ネットワークは、左右の車線境界線の放物線方程式を記述するパラメーター a、b、c を計算します。これらのパラメーターから、車線の位置に対応する x 座標と y 座標を計算します。これらの座標をイメージ座標にマッピングしなければなりません。

type laneNetPredict.m
function [laneFound,ltPts,rtPts] = laneNetPredict(net,frame,means,stds) 
%#codegen

% laneNetPredict Predict lane markers on the input image frame using the
% lane detection network

%   Copyright 2017-2024 The MathWorks, Inc.

dlFrame = dlarray(single(frame),'SSC');
persistent dllaneNet;
if isempty(dllaneNet)
    dllaneNet = coder.loadDeepLearningNetwork(net, 'dllaneNet');
end

dllanecoeffsNetworkOutput = predict(dllaneNet,dlFrame);
lanecoeffsNetworkOutput = extractdata(dllanecoeffsNetworkOutput);

% Recover original coeffs by reversing the normalization steps.
params = lanecoeffsNetworkOutput' .* stds + means;

% 'c' should be more than 0.5 for it to be a lane.
isRightLaneFound = abs(params(6)) > 0.5;
isLeftLaneFound =  abs(params(3)) > 0.5;

% From the networks output, compute left and right lane points in the image
% coordinates.
vehicleXPoints = 3:30;
ltPts = coder.nullcopy(zeros(28,2,'single'));
rtPts = coder.nullcopy(zeros(28,2,'single'));

if isRightLaneFound && isLeftLaneFound
    rtBoundary = params(4:6);
    rt_y = computeBoundaryModel(rtBoundary, vehicleXPoints);
    
    ltBoundary = params(1:3);
    lt_y = computeBoundaryModel(ltBoundary, vehicleXPoints);

    % Visualize lane boundaries of the ego vehicle.
    tform = get_tformToImage;

    % Map vehicle to image coordinates.
    ltPts =  tform.transformPointsInverse([vehicleXPoints', lt_y']);
    rtPts =  tform.transformPointsInverse([vehicleXPoints', rt_y']);
    laneFound = true;
else
    laneFound = false;
end
end

%% Helper Functions

% Compute boundary model.
function yWorld = computeBoundaryModel(model, xWorld)
yWorld = polyval(model, xWorld);
end

% Compute extrinsics.
function tform = get_tformToImage

%The camera coordinates are described by the caltech mono
% camera model.
yaw = 0;
pitch = 14; % Pitch of the camera in degrees
roll = 0;

translation = translationVector(yaw, pitch, roll);
rotation    = rotationMatrix(yaw, pitch, roll);

% Construct a camera matrix.
focalLength    = [309.4362, 344.2161];
principalPoint = [318.9034, 257.5352];
Skew = 0;

camMatrix = [rotation; translation] * intrinsicMatrix(focalLength, ...
    Skew, principalPoint);

% Turn camMatrix into 2-D homography.
tform2D = [camMatrix(1,:); camMatrix(2,:); camMatrix(4,:)]; % drop Z

tform = projective2d(tform2D);
tform = tform.invert();
end

% Translate to image co-ordinates.
function translation = translationVector(yaw, pitch, roll)
SensorLocation = [0 0];
Height = 2.1798;    % mounting height in meters from the ground
rotationMatrix = (...
    rotZ(yaw)*... % last rotation
    rotX(90-pitch)*...
    rotZ(roll)... % first rotation
    );


% Adjust for the SensorLocation by adding a translation.
sl = SensorLocation;

translationInWorldUnits = [sl(2), sl(1), Height];
translation = translationInWorldUnits*rotationMatrix;
end

% Rotation around X-axis.
function R = rotX(a)
a = deg2rad(a);
R = [...
    1   0        0;
    0   cos(a)  -sin(a);
    0   sin(a)   cos(a)];

end

% Rotation around Y-axis.
function R = rotY(a)
a = deg2rad(a);
R = [...
    cos(a)  0 sin(a);
    0       1 0;
    -sin(a) 0 cos(a)];

end

% Rotation around Z-axis.
function R = rotZ(a)
a = deg2rad(a);
R = [...
    cos(a) -sin(a) 0;
    sin(a)  cos(a) 0;
    0       0      1];
end

% Given the Yaw, Pitch, and Roll, determine the appropriate Euler angles
% and the sequence in which they are applied to align the camera's
% coordinate system with the vehicle coordinate system. The resulting
% matrix is a Rotation matrix that together with the Translation vector
% defines the extrinsic parameters of the camera.
function rotation = rotationMatrix(yaw, pitch, roll)
rotation = (...
    rotY(180)*...            % last rotation: point Z up
    rotZ(-90)*...            % X-Y swap
    rotZ(yaw)*...            % point the camera forward
    rotX(90-pitch)*...       % "un-pitch"
    rotZ(roll)...            % 1st rotation: "un-roll"
    );
end

% Intrinsic matrix computation.
function intrinsicMat = intrinsicMatrix(FocalLength, Skew, PrincipalPoint)
intrinsicMat = ...
    [FocalLength(1)  , 0                     , 0; ...
    Skew             , FocalLength(2)   , 0; ...
    PrincipalPoint(1), PrincipalPoint(2), 1];
end

CUDA 実行可能ファイルの生成

エントリポイント関数 detectLanesInVideo 用のスタンドアロン CUDA 実行可能ファイルを生成するには、'exe' ターゲットの GPU コード構成オブジェクトを作成し、ターゲット言語を C++ に設定します。関数 coder.DeepLearningConfig (GPU Coder) を使用して CuDNN 深層学習構成オブジェクトを作成し、それを GPU コード構成オブジェクトの DeepLearningConfig プロパティに割り当てます。

cfg = coder.gpuConfig('exe');
cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn');
cfg.GenerateReport = true;
cfg.GenerateExampleMain = "GenerateCodeAndCompile";
cfg.TargetLang = 'C++';
inputs = {coder.Constant(videoFile),coder.Constant(dlLaneNetFile), ...
    coder.Constant(laneCoeffMeans),coder.Constant(laneCoeffsStds)};

codegen コマンドを実行します。

codegen -args inputs -config cfg detectLanesInVideo
Code generation successful: View report

実行可能ファイルの実行

実行可能ファイルを実行するには、次のコード行のコメントを解除します。

if ispc
    [status,cmdout] = system("detectLanesInVideo.exe");
else
    [status,cmdout] = system("./detectLanesInVideo");
end

gpuLaneDetectionOutput.png

関連するトピック