opencv車道線檢測(cè)的實(shí)現(xiàn)方法
車道線檢測(cè),需要完成以下功能:
- 圖像裁剪:通過設(shè)定圖像ROI區(qū)域,拷貝圖像獲得裁剪圖像
- 反透視變換:用的是室外采集到的視頻,沒有對(duì)應(yīng)的變換矩陣。所以建立二維坐標(biāo),通過四點(diǎn)映射的方法計(jì)算矩陣,進(jìn)行反透視變化。后因ROI區(qū)域的設(shè)置易造成變換矩陣獲取困難和插值得到的透視圖效果不理想,故沒應(yīng)用
- 二值化:先變化為灰度圖,然后設(shè)定閾值直接變成二值化圖像。
- 形態(tài)學(xué)濾波:對(duì)二值化圖像進(jìn)行腐蝕,去除噪點(diǎn),然后對(duì)圖像進(jìn)行膨脹,彌補(bǔ)對(duì)車道線的腐蝕。
- 邊緣檢測(cè):canny變化、sobel變化和laplacian變化中選擇了效果比較好的canny變化,三者在代碼中均可以使用,canny變化效果稍微好一點(diǎn)。
- 直線檢測(cè):實(shí)現(xiàn)了兩種方法 1>使用opencv庫(kù)封裝好的霍夫直線檢測(cè)函數(shù),在原圖對(duì)應(yīng)區(qū)域用紅線描出車道線 2>自己寫一種直線檢測(cè),在頭文件中,遍歷ROI區(qū)域進(jìn)行特定角度范圍的直線檢測(cè)。兩種方法均可在視頻中體現(xiàn),第一種方法運(yùn)行效率較快。
- 按鍵控制:空格暫停,其余鍵退出,方便調(diào)試和截圖。
實(shí)現(xiàn)的效果
在亮度良好道路條件良好的情況下,檢測(cè)車前區(qū)域的車道線實(shí)現(xiàn)比較成功,排除掉高速護(hù)欄的影響,而且原圖像還能完整體現(xiàn)。
車子行駛在高速公路大型彎道上,可以在一定角度范圍內(nèi)認(rèn)定車道線仍是直線,檢測(cè)出為直線。
車子切換過程中只有一根車道線被識(shí)別,但是穩(wěn)定回變換車道后,實(shí)現(xiàn)效果良好。減速線為黃色,二值化是也被過濾,沒造成影響。
剛進(jìn)入隧道時(shí),攝像機(jī)光源基本處于高光狀態(tài),拍攝亮度基本不變,二值化圖像時(shí)情況良好,噪聲比較多但是沒產(chǎn)生多大線狀影響;當(dāng)攝像頭自動(dòng)調(diào)節(jié)亮度,圖像亮度變低,二值化時(shí)同一閾值把車道線給過濾掉,造成無法識(shí)別車道線的現(xiàn)象。
在道路損壞的情況下,由于閾值一定,基本上檢測(cè)不出車道線。
結(jié)論
實(shí)現(xiàn)的功能:實(shí)現(xiàn)了車道線檢測(cè)的基本功能,反透視變換矩陣實(shí)現(xiàn)了但效果不太理想,使用自己寫的直線檢測(cè)部分,車道線識(shí)別抗干擾能力較強(qiáng)。
缺點(diǎn):整個(gè)識(shí)別系統(tǒng)都是固定的參數(shù),只能在特定的環(huán)境產(chǎn)生良好的效果。
改進(jìn)空間:提取全部關(guān)鍵參數(shù),每次對(duì)ROI圖像進(jìn)行快速掃描更新參數(shù),否則使用默認(rèn)參數(shù)。例如,可以選擇每次5間隔取點(diǎn),以像素最高點(diǎn)的85%作為該次二值化的閾值。從而做到動(dòng)態(tài)車道線識(shí)別。
完整代碼
方法一
main.cpp
#include<cv.h> #include<cxcore.h> #include<highgui.h> #include"mylinedetect.h" #include<cstdio> #include<iostream> using namespace std; int main(){ //聲明IplImage指針 IplImage* pFrame = NULL; IplImage* pCutFrame = NULL; IplImage* pCutFrImg = NULL; //聲明CvCapture指針 CvCapture* pCapture = NULL; //聲明CvMemStorage和CvSeg指針 CvMemStorage* storage = cvCreateMemStorage(); CvSeq* lines = NULL; //生成視頻的結(jié)構(gòu) VideoWriter writer("result.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25.0, Size(856, 480)); //當(dāng)前幀數(shù) int nFrmNum = 0; //裁剪的天空高度 int CutHeight = 310; //窗口命名 cvNamedWindow("video", 1); cvNamedWindow("BWmode", 1); //調(diào)整窗口初始位置 cvMoveWindow("video", 300, 0); cvMoveWindow("BWmode", 300, 520); //不能打開則退出 if (!(pCapture = cvCaptureFromFile("lane.avi"))){ fprintf(stderr, "Can not open video file\n"); return -2; } //每次讀取一楨的視頻 while (pFrame = cvQueryFrame(pCapture)){ //設(shè)置ROI裁剪圖像 cvSetImageROI(pFrame, cvRect(0, CutHeight, pFrame->width, pFrame->height - CutHeight)); nFrmNum++; //第一次要申請(qǐng)內(nèi)存p if (nFrmNum == 1){ pCutFrame = cvCreateImage(cvSize(pFrame->width, pFrame->height - CutHeight), pFrame->depth, pFrame->nChannels); cvCopy(pFrame, pCutFrame, 0); pCutFrImg = cvCreateImage(cvSize(pCutFrame->width, pCutFrame->height), IPL_DEPTH_8U, 1); //轉(zhuǎn)化成單通道圖像再處理 cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY); } else{ //獲得剪切圖 cvCopy(pFrame, pCutFrame, 0); #if 0 //反透視變換 //二維坐標(biāo)下的點(diǎn),類型為浮點(diǎn) CvPoint2D32f srcTri[4], dstTri[4]; CvMat* warp_mat = cvCreateMat(3, 3, CV_32FC1); //計(jì)算矩陣反射變換 srcTri[0].x = 10; srcTri[0].y = 20; srcTri[1].x = pCutFrame->width - 5; srcTri[1].y = 0; srcTri[2].x = 0; srcTri[2].y = pCutFrame->height - 1; srcTri[3].x = pCutFrame->width - 1; srcTri[3].y = pCutFrame->height - 1; //改變目標(biāo)圖像大小 dstTri[0].x = 0; dstTri[0].y = 0; dstTri[1].x = pCutFrImg->width - 1; dstTri[1].y = 0; dstTri[2].x = 0; dstTri[2].y = pCutFrImg->height - 1; dstTri[3].x = pCutFrImg->width - 1; dstTri[3].y = pCutFrImg->height - 1; //獲得矩陣 cvGetPerspectiveTransform(srcTri, dstTri, warp_mat); //反透視變換 cvWarpPerspective(pCutFrame, pCutFrImg, warp_mat); #endif //前景圖轉(zhuǎn)換為灰度圖 cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY); //二值化前景圖 cvThreshold(pCutFrImg, pCutFrImg, 80, 255.0, CV_THRESH_BINARY); //進(jìn)行形態(tài)學(xué)濾波,去掉噪音 cvErode(pCutFrImg, pCutFrImg, 0, 2); cvDilate(pCutFrImg, pCutFrImg, 0, 2); //canny變化 cvCanny(pCutFrImg, pCutFrImg, 50, 120); //sobel變化 //Mat pCutFrMat(pCutFrImg); //Sobel(pCutFrMat, pCutFrMat, pCutFrMat.depth(), 1, 1); //laplacian變化 //Laplacian(pCutFrMat, pCutFrMat, pCutFrMat.depth()); #if 1 //0為下面的代碼,1為上面的代碼 #pragma region Hough直線檢測(cè) lines = cvHoughLines2(pCutFrImg, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 100, 15, 15); printf("Lines number: %d\n", lines->total); //畫出直線 for (int i = 0; i<lines->total; i++){ CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i); double k = ((line[0].y - line[1].y)*1.0 / (line[0].x - line[1].x)); cout<<"nFrmNum "<<nFrmNum<<" 's k = "<<k<<endl; if(!(abs(k)<0.1))//去掉水平直線 cvLine(pFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA); } #pragma endregion #else #pragma region mylinedetect Mat edge(pCutFrImg); vector<struct line> lines = detectLine(edge, 60); Mat pFrameMat(pFrame); drawLines(pFrameMat, lines); namedWindow("mylinedetect", 1); imshow("mylinedetect", pFrameMat); #pragma endregion #endif //恢復(fù)ROI區(qū)域 cvResetImageROI(pFrame); //寫入視頻流 writer << pFrame; //顯示圖像 cvShowImage("video", pFrame); cvShowImage("BWmode", pCutFrImg); //按鍵事件,空格暫停,其他跳出循環(huán) int temp = cvWaitKey(2); if (temp == 32){ while (cvWaitKey() == -1); } else if (temp >= 0){ break; } } } //銷毀窗口 cvDestroyWindow("video"); cvDestroyWindow("BWmode"); //釋放圖像 cvReleaseImage(&pCutFrImg); cvReleaseImage(&pCutFrame); cvReleaseCapture(&pCapture); return 0; }
mylinedetect.h
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> #include <vector> #include <cmath> using namespace cv; using namespace std; const double pi = 3.1415926f; const double RADIAN = 180.0 / pi; struct line{ int theta; int r; }; vector<struct line> detectLine(Mat &img, int threshold){ vector<struct line> lines; int diagonal = floor(sqrt(img.rows*img.rows + img.cols*img.cols)); vector< vector<int> >p(360, vector<int>(diagonal)); //統(tǒng)計(jì)數(shù)量 for (int j = 0; j < img.rows; j++) { for (int i = 0; i < img.cols; i++) { if (img.at<unsigned char>(j, i) > 0){ for (int theta = 0; theta < 360; theta++){ int r = floor(i*cos(theta / RADIAN) + j*sin(theta / RADIAN)); if (r < 0) continue; p[theta][r]++; } } } } //獲得最大值 for (int theta = 0; theta < 360; theta++){ for (int r = 0; r < diagonal; r++){ int thetaLeft = max(0, theta - 1); int thetaRight = min(359, theta + 1); int rLeft = max(0, r - 1); int rRight = min(diagonal - 1, r + 1); int tmp = p[theta][r]; if (tmp > threshold && tmp > p[thetaLeft][rLeft] && tmp > p[thetaLeft][r] && tmp > p[thetaLeft][rRight] && tmp > p[theta][rLeft] && tmp > p[theta][rRight] && tmp > p[thetaRight][rLeft] && tmp > p[thetaRight][r] && tmp > p[thetaRight][rRight]){ struct line newline; newline.theta = theta; newline.r = r; lines.push_back(newline); } } } return lines; } void drawLines(Mat &img, const vector<struct line> &lines){ for (int i = 0; i < lines.size(); i++){ vector<Point> points; int theta = lines[i].theta; int r = lines[i].r; double ct = cos(theta / RADIAN); double st = sin(theta / RADIAN); //公式 r = x*ct + y*st //計(jì)算左邊 int y = int(r / st); if (y >= 0 && y < img.rows){ Point p(0, y); points.push_back(p); } //計(jì)算右邊 y = int((r - ct*(img.cols - 1)) / st); if (y >= 0 && y < img.rows){ Point p(img.cols - 1, y); points.push_back(p); } //計(jì)算上邊 int x = int(r / ct); if (x >= 0 && x < img.cols){ Point p(x, 0); points.push_back(p); } //計(jì)算下邊 x = int((r - st*(img.rows - 1)) / ct); if (x >= 0 && x < img.cols){ Point p(x, img.rows - 1); points.push_back(p); } //畫線 cv::line(img, points[0], points[1], Scalar(255, 0, 0), 5, CV_AA); } }
方法二:
#include<cv.h> #include<cxcore.h> #include<highgui.h> #include<cstdio> #include<iostream> using namespace std; int main(){ //聲明IplImage指針 IplImage* pFrame = NULL; IplImage* pCutFrame = NULL; IplImage* pCutFrImg = NULL; IplImage* pCutBkImg = NULL; //聲明CvMat指針 CvMat* pCutFrameMat = NULL; CvMat* pCutFrMat = NULL; CvMat* pCutBkMat = NULL; //聲明CvCapture指針 CvCapture* pCapture = NULL; //聲明CvMemStorage和CvSeg指針 CvMemStorage* storage = cvCreateMemStorage(); CvSeq* lines = NULL; //當(dāng)前幀數(shù) int nFrmNum = 0; //裁剪的天空高度 int CutHeight = 250; //窗口命名 cvNamedWindow("video", 1); //cvNamedWindow("background", 1); cvNamedWindow("foreground", 1); //調(diào)整窗口初始位置 cvMoveWindow("video", 300, 30); cvMoveWindow("background", 100, 100); cvMoveWindow("foreground", 300, 370); //不能打開則退出 if (!(pCapture = cvCaptureFromFile("lane.avi"))){ fprintf(stderr, "Can not open video file\n"); return -2; } //每次讀取一楨的視頻 while (pFrame = cvQueryFrame(pCapture)){ //設(shè)置ROI裁剪圖像 cvSetImageROI(pFrame, cvRect(0, CutHeight, pFrame->width, pFrame->height - CutHeight)); nFrmNum++; //第一次要申請(qǐng)內(nèi)存p 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); //轉(zhuǎn)化成單通道圖像再處理 cvCvtColor(pCutFrame, pCutBkImg, CV_BGR2GRAY); cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY); //轉(zhuǎn)換成矩陣 cvConvert(pCutFrImg, pCutFrameMat); cvConvert(pCutFrImg, pCutFrMat); cvConvert(pCutFrImg, pCutBkMat); } else{ //獲得剪切圖 cvCopy(pFrame, pCutFrame, 0); //前景圖轉(zhuǎn)換為灰度圖 cvCvtColor(pCutFrame, pCutFrImg, CV_BGR2GRAY); cvConvert(pCutFrImg, pCutFrameMat); //高斯濾波先,以平滑圖像 cvSmooth(pCutFrameMat, pCutFrameMat, CV_GAUSSIAN, 3, 0, 0.0); //當(dāng)前幀跟背景圖相減 cvAbsDiff(pCutFrameMat, pCutBkMat, pCutFrMat); //二值化前景圖 cvThreshold(pCutFrMat, pCutFrImg, 35, 255.0, CV_THRESH_BINARY); //進(jìn)行形態(tài)學(xué)濾波,去掉噪音 cvErode(pCutFrImg, pCutFrImg, 0, 1); cvDilate(pCutFrImg, pCutFrImg, 0, 1); //更新背景 cvRunningAvg(pCutFrameMat, pCutBkMat, 0.003, 0); //pCutBkMat = cvCloneMat(pCutFrameMat); //將背景轉(zhuǎn)化為圖像格式,用以顯示 //cvConvert(pCutBkMat, pCutBkImg); cvCvtColor(pCutFrame, pCutBkImg, CV_BGR2GRAY); //canny變化 cvCanny(pCutFrImg, pCutFrImg, 50, 100); #pragma region Hough檢測(cè) lines = cvHoughLines2(pCutFrImg, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 100, 30, 15); printf("Lines number: %d\n", lines->total); //畫出直線 for (int i = 0; i<lines->total; i++){ CvPoint* line = (CvPoint* )cvGetSeqElem(lines, i); cvLine(pCutFrame, line[0], line[1], CV_RGB(255, 0, 0), 6, CV_AA); } #pragma endregion //顯示圖像 cvShowImage("video", pCutFrame); cvShowImage("background", pCutBkImg); cvShowImage("foreground", pCutFrImg); //按鍵事件,空格暫停,其他跳出循環(huán) int temp = cvWaitKey(2); if (temp == 32){ while (cvWaitKey() == -1); } else if (temp >= 0){ break; } } //恢復(fù)ROI區(qū)域(多余可去掉) cvResetImageROI(pFrame); } //銷毀窗口 cvDestroyWindow("video"); cvDestroyWindow("background"); cvDestroyWindow("foreground"); //釋放圖像和矩陣 cvReleaseImage(&pCutFrImg); cvReleaseImage(&pCutBkImg); cvReleaseImage(&pCutFrame); cvReleaseMat(&pCutFrameMat); cvReleaseMat(&pCutFrMat); cvReleaseMat(&pCutBkMat); cvReleaseCapture(&pCapture); return 0; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
您可能感興趣的文章
- 01-10深入線性時(shí)間復(fù)雜度求數(shù)組中第K大數(shù)的方法詳解
- 01-10Linux線程管理必備:解析互斥量與條件變量的詳解
- 01-10深入分析父子線程、進(jìn)程終止順序不同產(chǎn)生的結(jié)果
- 01-10深入探討linux下進(jìn)程的最大線程數(shù)、進(jìn)程最大數(shù)、進(jìn)程打開的文
- 01-10解析C/C++中如何終止線程的運(yùn)行
- 01-10C/C++中退出線程的四種解決方法
- 01-10C/C++ 多線程的學(xué)習(xí)心得總結(jié)
- 01-10利用ace的ACE_Task等類實(shí)現(xiàn)線程池的方法詳解
- 01-10shared_ptr線程安全性全面分析
- 01-10線程池的原理與實(shí)現(xiàn)詳解


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對(duì)數(shù)函數(shù)的表達(dá)式 c語言中對(duì)
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 04-02jquery與jsp,用jquery
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10delphi制作wav文件的方法