基于GPU编码器的车道检测优化

此示例显示如何从深度学习网络生成CUDA®代码,该网络由系列网络对象。在本例中,序列网络是一个卷积神经网络,可以从图像中检测和输出车道标记的边界。

先决条件

  • CUDA支持NVIDIA®GPU,具有3.2或更高的计算能力。

  • NVIDIA CUDA工具包和驱动程序。

  • 英伟达cuDNN图书馆。

  • 用于视频读取和图像显示操作的OpenCV库。

  • 编译器和库的环境变量。有关编译器和库支持的版本的信息,请参见万博1manbetx第三方产品s manbetx 845.有关设置环境变量,请参见设置前提产品s manbetx 845

  • 用于深度学习库的GPU编码器接口支持包。要安装此支持包,请使用万博1manbetx附加资源管理器

验证GPU环境

使用coder.checkGpuInstall函数来验证运行此示例所需的编译器和库是否正确设置。

envCfg = coder.gpuEnvConfig (“主机”);envCfg。DeepLibTarget =“cudnn”;envCfg。DeepCodegen = 1;envCfg。安静= 1;coder.checkGpuInstall (envCfg);

获得预训练序列网络

[laneNet,coeffeans,coeffStds]=getlanedtectionnetwork();

该网络将图像作为输入,并输出两条车道边界,分别对应于ego车辆的左车道和右车道。每条车道边界由抛物线方程表示:$y=ax^2+bx+c$,其中y是横向偏移量,x是与车辆的纵向距离。网络输出每个车道的三个参数a、b和c。网络结构与AlexNet类似,只是最后几层被较小的完全连接层和回归输出层替换。

ans = 23x1图层数组227 x227x3数据的图像输入图像的zerocenter正常化2 conv1卷积96年11 x11x3旋转步[4 4]和填充[0 0 0 0]3‘relu1 ReLU ReLU 4“norm1”横通道正常化横通道规范化5频道每个元素5“pool1”马克斯池3 x3 Max池步(2 - 2)和填充[0 0 0 0]6conv2卷积256 5 x5x48旋转步[1]和填充(2 2 2 2)7的relu2 ReLU ReLU 8 norm2的横通道正常化横通道正常化与5频道/元素9“pool2”马克斯池3 x3马克斯池步(2 - 2)和填充[0 0 0 0]10 conv3卷积384 3 x3x256[1]和隆起与进步填充[1 1 1 1]11的relu3 ReLU ReLU 12 conv4卷积384 3 x3x192旋转步[1]和填充[1 1 1 1]13的relu4 ReLU ReLU 14 conv5卷积256 3 x3x192旋转步[1]和填充[1 1 1 1]15 ' relu5 ReLU ReLU 16“pool5”马克斯池3 x3 Max池步(2 - 2)和填充[0 0 0 0]17 fc6完全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

检查主要入口点功能

类型探测车道
函数[laneFound,ltPts,rtPts]=从网络输出中检测车道(帧,laneCoeffMeans,laneCoeffStds)%1,计算%图像坐标中的左右车道点。摄像机坐标由加州理工学院%mono摄像机模型描述#codegen%mynet持久对象用于加载series网络对象。%在第一次调用此函数时,将构造持久对象并%setup。后续调用该函数时,将重用同一对象%以调用输入上的predict,从而避免重建和重新加载%网络对象。宿存花被;如果isempty(lanenet)lanenet=coder.loadDeepLearningNetwork('lanenet.mat','lanenet');end lanecoeffsNetworkOutput=lanenet.predict(permute(frame[2 1 3]);%通过反转规范化步骤params=lanecoeffsNetworkOutput.*laneCoeffStds+lanecoeffsmeans恢复原始系数;isRightLaneFound=abs(参数(6))>0.5;%c应大于0.5,以使其成为右车道isLeftLaneFound=abs(参数(3))>0.5;车辆积分=3:30;%米,传感器前面ltPts=编码器。零拷贝(零(28,2,'single');rtPts=编码器.nullcopy(零(28,2,'single');如果isRightLaneFound和isLeftLaneFound rtBoundary=params(4:6);rt_y=计算边界模型(rtBoundary,车辆点);ltBoundary=参数(1:3);lt_y=计算边界模型(ltBoundary,车辆点);%可视化ego车辆的车道边界tform=获取\u tformToImage;%将车辆映射到图像坐标ltPts=t形式转换点相反([vehicleXPoints',lt_y']);rtPts=t形式转换点相反([vehicleXPoints',rt_y']);laneFound=true;else-laneFound=false;结束函数yWorld=computeBoundaryModel(model,xWorld)yWorld=polyval(model,xWorld);结束函数tform=get_tformToImage%基于相机设置计算外部偏航=0;螺距=14;%摄像机的俯仰角(以度为单位)滚动=0;平移=平移向量(偏航、俯仰、横摇);旋转=旋转矩阵(偏航、俯仰、横摇);%构造一个摄像机矩阵聚焦长度=[309.4362344.2161];主点=[318.9034257.5352];歪斜=0;camMatrix=[旋转;平移]*内部矩阵(聚焦长度,…倾斜,主点);%将camMatrix转换为二维单应性tform2D=[camMatrix(1,:);camMatrix(2,:);camMatrix(4,:)];%下降Z t形式=投影2D(t或M2D);tform=tform.invert();端部功能平移=平移向量(偏航、俯仰、滚转)传感器位置=[0];高度=2.1798;%离地面旋转矩阵的安装高度(米)=(…旋转(偏航)*…%最后一次旋转旋转(90节)*…旋转(滚转)…%第一次旋转);%通过添加平移sl=传感器位置来调整传感器位置;转换单位=[sl(2),sl(1),高度];平移=平移世界单位*旋转矩阵;结束%-------------------------------------------------------------------------------------绕X轴旋转%函数R=rotX(a)a=deg2rad(a);R=[…10 0;0 cos(a)-sin(a);0 sin(a)cos(a)];结束%-------------------------------------------------------------------------------------绕Y轴旋转%函数R=rotY(a)a=deg2rad(a);R=[…cos(a)0sin(a);0110;-sin(a)0cos(a)];结束%-------------------------------------------------------------------------------------绕Z轴旋转%函数R=rotZ(a)a=deg2rad(a);R=[…cos(a)-sin(a)0;sin(a)cos(a)0;01];end%-------------------------------------------------------------------------------------%给定偏航、俯仰和横摇,确定适当的欧拉%角度以及它们应用于%将摄像头坐标系与车辆坐标系%对齐的顺序。生成的矩阵是一个旋转矩阵,它与平移向量一起定义相机的外部参数。函数旋转=旋转矩阵(偏航、俯仰、滚转)旋转=(…旋转(180)*…%上次旋转:点Z向上旋转(-90)*…%X-Y交换旋转(偏航)*…%点相机向前旋转(90俯仰)*…%取消俯仰旋转(滚转).%第一次旋转:“取消滚转”);结束函数intrinsicMat=intrinsicMatrix(焦点长度、偏移、主点)intrinsicMat=。。。[FocalLength(1),0,0;…歪斜,FocalLength(2),0;…主点(1),主点(2),1];终止

生成网络代码和后处理代码

网络计算参数a, b和c,这些参数描述了左右车道边界的抛物线方程。

根据这些参数,计算车道位置对应的x和y坐标。坐标必须映射到图像坐标。这个函数探测车道执行所有这些计算。通过为“lib”将目标语言设置为c++。使用编码器。DeepLearningConfig函数创建CuDNN深度学习配置对象并将其分配给DeepLearningConfigGPU代码配置对象的属性。运行编码基因命令

cfg=coder.gpuConfig(“lib”);cfg。DeepLearningConfig =编码器。DeepLearningConfig (“cudnn”);cfg。GenerateReport = true;cfg。TargetLang =“C++”;编码基因-args{one(227227,3,'single')、one(1,6,'double')、one(1,6,'double'))-配置cfgdetect_lane
代码生成成功:要查看报告,打开('codegen/lib/detect_lane/html/report.mldatx')。

生成代码描述

该系列网络是作为包含23层类的数组的C++类生成的。

c_lanenet{public: int32_T batchSize;int32_T numLayers;real32_T * inputData;real32_T * outputData;MWCNNLayer *层[23];公众:c_lanenet(无效);无效的设置(无效);无效的预测(无效);无效的清理(无效);real32_T * getInputDataPointer (); real32_T *getOutputDataPointer(); ~c_lanenet(void); };

这个设置()方法为每个层对象建立句柄并分配内存。这个预测方法对网络中的23层中的每一层调用预测。

cnn_lanenet_conv*_w和cnn_lanenet_conv*_b文件是网络中卷积层的二进制权重和偏差文件。cnn_lanenet_fc*_w和cnn_lanenet_fc*_b文件是网络中完全连接层的二进制权重和偏差文件。

codegendir = fullfile (“codegen”,“lib”,“探测车道”);署长(性别歧视)
.cnn_lanenet_data_offset.bin . .cnn_lanenet_data_scale.bin DeepLearningNetwork。cnn_lanenet_fc6_b.bin DeepLearningNetwork.h cnn_lanenet_fc6_w.bin DeepLearningNetwork.ho cnn_lanenet_fcLane1_b.bin MWCNNLayerImpl。cnn_lanenet_fclone1_w .bin MWCNNLayerImpl.hpp cnn_lanenet_fclone2_b .bino cnn_lanenet_fcLane2_w.bin MWCudaDimUtility。cu cnn_lanenet_responsename .txt mwcudadimultiple .hpp codeInfo. txt。垫MWCudaDimUtility。o codedescriptor。dmr mwelementwiseaffinlayer .cpp coder_array.h mwelementwiseaffinlayer .hpp compileInfo。垫MWElementwiseAffineLayer。o detect_lane。MWElementwiseAffineLayerImpl。铜detect_lane。hpp detect_lane.h MWElementwiseAffineLayerImpl. cu MWElementwiseAffineLayerImpl. cuo detect_lane。o MWElementwiseAffineLayerImplKernel。铜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_rtwutil.cu MWTargetNetworkImpl.cu detect_lane_rtwutil.h MWTargetNetworkImpl.hpp detect_lane_rtwutil.o MWTargetNetworkImpl.o detect_lane_terminate.cu buildInfo.mat detect_lane_terminate.h cnn_api.cpp detect_lane_terminate.o cnn_api.hpp detect_lane_types.h cnn_api.o examples cnn_lanenet_conv1_b.bin gpu_codegen_info.mat cnn_lanenet_conv1_w.bin html cnn_lanenet_conv2_b.bin interface cnn_lanenet_conv2_w.bin predict.cu cnn_lanenet_conv3_b.bin predict.h cnn_lanenet_conv3_w.bin predict.o cnn_lanenet_conv4_b.bin rtw_proj.tmw cnn_lanenet_conv4_w.bin rtwtypes.h cnn_lanenet_conv5_b.bin cnn_lanenet_conv5_w.bin

为后期处理输出生成附加文件

从经过培训的网络中导出平均值和标准值,以便在执行过程中使用。

codegendir = fullfile (pwd,“codegen”,“lib”,“探测车道”);fid = fopen (fullfile (codegendir“mean.bin”),' w ');A=[coeffStds];fwrite(fid,A,“双”);fclose(fid);

主文件

使用主文件编译网络代码。主文件使用OpenCVVideoCapture方法从输入的视频中读取帧。每一帧都被处理和分类,直到没有更多的帧被读取。在显示每一帧的输出之前,输出将使用detect_lane函数中生成detect_lane.cpp

类型main_lanenet.cpp
/* The MathWorks, Inc. */ #include  #include  #include  #include "opencv2/opencv.hpp" #include  #include  #include "detect_lane.h" using namespace cv;void readData(float *input, mat&orig, Mat& im) {Size Size(227,227); / /输入数据调整(源自,im,大小,0,0,INTER_LINEAR);(int j = 0; < 227 * 227; j + +) {/ / BGR RGB输入[2 * 227 * 227 + j] =(浮动)(im.data [j * 3 + 0]);输入(1 * 227 * 227 + j] =(浮动)(im.data [j * 3 + 1]);输入[0 * 227 * 227 + j] =(浮动)(im.data [j * 3 + 2]);}} void addLane(float pts[28][2], Mat & im, int numPts) {std::vector 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, CV_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",CV_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, cvPoint(200,30), CV_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; }

下载示例视频

如果~存在(”。/ caltech_cordova1.avi ',“文件”) url ='//www.tianjin-qmedu.com/万博1manbetxsupportfiles/gpucoder/media/caltech_cordova1.avi'; 韦伯萨夫(“caltech_cordova1.avi”url);结束

生成可执行文件

如果ispc setenv(“MATLAB_ROOT”, matlabroot);vcvarsall = mex.getCompilerConfigurations (“C++”).Details.CommandLineShell;塞滕夫(“VCVARSALL”,vcvarsall);[~,~]=系统(“make_win_lane_detection.bat”);cd(codegendir);[status,cmdout]=系统(“lanenet.exe…”\caltech_cordova1.avi“);其他的塞滕夫(“MATLAB_ROOT”, matlabroot);(~。~)=系统('make-f Makefile\u lane\u detection.mk');cd(codegendir);[status,cmdout]=系统(“./lanenet../../../caltech_cordova1.avi”);结束

输入截图

输出屏幕截图

另见

||||

相关的话题