В этом примере показано, как сгенерировать код CUDA® от нейронной сети для глубокого обучения, представленной SeriesNetwork объект. В этом примере серийная сеть является сверточной нейронной сетью, которая может обнаружить и вывести контуры маркера маршрута от изображения.
CUDA включил NVIDIA® графический процессор.
NVIDIA инструментарий CUDA и драйвер.
Библиотека NVIDIA cuDNN.
Библиотеки OpenCV для видео чтения и изображения отображают операции.
Переменные окружения для компиляторов и библиотек. Для получения информации о поддерживаемых версиях компиляторов и библиотек, смотрите Стороннее Оборудование. Для подготовки переменных окружения смотрите Подготовку Необходимых как условие продуктов.
Используйте coder.checkGpuInstall функция, чтобы проверить, что компиляторы и библиотеки, необходимые для выполнения этого примера, настраиваются правильно.
envCfg = coder.gpuEnvConfig('host'); envCfg.DeepLibTarget = 'cudnn'; envCfg.DeepCodegen = 1; envCfg.Quiet = 1; coder.checkGpuInstall(envCfg);
[laneNet, coeffMeans, coeffStds] = getLaneDetectionNetworkGPU();
Эта сеть берет изображение в качестве входа и выходных параметров два контура маршрута, которые соответствуют левым и правым маршрутам автомобиля, оборудованного датчиком. Каждый контур маршрута представлен параболическим уравнением:
где y является боковым смещением, и x является продольным расстоянием от транспортного средства. Сетевые выходные параметры эти три параметра a, b, и c на маршрут. Сетевая архитектура похожа на AlexNet за исключением того, что последние несколько слоев заменяются меньшим полносвязным слоем и регрессией выходной слой.
laneNet.Layers
ans =
23×1 Layer array with layers:
1 'data' Image Input 227×227×3 images with 'zerocenter' normalization
2 'conv1' Convolution 96 11×11×3 convolutions with stride [4 4] and padding [0 0 0 0]
3 'relu1' ReLU ReLU
4 'norm1' Cross Channel Normalization cross channel normalization with 5 channels per element
5 'pool1' Max Pooling 3×3 max pooling with stride [2 2] and padding [0 0 0 0]
6 'conv2' Convolution 256 5×5×48 convolutions with stride [1 1] and padding [2 2 2 2]
7 'relu2' ReLU ReLU
8 'norm2' Cross Channel Normalization cross channel normalization with 5 channels per element
9 'pool2' Max Pooling 3×3 max pooling with stride [2 2] and padding [0 0 0 0]
10 'conv3' Convolution 384 3×3×256 convolutions with stride [1 1] and padding [1 1 1 1]
11 'relu3' ReLU ReLU
12 'conv4' Convolution 384 3×3×192 convolutions with stride [1 1] and padding [1 1 1 1]
13 'relu4' ReLU ReLU
14 'conv5' Convolution 256 3×3×192 convolutions with stride [1 1] and padding [1 1 1 1]
15 'relu5' ReLU ReLU
16 'pool5' Max Pooling 3×3 max pooling with stride [2 2] and padding [0 0 0 0]
17 'fc6' Fully Connected 4096 fully connected layer
18 'relu6' ReLU ReLU
19 'drop6' Dropout 50% dropout
20 'fcLane1' Fully Connected 16 fully connected layer
21 'fcLane1Relu' ReLU ReLU
22 'fcLane2' Fully Connected 6 fully connected layer
23 'output' Regression Output mean-squared-error with 'leftLane_a', 'leftLane_b', and 4 other responses
type detect_lane.m
function [laneFound, ltPts, rtPts] = detect_lane(frame, laneCoeffMeans, laneCoeffStds)
% From the networks output, compute left and right lane points in the
% image coordinates. The camera coordinates are described by the caltech
% mono camera model.
%#codegen
% A persistent object mynet is used to load the series 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.
persistent lanenet;
if isempty(lanenet)
lanenet = coder.loadDeepLearningNetwork('laneNet.mat', 'lanenet');
end
lanecoeffsNetworkOutput = lanenet.predict(permute(frame, [2 1 3]));
% Recover original coeffs by reversing the normalization steps
params = lanecoeffsNetworkOutput .* laneCoeffStds + laneCoeffMeans;
isRightLaneFound = abs(params(6)) > 0.5; %c should be more than 0.5 for it to be a right lane
isLeftLaneFound = abs(params(3)) > 0.5;
vehicleXPoints = 3:30; %meters, ahead of the sensor
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
function yWorld = computeBoundaryModel(model, xWorld)
yWorld = polyval(model, xWorld);
end
function tform = get_tformToImage
% Compute extrinsics based on camera setup
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
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
function intrinsicMat = intrinsicMatrix(FocalLength, Skew, PrincipalPoint)
intrinsicMat = ...
[FocalLength(1) , 0 , 0; ...
Skew , FocalLength(2) , 0; ...
PrincipalPoint(1), PrincipalPoint(2), 1];
end
Сеть вычисляет параметры a, b, и c, которые описывают параболическое уравнение для левых и правых контуров маршрута.
От этих параметров вычислите координаты X и Y, соответствующие положениям маршрута. Координаты должны быть сопоставлены, чтобы отобразить координаты. Функциональный detect_lane.m выполняет все эти расчеты. Сгенерируйте код CUDA для этой функции путем создания объекта настройки графического процессора кода для 'lib' цель и набор выходной язык на C++. Используйте coder.DeepLearningConfig функция, чтобы создать CuDNN объект настройки глубокого обучения и присвоение это к DeepLearningConfig свойство объекта настройки графического процессора кода. Запустите codegen команда.
cfg = coder.gpuConfig('lib'); cfg.DeepLearningConfig = coder.DeepLearningConfig('cudnn'); cfg.GenerateReport = true; cfg.TargetLang = 'C++'; codegen -args {ones(227,227,3,'single'),ones(1,6,'double'),ones(1,6,'double')} -config cfg detect_lane
Code generation successful: To view the report, open('codegen/lib/detect_lane/html/report.mldatx').
Серийная сеть сгенерирована как класс C++, содержащий массив 23 классов слоя.
class c_lanenet { public: int32_T batchSize; int32_T numLayers; real32_T *inputData; real32_T *outputData; MWCNNLayer *layers[23]; public: c_lanenet(void); void setup(void); void predict(void); void cleanup(void); ~c_lanenet(void); };
setup() метод класса настраивает указатели и выделяет память для каждого расположенного на слое объекта. predict() метод вызывает предсказание для каждого из этих 23 слоев в сети.
cnn_lanenet_conv* _ w и cnn_lanenet_conv* _ b файлы являются бинарными весами и смещают файл для слоя свертки в сети. cnn_lanenet_fc* _ w и cnn_lanenet_fc* _ b файлы являются бинарными весами и смещают файл для полносвязного слоя в сети.
codegendir = fullfile('codegen', 'lib', 'detect_lane'); dir(codegendir)
. cnn_lanenet0_0_conv4_w.bin .. cnn_lanenet0_0_conv5_b.bin .gitignore cnn_lanenet0_0_conv5_w.bin DeepLearningNetwork.cu cnn_lanenet0_0_data_offset.bin DeepLearningNetwork.h cnn_lanenet0_0_data_scale.bin DeepLearningNetwork.o cnn_lanenet0_0_fc6_b.bin MWCNNLayerImpl.cu cnn_lanenet0_0_fc6_w.bin MWCNNLayerImpl.hpp cnn_lanenet0_0_fcLane1_b.bin MWCNNLayerImpl.o cnn_lanenet0_0_fcLane1_w.bin MWCudaDimUtility.cu cnn_lanenet0_0_fcLane2_b.bin MWCudaDimUtility.hpp cnn_lanenet0_0_fcLane2_w.bin MWCustomLayerForCuDNN.cpp cnn_lanenet0_0_responseNames.txt MWCustomLayerForCuDNN.hpp codeInfo.mat MWCustomLayerForCuDNN.o codedescriptor.dmr MWElementwiseAffineLayer.cpp compileInfo.mat MWElementwiseAffineLayer.hpp defines.txt MWElementwiseAffineLayer.o detect_lane.a MWElementwiseAffineLayerImpl.cu detect_lane.cu MWElementwiseAffineLayerImpl.hpp detect_lane.h MWElementwiseAffineLayerImpl.o detect_lane.o MWElementwiseAffineLayerImplKernel.cu detect_lane_data.cu MWElementwiseAffineLayerImplKernel.o detect_lane_data.h MWFusedConvReLULayer.cpp detect_lane_data.o MWFusedConvReLULayer.hpp detect_lane_initialize.cu MWFusedConvReLULayer.o detect_lane_initialize.h MWFusedConvReLULayerImpl.cu detect_lane_initialize.o MWFusedConvReLULayerImpl.hpp detect_lane_ref.rsp MWFusedConvReLULayerImpl.o detect_lane_rtw.mk MWKernelHeaders.hpp detect_lane_terminate.cu MWTargetNetworkImpl.cu detect_lane_terminate.h MWTargetNetworkImpl.hpp detect_lane_terminate.o MWTargetNetworkImpl.o detect_lane_types.h buildInfo.mat examples cnn_api.cpp gpu_codegen_info.mat cnn_api.hpp html cnn_api.o interface cnn_lanenet0_0_conv1_b.bin mean.bin cnn_lanenet0_0_conv1_w.bin predict.cu cnn_lanenet0_0_conv2_b.bin predict.h cnn_lanenet0_0_conv2_w.bin predict.o cnn_lanenet0_0_conv3_b.bin rtw_proj.tmw cnn_lanenet0_0_conv3_w.bin rtwtypes.h cnn_lanenet0_0_conv4_b.bin
Среднее значение экспорта и значения станд. от обучившего сеть для использования во время выполнения.
codegendir = fullfile(pwd, 'codegen', 'lib','detect_lane'); fid = fopen(fullfile(codegendir,'mean.bin'), 'w'); A = [coeffMeans coeffStds]; fwrite(fid, A, 'double'); fclose(fid);
Скомпилируйте сетевой код при помощи основного файла. Основной файл использует OpenCV VideoCapture метод, чтобы считать системы координат из входного видео. Каждый кадр обрабатывается и классифицируется, пока больше систем координат не читается. Прежде, чем отобразить вывод для каждой системы координат, выходные параметры постобрабатываются при помощи detect_lane функция сгенерирована в detect_lane.cu.
type main_lanenet.cu
/* Copyright 2016 The MathWorks, Inc. */
#include <stdio.h>
#include <stdlib.h>
#include <cuda.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/core/types.hpp>
#include <opencv2/highgui.hpp>
#include <list>
#include <cmath>
#include "detect_lane.h"
using namespace cv;
void readData(float *input, Mat& orig, Mat & im)
{
Size size(227,227);
resize(orig,im,size,0,0,INTER_LINEAR);
for(int j=0;j<227*227;j++)
{
//BGR to RGB
input[2*227*227+j]=(float)(im.data[j*3+0]);
input[1*227*227+j]=(float)(im.data[j*3+1]);
input[0*227*227+j]=(float)(im.data[j*3+2]);
}
}
void addLane(float pts[28][2], Mat & im, int numPts)
{
std::vector<Point2f> iArray;
for(int k=0; k<numPts; k++)
{
iArray.push_back(Point2f(pts[k][0],pts[k][1]));
}
Mat curve(iArray, true);
curve.convertTo(curve, CV_32S); //adapt type for polylines
polylines(im, curve, false, CV_RGB(255,255,0), 2, LINE_AA);
}
void writeData(float *outputBuffer, Mat & im, int N, double means[6], double stds[6])
{
// get lane coordinates
boolean_T laneFound = 0;
float ltPts[56];
float rtPts[56];
detect_lane(outputBuffer, means, stds, &laneFound, ltPts, rtPts);
if (!laneFound)
{
return;
}
float ltPtsM[28][2];
float rtPtsM[28][2];
for(int k=0; k<28; k++)
{
ltPtsM[k][0] = ltPts[k];
ltPtsM[k][1] = ltPts[k+28];
rtPtsM[k][0] = rtPts[k];
rtPtsM[k][1] = rtPts[k+28];
}
addLane(ltPtsM, im, 28);
addLane(rtPtsM, im, 28);
}
void readMeanAndStds(const char* filename, double means[6], double stds[6])
{
FILE* pFile = fopen(filename, "rb");
if (pFile==NULL)
{
fputs ("File error",stderr);
return;
}
// obtain file size
fseek (pFile , 0 , SEEK_END);
long lSize = ftell(pFile);
rewind(pFile);
double* buffer = (double*)malloc(lSize);
size_t result = fread(buffer,sizeof(double),lSize,pFile);
if (result*sizeof(double) != lSize) {
fputs ("Reading error",stderr);
return;
}
for (int k = 0 ; k < 6; k++)
{
means[k] = buffer[k];
stds[k] = buffer[k+6];
}
free(buffer);
}
// Main function
int main(int argc, char* argv[])
{
float *inputBuffer = (float*)calloc(sizeof(float),227*227*3);
float *outputBuffer = (float*)calloc(sizeof(float),6);
if ((inputBuffer == NULL) || (outputBuffer == NULL)) {
printf("ERROR: Input/Output buffers could not be allocated!\n");
exit(-1);
}
// get ground truth mean and std
double means[6];
double stds[6];
readMeanAndStds("mean.bin", means, stds);
if (argc < 2)
{
printf("Pass in input video file name as argument\n");
return -1;
}
VideoCapture cap(argv[1]);
if (!cap.isOpened()) {
printf("Could not open the video capture device.\n");
return -1;
}
cudaEvent_t start, stop;
float fps = 0;
cudaEventCreate(&start);
cudaEventCreate(&stop);
Mat orig, im;
namedWindow("Lane detection demo",WINDOW_NORMAL);
while(true)
{
cudaEventRecord(start);
cap >> orig;
if (orig.empty()) break;
readData(inputBuffer, orig, im);
writeData(inputBuffer, orig, 6, means, stds);
cudaEventRecord(stop);
cudaEventSynchronize(stop);
char strbuf[50];
float milliseconds = -1.0;
cudaEventElapsedTime(&milliseconds, start, stop);
fps = fps*.9+1000.0/milliseconds*.1;
sprintf (strbuf, "%.2f FPS", fps);
putText(orig, strbuf, Point(200,30), FONT_HERSHEY_DUPLEX, 1, CV_RGB(0,0,0), 2);
imshow("Lane detection demo", orig);
if( waitKey(50)%256 == 27 ) break; // stop capturing by pressing ESC */
}
destroyWindow("Lane detection demo");
free(inputBuffer);
free(outputBuffer);
return 0;
}
if ~exist('./caltech_cordova1.avi', 'file') url = 'https://www.mathworks.com/supportfiles/gpucoder/media/caltech_cordova1.avi'; websave('caltech_cordova1.avi', url); end
if ispc setenv('MATLAB_ROOT', matlabroot); vcvarsall = mex.getCompilerConfigurations('C++').Details.CommandLineShell; setenv('VCVARSALL', vcvarsall); system('make_win_lane_detection.bat'); cd(codegendir); system('lanenet.exe ..\..\..\caltech_cordova1.avi'); else setenv('MATLAB_ROOT', matlabroot); system('make -f Makefile_lane_detection.mk'); cd(codegendir); system('./lanenet ../../../caltech_cordova1.avi'); end

