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

实现功能:

视频图像采集

图像预处理

车道线检测与识别



实现的效果:

基于机器视觉的车道线检测与追踪_第1张图片基于机器视觉的车道线检测与追踪_第2张图片




实现思路:

视频采集部分:摄像头采集分为单目采集与多目(双目),双目采集采用立体视觉方法,虽然对于恢复物体的深度信息比较容易,但是图像共轭像点的匹配问题,却在很大程度上限制了它的应用。本系统采用单目视觉方法,即通过一个摄像机来采集图像。虽然,这种方法得到的图像不包含物体的深度信息,但是,如果把时间看作第三轴线,我们便可以从连续的视频图像中得到准三维图像

图像预处理部分:包含图像灰度化,增强图像对比度,图像中值滤波,图像边缘增强,边缘检测信息与区域增长信息融合几部分。

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



实现代码:


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

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

        IplImage* pCutFrame = NULL;
        IplImage* pCutFrImg = NULL;
        IplImage* pCutBkImg = NULL;


        CvMat* pCutFrameMat = NULL;
        CvMat* pCutFrMat = NULL;
        CvMat* pCutBkMat = NULL;


      //  CvCapture* pCapture = NULL;

        CvMemStorage* storage = cvCreateMemStorage();
        CvSeq* lines = NULL;
	 int nFrmNum = 0;
        int CutHeight = 250;

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

if (!(pCapture = cvCaptureFromFile("//home//wengkl//Downloads//car//3.mov"))){
        fprintf(stderr, "Can not open video file\n");
        return -2;
  	  }
    //每次读取一桢的视频
    while (pFrame = cvQueryFrame(pCapture)){
		...
	}

设置感兴趣区域:

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

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

  if (nFrmNum == 1){
            pCutFrame = cvCreateImage(cvSize(pFrame->width, pFrame->height - CutHeight), pFrame->depth, pFrame->nChannels);
            cvCopy(pFrame, pCutFrame, 0);
            pCutBkImg = cvCreateImage(cvSize(pCutFrame->width, pCutFrame->height), IPL_DEPTH_8U, 1);
            pCutFrImg = cvCreateImage(cvSize(pCutFrame->width, pCutFrame->height), IPL_DEPTH_8U, 1);


            pCutBkMat = cvCreateMat(pCutFrame->height, pCutFrame->width, CV_32FC1);
            pCutFrMat = cvCreateMat(pCutFrame->height, pCutFrame->width, CV_32FC1);
            pCutFrameMat = cvCreateMat(pCutFrame->height, pCutFrame->width, CV_32FC1);


            cvCvtColor(pCutFrame, pCutBkImg, CV_BGR2GRAY);
            cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY);


            cvConvert(pCutFrImg, pCutFrameMat);
            cvConvert(pCutFrImg, pCutFrMat);
            cvConvert(pCutFrImg, pCutBkMat);
        }

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

获得剪切图

  cvCopy(pFrame, pCutFrame, 0);
前景图转换为灰度图

cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY);
            cvConvert(pCutFrImg, pCutFrameMat);
图像平滑处理,计算两幅图像差

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

            cvAbsDiff(pCutFrameMat, pCutBkMat, pCutFrMat);
二值化前景图

cvThreshold(pCutFrMat, pCutFrImg, 35, 255.0, CV_THRESH_BINARY);
形态学滤波

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


更新背景

            cvRunningAvg(pCutFrameMat, pCutBkMat, 0.003, 0);

            cvConvert(pCutBkMat, pCutBkImg);
canny变换

            cvCanny(pCutFrImg, pCutFrImg, 50, 100);
霍夫变换直线检测,并在图像上标记出来,打印出当前帧数与直线斜率

            lines = cvHoughLines2(pCutFrImg, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 100, 30, 15);
            printf("line = %d\n",lines->total);
            for (int i = 0; itotal; i++){
                CvPoint* line = (CvPoint* )cvGetSeqElem(lines, i);
            // cvLine(pCutFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA);
                double k = ((line[0].y - line[1].y)*1.0 / (line[0].x - line[1].x));
                cout<<"nFrmNum "<    cvReleaseImage(&pCutFrImg);
    cvReleaseImage(&pCutBkImg);
    cvReleaseImage(&pCutFrame);
    cvReleaseMat(&pCutFrameMat);
    cvReleaseMat(&pCutFrMat);
    cvReleaseMat(&pCutBkMat);



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

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

所以附上部分V4L2的代码
int grappicture()
{
    int grab;
    BITMAPFILEHEADER   bf;
    BITMAPINFOHEADER   bi;


    FILE * fp1,* fp2;


    fp1 = fopen(BMP, "wb");
    fp2 = fopen(YUV, "wb");
    if(!fp1)
    {
        printf("open "BMP"error\n");
        return(FALSE);
    }




    if(!fp2)
    {
        printf("open "YUV"error\n");
        return(FALSE);
    }




    //Set BITMAPINFOHEADER
    bi.biSize = 40;
    bi.biWidth = IMAGEWIDTH;
    bi.biHeight = IMAGEHEIGHT;
    bi.biPlanes = 1;
    bi.biBitCount = 24;
    bi.biCompression = 0;
    bi.biSizeImage = IMAGEWIDTH*IMAGEHEIGHT*3;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;




    //Set BITMAPFILEHEADER
    bf.bfType = 0x4d42;
    bf.bfSize = 54 + bi.biSizeImage;
    bf.bfReserved = 0;
    bf.bfOffBits = 54;


    grab=v4l2_grab();
//    printf("GRAB is %d\n",grab);
    printf("grab ok\n");
    fwrite(buffers[0].start, IMAGEHEIGHT*IMAGEWIDTH*2,1, fp2);
    printf("save "YUV"OK\n");


    yuyv_2_rgb888();
    fwrite(&bf, 14, 1, fp1);
    fwrite(&bi, 40, 1, fp1);
    fwrite(frame_buffer, bi.biSizeImage, 1, fp1);
    printf("save "BMP"OK\n");
    close_v4l2();


    return(TRUE);
}



然后只需将源码程序修改如下部分:
在函数开头初始化摄像头
        if(!initcamera())
         {
             printf("init failure");
         }

pFrame = cvQueryFrame(pCapture)
修改为
int grapp=grappicture();
if(grapp==0)

{
cout<<"grapcamera failue !"<


移植问题与总结:

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

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

你可能感兴趣的:(基于机器视觉的车道线检测与追踪)