分享

基于机器视觉的车道线检测与追踪

 taotao_2016 2018-01-23


实现功能:


视频图像采集

图像预处理

车道线检测与识别



实现的效果:


实现思路:

视频采集部分:摄像头采集分为单目采集与多目(双目),双目采集采用立体视觉方法,虽然对于恢复物体的深度信息比较容易,但是图像共轭像点的匹配问题,却在很大程度上限制了它的应用。本系统采用单目视觉方法,即通过一个摄像机来采集图像。


虽然,这种方法得到的图像不包含物体的深度信息,但是,如果把时间看作第三轴线,我们便可以从连续的视频图像中得到准三维图像图像预处理部分:包含图像灰度化,增强图像对比度,图像中值滤波,图像边缘增强,边缘检测信息与区域增长信息融合几部分。


车道线检测识别:有多种方法,如最小二乘法,Hough变换,Radon变换等,本系统使用opencv自带直线检测库霍夫变换。



实现代码:


读取视频检测其中车道线部分核心代码;

用到的结构体声明以及变量初始化:


[cpp] view plain copy

  1.   IplImage* pCutFrame = NULL;  

  2.   IplImage* pCutFrImg = NULL;  

  3.   IplImage* pCutBkImg = NULL;  

  4.   

  5.   

  6.   CvMat* pCutFrameMat = NULL;  

  7.   CvMat* pCutFrMat = NULL;  

  8.   CvMat* pCutBkMat = NULL;  

  9.   

  10.   

  11. //  CvCapture* pCapture = NULL;  

  12.   

  13.   CvMemStorage* storage = cvCreateMemStorage();  

  14.   CvSeq* lines = NULL;  

[cpp] view plain copy

  1. <>'white-space:pre'>     int nFrmNum = 0;  

  2.         int CutHeight = 250;  


打开已有视频(或摄像头),按帧读取视频



[cpp] view plain copy

  1. if (!(pCapture = cvCaptureFromFile('//home//wengkl//Downloads//car//3.mov'))){  

  2.         fprintf(stderr, 'Can not open video file\n');  

  3.         return -2;  

  4.       }  

  5.     //每次读取一桢的视频  

  6.     while (pFrame = cvQueryFrame(pCapture)){  

[cpp] view plain copy

  1. <>'white-space:pre'>        ...  

[cpp] view plain copy

  1. <>'white-space:pre'>    }  



设置感兴趣区域:


[cpp] view plain copy

  1. cvSetImageROI(pFrame, cvRect(0, CutHeight, pFrame->width, pFrame->height - CutHeight));  



如果是第一帧,分配内存空间


[cpp] view plain copy

  1. if (nFrmNum == 1){  

  2.           pCutFrame = cvCreateImage(cvSize(pFrame->width, pFrame->height - CutHeight), pFrame->depth, pFrame->nChannels);  

  3.           cvCopy(pFrame, pCutFrame, 0);  

  4.           pCutBkImg = cvCreateImage(cvSize(pCutFrame->width, pCutFrame->height), IPL_DEPTH_8U, 1);  

  5.           pCutFrImg = cvCreateImage(cvSize(pCutFrame->width, pCutFrame->height), IPL_DEPTH_8U, 1);  

  6.   

  7.   

  8.           pCutBkMat = cvCreateMat(pCutFrame->height, pCutFrame->width, CV_32FC1);  

  9.           pCutFrMat = cvCreateMat(pCutFrame->height, pCutFrame->width, CV_32FC1);  

  10.           pCutFrameMat = cvCreateMat(pCutFrame->height, pCutFrame->width, CV_32FC1);  

  11.   

  12.   

  13.           cvCvtColor(pCutFrame, pCutBkImg, CV_BGR2GRAY);  

  14.           cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY);  

  15.   

  16.   

  17.           cvConvert(pCutFrImg, pCutFrameMat);  

  18.           cvConvert(pCutFrImg, pCutFrMat);  

  19.           cvConvert(pCutFrImg, pCutBkMat);  

  20.       }  



如果不是第一帧,进行各类检测如下:

获得剪切图


[cpp] view plain copy

  1. cvCopy(pFrame, pCutFrame, 0);  

前景图转换为灰度图



[cpp] view plain copy

  1. cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY);  

  2.             cvConvert(pCutFrImg, pCutFrameMat);  

图像平滑处理,计算两幅图像差



[cpp] view plain copy

  1. cvSmooth(pCutFrameMat, pCutFrameMat, CV_GAUSSIAN, 3, 0, 0.0);  

  2.   

  3. cvAbsDiff(pCutFrameMat, pCutBkMat, pCutFrMat);  

二值化前景图



[cpp] view plain copy

  1. cvThreshold(pCutFrMat, pCutFrImg, 35, 255.0, CV_THRESH_BINARY);  

形态学滤波



[cpp] view plain copy

  1. cvErode(pCutFrImg, pCutFrImg, 0, 1);  

  2.  cvDilate(pCutFrImg, pCutFrImg, 0, 1);  


更新背景



[cpp] view plain copy

  1. cvRunningAvg(pCutFrameMat, pCutBkMat, 0.003, 0);  

  2.   

  3. cvConvert(pCutBkMat, pCutBkImg);  

canny变换



[cpp] view plain copy

  1. cvCanny(pCutFrImg, pCutFrImg, 50, 100);  

霍夫变换直线检测,并在图像上标记出来,打印出当前帧数与直线斜率



[cpp] view plain copy

  1. lines = cvHoughLines2(pCutFrImg, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 100, 30, 15);  

  2. printf('line = %d\n',lines->total);  

  3. for (int i = 0; itotal; i++){  

  4.     CvPoint* line = (CvPoint* )cvGetSeqElem(lines, i);  

  5. // cvLine(pCutFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA);  

  6.     double k = ((line[0].y - line[1].y)*1.0 / (line[0].x - line[1].x));  

  7.     cout'nFrmNum '<>' 's k = '<><>

  8.     if(!(abs(k)<>

  9.     {  

  10.         cvLine(pFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA);  

  11.         cvLine(pCutFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA);  

  12.     }  

  13. }  



释放内存


[cpp] view plain copy

  1. cvReleaseImage(&pCutFrImg);  

  2. cvReleaseImage(&pCutBkImg);  

  3. cvReleaseImage(&pCutFrame);  

  4. cvReleaseMat(&pCutFrameMat);  

  5. cvReleaseMat(&pCutFrMat);  

  6. cvReleaseMat(&pCutBkMat);  





由于本系统是移植到FriendlyARM上使用的,并没有移植GUI的库,不能直接用


  1. [cpp] view plain copy

    1. CvCapture* pCapture = cvCreateCameraCapture(-1);    

所以附上部分V4L2的代码

[cpp] view plain copy

  1. int grappicture()  

  2. {  

  3.     int grab;  

  4.     BITMAPFILEHEADER   bf;  

  5.     BITMAPINFOHEADER   bi;  

  6.   

  7.   

  8.     FILE * fp1,* fp2;  

  9.   

  10.   

  11.     fp1 = fopen(BMP, 'wb');  

  12.     fp2 = fopen(YUV, 'wb');  

  13.     if(!fp1)  

  14.     {  

  15.         printf('open 'BMP'error\n');  

  16.         return(FALSE);  

  17.     }  

  18.   

  19.   

  20.   

  21.   

  22.     if(!fp2)  

  23.     {  

  24.         printf('open 'YUV'error\n');  

  25.         return(FALSE);  

  26.     }  

  27.   

  28.   

  29.   

  30.   

  31.     //Set BITMAPINFOHEADER  

  32.     bi.biSize = 40;  

  33.     bi.biWidth = IMAGEWIDTH;  

  34.     bi.biHeight = IMAGEHEIGHT;  

  35.     bi.biPlanes = 1;  

  36.     bi.biBitCount = 24;  

  37.     bi.biCompression = 0;  

  38.     bi.biSizeImage = IMAGEWIDTH*IMAGEHEIGHT*3;  

  39.     bi.biXPelsPerMeter = 0;  

  40.     bi.biYPelsPerMeter = 0;  

  41.     bi.biClrUsed = 0;  

  42.     bi.biClrImportant = 0;  

  43.   

  44.   

  45.   

  46.   

  47.     //Set BITMAPFILEHEADER  

  48.     bf.bfType = 0x4d42;  

  49.     bf.bfSize = 54 + bi.biSizeImage;  

  50.     bf.bfReserved = 0;  

  51.     bf.bfOffBits = 54;  

  52.   

  53.   

  54.     grab=v4l2_grab();  

  55. //    printf('GRAB is %d\n',grab);  

  56.     printf('grab ok\n');  

  57.     fwrite(buffers[0].start, IMAGEHEIGHT*IMAGEWIDTH*2,1, fp2);  

  58.     printf('save 'YUV'OK\n');  

  59.   

  60.   

  61.     yuyv_2_rgb888();  

  62.     fwrite(&bf, 14, 1, fp1);  

  63.     fwrite(&bi, 40, 1, fp1);  

  64.     fwrite(frame_buffer, bi.biSizeImage, 1, fp1);  

  65.     printf('save 'BMP'OK\n');  

  66.     close_v4l2();  

  67.   

  68.   

  69.     return(TRUE);  

  70. }  




然后只需将源码程序修改如下部分:

在函数开头初始化摄像头

[cpp] view plain copy

  1. if(!initcamera())  

  2.  {  

  3.      printf('init failure');  

  4.  }  


[cpp] view plain copy

  1. pFrame = cvQueryFrame(pCapture)  

修改为

[cpp] view plain copy

  1. int grapp=grappicture();  

  2. if(grapp==0)  

  3.   

  4. {  

  5. cout'grapcamera failue !'<>

  6. }  

  7.  IplImage*  pFrame= cvLoadImage('image_bmp.bmp',-1);//读取原彩色图  


移植问题与总结:

在PC下跑这部分代码是很流畅的,但是一旦移植到友善之臂下,摄像头每次保存图片并解析的速度就会降低;加上摄像头的分辨率比较低,导致在ARM上车道线的识别会有误差

另外,目前代码检测的车道线均为直线或类似直线,当车速过快或者转弯角度过大时,检测出的斜率会混乱。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多