实现功能:
视频图像采集 图像预处理 车道线检测与识别
实现的效果:
实现思路: 视频采集部分:摄像头采集分为单目采集与多目(双目),双目采集采用立体视觉方法,虽然对于恢复物体的深度信息比较容易,但是图像共轭像点的匹配问题,却在很大程度上限制了它的应用。本系统采用单目视觉方法,即通过一个摄像机来采集图像。
虽然,这种方法得到的图像不包含物体的深度信息,但是,如果把时间看作第三轴线,我们便可以从连续的视频图像中得到准三维图像图像预处理部分:包含图像灰度化,增强图像对比度,图像中值滤波,图像边缘增强,边缘检测信息与区域增长信息融合几部分。
车道线检测识别:有多种方法,如最小二乘法,Hough变换,Radon变换等,本系统使用opencv自带直线检测库霍夫变换。
实现代码:
读取视频检测其中车道线部分核心代码; 用到的结构体声明以及变量初始化:
[cpp] view plain copy 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;
[cpp] view plain copy <>'white-space:pre'> int nFrmNum = 0; int CutHeight = 250;
打开已有视频(或摄像头),按帧读取视频
[cpp] view plain copy if (!(pCapture = cvCaptureFromFile('//home//wengkl//Downloads//car//3.mov'))){ fprintf(stderr, 'Can not open video file\n'); return -2; } //每次读取一桢的视频 while (pFrame = cvQueryFrame(pCapture)){
[cpp] view plain copy <>'white-space:pre'> ...
[cpp] view plain copy <>'white-space:pre'> }
设置感兴趣区域:
[cpp] view plain copy cvSetImageROI(pFrame, cvRect(0, CutHeight, pFrame->width, pFrame->height - CutHeight));
如果是第一帧,分配内存空间
[cpp] view plain copy 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); }
如果不是第一帧,进行各类检测如下: 获得剪切图
[cpp] view plain copy cvCopy(pFrame, pCutFrame, 0);
前景图转换为灰度图
[cpp] view plain copy cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY); cvConvert(pCutFrImg, pCutFrameMat);
图像平滑处理,计算两幅图像差
[cpp] view plain copy cvSmooth(pCutFrameMat, pCutFrameMat, CV_GAUSSIAN, 3, 0, 0.0); cvAbsDiff(pCutFrameMat, pCutBkMat, pCutFrMat);
二值化前景图
[cpp] view plain copy cvThreshold(pCutFrMat, pCutFrImg, 35, 255.0, CV_THRESH_BINARY);
形态学滤波
[cpp] view plain copy cvErode(pCutFrImg, pCutFrImg, 0, 1); cvDilate(pCutFrImg, pCutFrImg, 0, 1);
更新背景
[cpp] view plain copy cvRunningAvg(pCutFrameMat, pCutBkMat, 0.003, 0); cvConvert(pCutBkMat, pCutBkImg);
canny变换
[cpp] view plain copy cvCanny(pCutFrImg, pCutFrImg, 50, 100);
霍夫变换直线检测,并在图像上标记出来,打印出当前帧数与直线斜率
[cpp] view plain copy 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 '<>' 's k = '<><> if(!(abs(k)<> { cvLine(pFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA); cvLine(pCutFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA); } }
释放内存
[cpp] view plain copy cvReleaseImage(&pCutFrImg); cvReleaseImage(&pCutBkImg); cvReleaseImage(&pCutFrame); cvReleaseMat(&pCutFrameMat); cvReleaseMat(&pCutFrMat); cvReleaseMat(&pCutBkMat);
由于本系统是移植到FriendlyARM上使用的,并没有移植GUI的库,不能直接用
[cpp] view plain copy CvCapture* pCapture = cvCreateCameraCapture(-1);
所以附上部分V4L2的代码
[cpp] view plain copy 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); }
然后只需将源码程序修改如下部分: 在函数开头初始化摄像头
[cpp] view plain copy if(!initcamera()) { printf('init failure'); }
将 [cpp] view plain copy pFrame = cvQueryFrame(pCapture)
修改为
[cpp] view plain copy int grapp=grappicture(); if(grapp==0) { cout'grapcamera failue !'<> } IplImage* pFrame= cvLoadImage('image_bmp.bmp',-1);//读取原彩色图
移植问题与总结: 在PC下跑这部分代码是很流畅的,但是一旦移植到友善之臂下,摄像头每次保存图片并解析的速度就会降低;加上摄像头的分辨率比较低,导致在ARM上车道线的识别会有误差 另外,目前代码检测的车道线均为直线或类似直线,当车速过快或者转弯角度过大时,检测出的斜率会混乱。
|