车道线检测-方法1

在汽车自动驾驶过程中,你需要汽车能够对周围的世界进行很好的感知。我们人类通过使用眼睛来了解汽车跑的有多快,汽车的车道线在哪,明白在哪里可以进行转弯,汽车通过摄像头,毫米波雷达,mio等其它的传感器来帮助我们实现人眼的功能。
为了让车道线检测的结果能够上车,我并没有上神经网络,而是采用特征检测方法进行测量。在阅读相关论文的方法后,我按照以下的步骤进行实验:

  • 视频图片预处理。设置图片的ROI区域,对图片进行灰度转换。
  • 图片边缘处理。切割ROI为若干个子图,二值化后,计算子图的连通域。
  • 霍夫变换,计算图片灭点。
  • 直线特征筛选,ransac 直线拟合。
  • 绘制车道线。
视频图片预处理
frame.jpg

我们知道,车道线的特征很固定,它一定在地上,而不会跑到天上去。因此,在检测的第一步,我就把图片进行了裁剪,减少了天空,树等干扰,也减少了处理图片的时间。经裁剪后的ROI ,就要转换为灰度图。其实,看了许多论文后,发现在这个步骤前,还有的对图片进行色彩空间的转换,增强黄色和白色的特征(车道线为白色或者黄色)。我在灰度转换的时候,也没有直接使用opencv的转灰度图的api, 而是提取B,G,R 三个通道值,计算I = R+G-B。读者可以实践两种转灰度的方法,会发现后者得到的灰度图更加清晰。

cv::Mat img2gray(cv::Mat img){
    int height= img.rows;
    int width = img.cols;
    cv::Mat gray  (height, width, CV_8U,cv::Scalar(0,0,0));
    for (int i=0;i< height;i++) {
        for (int j = 0; j < width; j++) {
            int B = img.at<cv::Vec3b>(i, j)[0];
            int G = img.at<cv::Vec3b>(i, j)[1];
            int R = img.at<cv::Vec3b>(i, j)[2];
            int I = R + G -B;

            if (I > 255)
                I = 255;
            gray.at<uint8_t >(i, j) = I;
        }
    }
    return  gray;
}
图片的边缘处理

一提到车道线的边缘处理,一定会条件反射想到canny 边缘检测。如果只是单纯进行canny检测,那么检测结果不会太好。canny很依赖原始图片的质量,如果原来的图片车道线很分明,天气状况良好,那么canny的效果会好,但是如果图片质量不太好,那么干扰的点就会很多,这会直接影响霍夫变换的结果。因此我在canny之前对图像做了一个分割处理,对每个子图二值化,canny检测后,根据车道线的特征做连通域计算,最后将子图还原原图。


边缘化.png

可以看出,经过这样的处理后,车道线就很清晰的呈现出来了。

霍夫变换,灭点计算

在得到上述的二值化图后,紧接着就是上opencv概率霍夫变换,检测直线。调用opencv很简单,但是注意一些参数的设定就好。

直线特征筛选,ransac 直线拟合。

并不是所有霍夫变换得到的直线都是我们的候选线 ,我们有一些筛选条件。最直接的条件是,不经过灭点的直线,一定不是车道线。所以在,霍夫变换之后,我们需要计算图片的灭点。车道线有方向角的信息,也可以通过方向角进一步筛选。由于我在图像边缘计算的时候,已经在连通域计算时候考虑车道方向角,所以这里就不再计算了。

对于候选的直线,我采用ransac算法进行拟合。如果对ransac不是很了解的,可以先看看算法的描述,我这里直接上代码了。

void ransac (Mat X ,Mat Y,std::vector<Vec2d > &result ){
    int Size = X.rows;
    int iters = 400;
    double sigma = 0.25;
    double best_k = 0.0;
    double best_b = 0.0;
    double k,b;

    int pretotal = 2;
    double P = 0.90;
    int pos1,pos2;
    int total_inlier;
    default_random_engine e;
    uniform_int_distribution<unsigned> u(0, Size);
    int i;
    for (i=0;i<iters;i++){
            pos1= u(e);
            pos2 =u(e);
            int * x_1 = X.ptr<int>(pos1);
            int * x_2 = X.ptr<int>(pos2);
            int * y_1 = Y.ptr<int>(pos1);
            int * y_2 = Y.ptr<int>(pos2);

            k = (*(y_2) - *(y_1)) / (*(x_2) - *(x_1) + 0.0000001);
            b = *(y_1) - k * *(x_1);

            total_inlier= 0;
            for (int j=0;j<Size;j++){
                double  y_estimate = k * *(X.ptr<int>(j)) +b;
                if (fabs(y_estimate-*(Y.ptr<int>(j)))<sigma){
                     total_inlier+= 1;
                }
            }

            if (total_inlier>pretotal){
                iters = log(1-P)/log( 1-pow(total_inlier/(Size+0.0000001) ,2.0 ));
                pretotal = total_inlier;
                best_k = k;
                best_b = b;
            }

            if (total_inlier > (Size/2)){
                    break;
            }
    }

    result.push_back(Vec2d(best_k,best_b));
}

绘制直线

最重要的检测部分已经完成了,那么绘制车道线就很轻松了,通过简单的几行代码,就可以把我们的直线绘制出来了。

void draw_lane(std::vector<cv::Vec4i>coordinate)
{
      std::cout << coordinate.size()<< std::endl;
      for (auto it = coordinate.begin();it!= coordinate.end();it ++){
         line(img, cv::Point((*it)[0], (*it)[1]), cv::Point((*it)[2], (*it)[3]),       cv::Scalar(0,255,0), 3);
    }
     imshow("lane ",img);
       cv::waitKey(1)
}

下图就是最后的检测结果啦~。其中,红色圆点表示灭点位置??煽闯?,车道线已经比较好的拟合出来,但是左右两边还是有杂线,这就需要进一步筛选过滤了。

lane.png
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容