关键词:车道线识别 椭圆检测 霍夫变换
Study on Lane Mark Identification Algorithm Based on
Image Processing
In order to improve the real-time and accuracy of lane recognition algorithm, presents a method of lane detection based on improved Hough transform. The preprocessing stage in the lane line, first determine the image of the region of interest, and then smoothing, adaptive threshold segmentation, edge detection of image, thereby reducing the amount of data processing stage, most of the interference factors. The recognition phase in the lane line, according to the nature of the circle and ellipse, improvement of the Hof transform to the traditional, greatly reduces the amount of computation in the detection of circle and ellipse, and improve the accuracy of detection. Finally, the use of OpenCV image processing library program, proved the correctness of the new algorithm, and the desired result.
Key Words:lane mark identification;ellipse detection;hough transform
摘要. I
Abstract II
目录. III
第1章 绪论. 1
1.1 研究背景... 1
2.1 国内外研究现状... 2
1.2.1 国外研究现状. 2
1.2.2 国内研究现状. 3
3.1 论文主要内容... 3
第2章 车道线的预处理. 5
2.1 认识车道线... 5
2.1.1 车道线的基本分类. 5
2.1.2 车道线的标划区分. 6
2.1.3 车道线的基本形状. 6
2.2 感兴趣区域... 6
2.3 平滑滤波... 7
2.3.1 均值滤波. 7
2.3.2 中值滤波. 8
2.3.3 高斯滤波. 9
2.3.4 平滑滤波总结. 10
2.4 二值化... 10
2.4.1 二值化的基本概念. 10
2.4.2 大津法自适应阈值分割. 10
2.5 边缘提取... 11
2.5.1 Sobel算子. 12
2.5.2 Canny算子. 13
2.5.3 Sobel算子和Canny算子的比较. 14
第3章 Hough变换直线和椭圆的检测. 15
3.1 Hough变换直线检测... 15
3.1.1 Hough变换直线检测的基本原理. 15
3.1.2 Hough变换的缺陷和改进. 16
3.2 Hough变换圆检测... 17
3.2.1 Hough变换圆检测的基本原理. 17
3.2.2 对hough变换圆检测算法的改进. 18
3.2.3 最小二乘法求曲线上一点的切线. 18
3.3 Hough变换椭圆检测... 19
第4章 车道线的识别. 22
结论. 25
参考文献:. 26
致谢. 28
附录1 29
车道线的识别是图像处理和交通智能化领域的一个重要分支,特别是近年来,随着只能汽车、自动识别的发展和应用,车道线识别的研究得到了长足的发展,并且会在未来随着人们对识别精度、速度的要求越来越高,车道线识别领域的研究将会愈发火热,可以说这是一个方兴未艾的研究领域。
车道线识别的研究成果很大程度上得益与数字图象处理和计算机技术的发展。数字图像处理最早出现于20世纪50年代,当时的电子计算机已经发展到一定水平,人们开始利用计算机来处理图形和图像信息。数字图像处理作为一门学科大约形成于20世纪60年代初期。早期的图像处理的目的是改善图像的质量,它以人为对象,以改善人的视觉效果为目的。图像处理中,输入的是质量低的图像,输出的是改善质量后的图像,常用的图像处理方法有图像增强、复原、编码、压缩等。首次获得实际成功应用的是美国喷气推进实验室(JPL)。他们对航天探测器徘徊者7号在1964年发回的几千张月球照片使用了图像处理技术,如几何校正、灰度变换、去除噪声等方法进行处理,并考虑了太阳位置和月球环境的影响,由计算机成功地绘制出月球表面地图,获得了巨大的成功。随后又对探测飞船发回的近十万张照片进行更为复杂的图像处理,以致获得了月球的地形图、彩色图及全景镶嵌图,获得了非凡的成果,为人类登月创举奠定了坚实的基础,也推动了数字图像处理这门学科的诞生。在以后的宇航空间技术,如对火星、土星等星球的探测研究中,数字图像处理技术都发挥了巨大的作用。随着图像处理技术的深入发展,从70年代中期开始,随着计算机技术和人工智能、思维科学研究的迅速发展,数字图像处理向更高、更深层次发展。人们已开始研究如何用计算机系统解释图像,实现类似人类视觉系统理解外部世界,这被称为图像理解或计算机视觉。很多国家,特别是发达国家投入更多的人力、物力到这项研究,取得了不少重要的研究成果。其中代表性的成果是70年代末MIT的Marr提出的视觉计算理论,这个理论成为计算机视觉领域其后十多年的主导思想。图像理解虽然在理论方法研究上已取得不小的进展,但它本身是一个比较难的研究领域,存在不少困难,因人类本身对自己的视觉过程还了解甚少,因此计算机视觉是一个有待人们进一步探索的新领域。
在过去几十年中,国内外许多专家学者在车道线识别和跟踪系统方面已经做出了很多积极有意义的探索。国外一些关于智能车辆辅助驾驶系统的研究成果已经比较成熟,有些已经投入到商业化应用中,相对国外的研究水平,国内的研究起步比较晚,但是发展速度比较快,相信在未来的几年里,国内的一些研究成果应该能够快速跟上国际版步伐,逐渐发展完善起自己的应用体系,尽快的投入到实际生产应用中。
国外一些关于车道线识别的研究成果已经比较成熟,目前,有些方法已经投入到了实际的商业化应用中。其中,具有代表性的系统主要由:意大利帕尔玛大学开发的GOLD系统[20];美国密歇根州立大学人工智能实验室开发的LOIS系统;美国卡内基梅隆大学开发的RALPH系统;美国卡内基梅隆大学机器人学院NavLab实验室和Vision&Autonomous System Center联合开发的SCARF系统和ALVINN系统。
GOLD系统:该系统采用立体视觉技术,根据目前车道线的油漆颜色特征来定位车道线所处位置,但是立体视觉技术面临图像匹配以及运行时间长等问题,因而该系统有设计了并行SIMD硬件结构来满足实时性要求。
LOIS系统:该系统利用一种可变型的道路模板技术,将道路的弯曲度和智能车辆在行驶过程中所处道路的位置问题转换成多维参数空间的最优化问题,通过得到的最优解来解决车道偏离预警中出现的问题。
RALPH系统:该系统首先根据车辆速度的变化情况,建立了与之相对应的一系列梯形窗口,通过对梯形窗口内的视频图像通过逆透视变换来确定道路的弯曲度,然后计算智能车辆偏离车道中心线的距离,判断车道线偏离情况,最后通过跟踪逆透视变换得到的道路平行线,从而实现道路车道线的追踪。该系统在实验开始的时候进行了道路结构化假设,在很大程度上提高了车道线检测识别的鲁棒性。
SCARF系统:该系统利用双目视觉原理,在图像中设置的梯形窗口内,利用霍夫变换来估计车道线可能出现的ROI区域,然后反投影到真实的道路平面,通过控制车辆的方向沿着ROI中心线方向行驶来确保车辆不会偏离车道线。
ALVINN系统:采用基于BP神经网络的方法,通过对不同天气状况下的车道线特征进行训练,来得到一个参数训练模型,从而根据训练得到的参数模型来预测不同天气状况下的车道线位置。
今年来,我国在该领域也做了一些积极的探索和研究,但是与其他发达国家相比起步比较晚,因此,国内在该领域的研究和探索还存在一定的改进空间。国内的研究成果主要有以下几个:
清华大学计算机智能技术与系统国家重点实验室研制的THMR系统:该项目采用的道路模型是直线模型,利用多窗口的双阈值二值化进行特征提取,在后续处理中采用增强转移网络来完成,在算法实时性方面做得比较好,但由于道路模型首先,因此只能对直线车道进行检测和识别。
吉林工业大学研制的JUTIV系统:该项目采用3D回旋曲线为道路模型,用最大类方差方法来设定阈值提取道路边缘,利用随即采用的LmedSquare方法进行车道线曲线拟合,同时结合了驾驶员稳态预瞄原理,建立了车道线拟合的预测区域,并进一步利用多传感器信息融合技术对复杂环境下的车道线检测识别与跟踪等关键技术做了系统研究。
一种车道线识别方法,包括以下步骤:从在绘有不同车道标志图案 的车道上行驶的汽车中,通过图像传 感器拍摄汽车前方视野; 从上 述步骤中拍摄的图像中选取汽车左、右车道边界的数据,通过将对所述选取的车道边界数据进行处理,完成车道线的识别,确定车辆所在区域的车道线类型。
论文中主要分以下部分对车道线识别算法进行分析和描述:
第2章:介绍了车道线的基本知识,分析讨论了车道线预处理的过程和用到的相关算法;
第3章:具体分析霍夫变换算法,并根据圆和椭圆的性质对霍夫变换算法进行改进
第4章:在对所使用的算法和车道线特征进行分析综合的基础上,使用C语言结合OpenCV图像处理库编写程序实现算法,并对算法进行验证,得到最终的车道线识别结果。
预处理是车道线识别过程中一个十分重要的一环。良好的预处理能使排除各种干扰,使运算量大大下降,大大提高车道线识别的实时性和准确性,反之,则不仅会使识别效率降低,甚至导致得到错误的结果。
预处理的过程主要分为车道线的设置感兴趣区域、平滑滤波、阈值分割、边缘检测。本章首先简要介绍车道线的基本知识,然后对感兴趣区域的原理和应用进行分析,并针对车道线的具体特征和要得到的目标选择合适的预处理方法,进而对每种预处理过程中的算法进行分析讨论,得出适合的预处理算法。
车道分界线是用来分隔同方向行驶的交通流的交通标志线,凡同方向车行道有两条或两条以上车道时,均应划车道分界线。车道分界线有两种,即车道分界虚线和导向车道分界实线。车道分界虚线,在保证安全的原则下,准许车辆越线超车或变更车道行驶;导向车道分界实线,不准车辆越线或变更车道。
图(2-1)所示为各种车道线:
|
白色虚线:划于路段中时,用以分隔同向行驶的交通流或作为行车安全距离识别线;划于路口时,用以引导车辆行进;
白色实线:划于路段中时,用以分隔同向行驶的机动车和非机动车或指示车行道线;划于路口时,用作导向车道线或停车线;
黄色虚线:划于路段中时,用以分隔对向行驶的交通流,划于路侧或缘石上时,用以禁止车辆长时在路边停放。
黄色实线:划于路段中时,用以分隔对向行驶的交通流;划于路侧或缘石上时,用以禁止车辆长时或临时在路边停放。
双白虚线:划于路口时作为减速让行线;划于路段中时,作为行车方向随时间改变之可变车道线;
双黄实线:划于路段中时,用以分隔对向行驶的交通流;
黄色虚实线:划于路段中时,用以分隔对向行驶的交通流。黄色实线一侧禁止车辆超车、跨越或回转,黄色虚线一侧在保证安全的情况下准许车辆超车、跨越或回转;
双白实线:划于路口时,作为停车让行线。
通过对各种车道线的比较,我们可以法线,车道线基本分为两种形状:直线和圆,其他的形状还包括三角形等,但它们的基本组成元素仍然是直线,所以我们同样能对其运用直线的方式处理。但是必须注意的是,由于图像采集设备与道路之间有一定的倾角,所以道路中的圆出现在图像中时将会变为椭圆。
通过将基本的识别目标固定在直线和椭圆的范围中,我们便将车道识别的问题抽象为直线和椭圆的识别,研究方向进一步固定。
感兴趣区域简称为ROI(region of interest)是将图像中的重要区域进行标记,随后的各种算法和分析将主要针对ROI。这样做的好处是不仅使处理的数据量变少,而且也排除了非重点区域的潜在干扰。
观察采集到的图像,如图所示,我们可以发现,图像中的上部是天空和较远处的信息,这一部分信息是不需要的,而且可能会对处理结果造成干扰。所以,在图像的处理中我们应当适当选取ROI,对于以后的处理有莫大裨益。
如图(2-2)所示,其中下部较暗的区域即为我们的感兴趣区域:
|
各类图像处理系统在图像的采集、获取、传送和转换(如成像、复制扫描、传输以及显示等)过程中,均处在复杂的环境中,光照、电磁多变,所有的图像均不同程度地被可见或不可见的噪声干扰。噪声源包括电子噪声、光子噪声、斑点噪声和量化噪声。如果信噪比低于一定的水平,噪声逐渐变成可见的颗粒形状,导致图像质量的下降。除了视觉上质量下降,噪声同样可能掩盖重要的图像细节,因此,在对采集到的原始图像做进一步的处理时,需要对图像进行必要的滤波降噪处理。
均值滤波是典型的线性滤波算法,它是指在图像上对目标像素给一个模板,该模板包括了其周围的临近像素,模板大小一般为3×3、5×5、或7×7 ,用其临近区域像素值的均值来替代目标像素。
均值滤波的原理如图(2-3)所示,其中a)图中1-8为像素点的临近区域,b)图为的权系数矩阵:
|
图2-3 均值滤波原理 |
b) |
均值滤波方法如公式(2-1)所示:
(2-1)
均值滤波具有算法简单,计算速度快的优点,但它的缺点也很明显:降低噪声
的同时使图像产生模糊,特别是景物的边缘和细节部分。
中值滤波是一种非线性数字滤波器技术,经常用于去除图像或者其它信号中的噪声。
中值滤波的思想是将一个像素替换为其临近区域的所有像素的中值,临近区域一般选以此像素为中心的奇数大小的窗口,常用的为3×3、5×5、7×7等。
中值滤波的计算方法如公式(2-2)所示:
(2-2)
中值滤波法对消除椒盐噪声非常有效,在光学测量条纹图象的相位分析处理方法中有特殊作用,但在条纹中心分析方法中作用不大.
中值滤波在图像处理中,常用于保护边缘信息,是经典的平滑噪声的方法。
图(2-4)为中值滤波效果图:
|
高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的思想是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
高斯滤波的效果图如图2-5所示:
|
通过比较以上三种滤波方法,可以看出三者的区别和适用范围。均值滤波算法简单,但会造成图像的模糊,中值滤波对椒盐噪声有非常好的去除效果,并且能够保存边缘信息,高斯滤波则对高斯噪声具有很好的处理效果。
图像边缘的信息对于本论文中的车道线识别算法有重要作用,所以在选择滤波算法上应优先选用中值滤波和高斯滤波。
图像的二值化就是将一副灰度图像转换为二值图像,例如设定一个阈值K,像素值小于K的置为0,反之则置为255,如公式(2-3)所示:
(2-3)
虽然通过给定一个阈值能实现图像的二值化,但在很多时候并不能给出一个合适的阈值,为了解决这个问题,人们提出了许多自适应的阈值分割方法,以适应不同灰度分布的图像,其中应用广泛的有大津法自适应阈值等。
大津法也叫最大类间方差法,是一种自适应的阈值确定的方法,简称OTSU。它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。对于图像,前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为,其平均灰度;背景像素点数占整幅图像的比例为,其平均灰度为。图像的总平均灰度记为,类间方差记为。假设图像的背景较暗,并且图像的大小为,图像中像素的灰度值小于阈值T的像素个数记作,像素灰度大于阈值T的像素个数记作,则有
(2-4)
(2-5)
(2-6)
(2-7)
(2-8)
(2-9)
经计算后,得到等价公式(2-10):
(2-10)
采用遍历的方法得到使类间方差最大的阈值T,即为所求。
大津法阈值分割如图2-6所示:
|
图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个阶跃,既从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。图象的边缘部分集中了图象的大部分信息,图象边缘的确定与提取对于整个图象场景的识别与理解是非常重要的,同时也是图象分割所依赖的重要特征,边缘检测主要是图象的灰度变化的度量、检测和定位。
Sobel算子主要用作边缘检测。在技术上,它是一离散性差分算子,用来运算图像亮度函数的梯度之近似值。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。
Sobel算子包含两个的矩阵,如图(2-7)所示,用这两个矩阵与图像进行卷积,便可分别得到横向及纵向的亮度差分近似值。
如果以A代表原始图像,Gx及Gy分别代表经横向及纵向边缘检测的图像,其计算方法如公式(2-11)和(2-12):
(2-11)
(2-12)
图像的每一个像素的横向及纵向梯度值可用公式(2-13)来计算梯度的大小。
(2-13)
通常,也使用一个近似计算的公式(2-14)来提高计算速度:
(2-14)
可用以下公式计算梯度方向:
(2-15)
如果角度Θ等于零,即代表图像该处拥有纵向边缘,左方较右方暗。
Sobel算子边缘提取效果如图(2-8)所示,a)图为源图像,b)图为X方向边缘检测结果,c)图为Y方向边缘检测结果
图2-8 Sobel算子边缘检测效果图 |
a) |
b) |
c) |
Canny边缘检测算子是一个多级边缘检测算法,它的目标为实现最优的边缘检测算法。
Canny算子的处理过程分为几个步骤,首先要对输入图像进行高斯平滑处理,然后使用一个二维一阶导数算子作用与平滑处理后的图像,用于突出图像中高一空间导数的图像区域,这样我们就从原始图像生成了图像中每个点亮度梯度图以及亮度梯度的方向。随后Canny跟踪这些亮度梯度并且保留局部最大值而把其他值设为0,即非极大值抑制,最后使用双阈值算法检测和连接边缘。
对图(2-8)中图像进行Canny边缘检测效果如图(2-9)所示:
|
Sobel算子通过对图像进行卷积运算,算法简单且计算速度相对较快,缺点是Sobel算子并没有将图像的主题与背景严格地区分开来,换言之就是Sobel算子并没有基于图像灰度进行处理,由于Sobel算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。
算子是一个具有滤波增强检测的多阶段的优化算子,在进行处理前,
Canny算子先利用高斯平滑滤波器来平滑图像以除去噪声,Canny分割算法采用一阶偏导的有限差分来计算梯度幅值和方向,在处理过程中,Canny算子还将经过一个非极大值抑制的过程,最后Canny算子还采用两个阈值来连接边缘。可以看出Canny算子的处理过程比较复杂,但它的处理结果是比较好的。
Hough变换的核心思想是将图像空间转化到参数空间进行分析。
在平面直角坐标系中,一条直线可以用方程来表示,其中和是参数,分别是斜率和截距。过某一点的所有直线的参数都会满足方程,即点确定了一族直线。方程在参数平面上是一条直线。这样,图像平面上的一个前景像素点就对应到参数平面上的一条直线,同理,直线上的其他点也对应一条参数空间的直线,最终,在参数空间中的这些直线将会交于一点,很显然,这个点就是。
但是方程无法表示这样的直线(斜率无穷大),所以在实际应用中使用直线的法线式方程来表示直线:
此时,图像空间中的一点将对应参数空间的一条正弦曲线,这样图像空间中的一条直线就对应参数空间中的一点。如图(3-1)所示:
|
图(3-2)为霍夫变换直线检测的效果(已设置感兴趣区域):
|
Hough变换具有显而易见的优点,但它也有一些不可忽视的缺点:
1、运算量大。传统的Hough变换需要对每个点进行计算,不仅运算量大,而且会产生大量冗余数据,并且运用Hough变换进行圆或其他图像的检测时,由于参数的增多(如圆需要3个参数),计算量将会急剧增加,所以,Hough变换的实时性不高;
2、虽然Hough变换能得到直线的参数方程,但它无法确定直线的起止点,即无法确定直线是否是连续的;
3、图像中的噪声点会对Hough变换的结果产生较大的影响。
针对以上缺陷,需要对Hough变换进行一定的改进,以更好地完成期望的工作:
1、对图像进行合适的预处理,尽力排除噪声点的干扰;
2、对图像进行边缘提取、区域分割等,提取出有用的信息,再根据统计学的一些原理来应用Hough变换,减少Hough变换的运算量
3、对于Hough变换以上缺陷中的第二点,可以设计特殊的数据结构记录Hough变换过程中的数据,从而解决探测直线等起止点的问题。
Hough变换直线检测中是将图像空间中的一条直线映射为参数空间中的一个点,对圆的检测同样如此,这也是Hough变换的核心思想所在。但是直线检测需要两个参数,而圆检测需要三个参数(为圆心坐标,r为圆的半径)。
在平面直角坐标系中,圆用方程来表示,将圆上的一点映射到参数空间,对应的方程为由于圆的半径r不确定,所以参数空间是一个三维空间,每一点在参数空间映射为一个圆锥,如图(3-3)所示:
|
因此,Hough变换圆检测的计算量会是相当大的,这是传统Hough变换最大的缺陷之一。
由于传统的Hough变换圆检测算法计算量太大,所以需要对其进行改进以增强其实时性。
对于圆,有如下性质:圆上一点的法线必过圆心。根据这一性质,随机选取圆上若干各点,求得其法线,法线的交点便是圆心,进而也可以很容易地计算出圆的半径。
那么如何求一个点的法线呢?对于图像中的一个点,其法线方向便是其梯度变换最大的方向,且法线与切线呈垂直关系,那么我们可以先求一点的切线,进而得到这一点的法线。
最小二乘法(又称最小平方法)是一种数学优化技术。它通过最小化误差的平方和寻找数据的最佳匹配函数。利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小。最小二乘法还可用于曲线拟合等初中应用领域。
假设图像中存在处于一条直线附近的几个点,那么设这条直线的方程为:
为直线斜率,为截距。
为求得和,根据最小二乘法原理,将实测值与的离差的平方和最小作为最优判据。
令 (3-1)
当最小时,分别对和求偏导,令这两个偏导数等于0:
(3-2)
(3-3)
两个偏导数等于0,即得:
(3-4)
(3-5)
将和的结果代入原直线方程,即得拟合所得的直线方程为:
n |
X |
k |
Y |
X |
X |
X |
n |
Y |
X |
Y |
X |
n |
Y |
i |
i |
i |
i |
i |
i |
i |
i |
å |
å |
å |
å |
å |
å |
- |
+ |
- |
- |
= |
2 |
2 |
) |
( |
同样,在图像中圆上某一点的切线方程也可用同样的方法求得。
虽然最小二乘法能比较方便的得出圆上一点的切线,但要知道其正确性与点的样本大小有很大关系,如果样本太小,则计算误差变大,很可能得不到正确的结果,若样本太大,则计算量也将随之增大,所以样本大小的选取是一个不可忽视的问题。
图(3-4)展示了根据最小二乘法原理得到的曲线上一点的切线:
|
根据前面的分析,道路车道线经过图像采集设备采集后,其原本的圆将会变化为椭圆,所以我们更应该关注椭圆的检测。
根据Hough变换圆检测的算法,进一步讨论椭圆的检测,由于椭圆上一点的法线并不过椭圆的中心,所以上面的圆检测算法不能直接用于椭圆的检测,需要进一步的改进。根据椭圆的性质:椭圆上两点法线的交点与这两点中点所确定的直线过椭圆中心。我们自然而然便能对圆检测算法加以改进以适应椭圆的检测。
椭圆检测的过程描述为:
第一步:在目标图形上选取随机的三个点分别记为、和,并求得他们各自的切线,分别记为、、和他们两两间的中点,分别记为、和(即为和的中点);
第二步:找到这三条切线两两相交的交点,分别记为、和(即为和的交点,以此类推);
第三步:将对应的交点与中点连接,即与、与、与,将得到的直线分别记为、和;
第四步:求得这三条连线两两相交的交点,根据一定的距离准则,判断这三点是否足够接近,如果足够接近,说明此时的形状可能是一个椭圆,否则则说明不是;
第五步:在目标图形上另选一点,求其切线,并将其与其他三点中的一点重新配对,得到两切线交点与中点的直线方程;
第六步:根据同样的距离准则,判断新得到的直线与其他直线的交点是否足够接近,如果是,那么目标图形是一个椭圆,如果不是,则说明目标图形不是椭圆。
图(3-5)为根据此方法对椭圆进行检测的结果:
|
由图可以看出,该方法能正确的检测出图像中的椭圆,说明了此方法的正确性和可靠性。
综合分析前面章节中的处理过程和算法,总结出车道线识别的流程,如图(4-1)所示:
|
在Linux平台上,使用C语言和Gtk+结合OpenCV图像处理库编写程序,进行车道线的识别,如图(4-2)所示:
|
车道线的识别结果如图(4-3)所示:
|
从识别的结果可以看出,算法能对车道线进行准确的识别,达到了预期的目的和理想的效果。
从以上的算法分析和实验结果,可以看出,本论文中的算法能对车道线较好较准确的进行识别,算法的主要优点有:
1、算法的准确性高。相比传统的识别算法,本论文中的算法经过优化处理,提高了识别的准确性;
2、算法的实时性好,特别是经过对Hough变换等算法的一定的改进,大大减少了计算量,提高了计算速度;
3、算法能检测多种车道线,适应范围广;
4、算法结构清晰,鲁棒性好,方便扩展和改进;
当然,在实际的应用中也发现了一些问题,主要有:
1、对于复杂路况和天气条件欠佳时,算法的适应性还不够高;
2、对于一些现实中存在的干扰因素如马路沿等,不能进行有效的排除。
这些存在的问题,也指出了下一阶段改进的方向和思路,以后的时间里还有更多的工作要做。
对于未来,随着人工智能、机器学习等的不断发展,将这些技术应用于车道识别中,必将能极大的提高车道线识别的精确性和适应性,并且伴随着智能汽车的进一步发展,新的技术不断得到应用,车道线的识别技术一定会迎来一个大的发展。特别是国内,随着中国的科技水平不断提高,市场需要持续增大,对于车道线的自动识别的需求也将更加迫切。所以未来是光明的,但更需要我们脚踏实地,不断取得新的突破。
[1]鲁曼,蔡自兴,李仪.道路区域分割的车道线检测方法[J].智能系统学报,2010,5(6):505-509
[2]金辉,吴乐林,陈慧岩,龚建伟 .结构化道路车道线识别的一种改进算法[J].北京理工大学学报,2007,27(6):502-505
[3]朱桂英,张瑞林 .基于Hough变换的圆检测方法[J].计算机工程与设计,2008,29(6)
[4]郭磊,王建强,李克强.基于点集优化和干扰点模糊化的车道线识别[J].中国机械工程,18(15):1872-1876
[5]杨喜宁,段建民,高德芝,郑榜贵.基于改进Hough变换的车道线检测技术[J].计算机测量与控制,2010,18(2):292-298
[6]莫建文,范楷,张顺岚.基于扩散性搜索区域的车道线检测与跟踪算法[J].桂林电子科技大学学报,2011,31(6):464-468
[7]王晓云,王永忠.基于线性双曲线模型的车道线检测算法[J].杭州电子科技大学学报,2010,30(6):64-67
[8]徐岩,雷涛.基于形态学方法的车道线检测算法研究[J].铁道学报,2009,31(1):107-110
[9]秦开怀,王海颍,郑辑涛.一种基于Hough变换的圆和矩形的快速检测方法[J].中国图像图形学报,2010,15(1):110-115
[10]段汝娇,赵伟,黄松岭,称建业.一种基于改进Hough变换的直线快速检测算法[J].仪器仪表学报,2010,31(12):2774-2780
[11]李若皓,丁冬花.一种基于扫描线的车道线识别算法[J].微计算机信息,2008,24(6):244-246
[12]刘富强,张姗姗,朱文红,李志鹏.一种基于视觉的车道线检测与跟踪算法[J],2010,38(2):223-229
[13]黄永林,叶玉堂,陈镇龙,乔闹生.一种新的快速Hough变换圆检测方法[J],2010,24(9):837-841
[14]陈洪波,王强,徐晓蓉,陈真诚,汤井田.用改进的Hough变换检测交通标志图像的直线特征[J],2009,17(5):1111-1118
[15]K ast rinaki V , Zervakis M , K alait zakis K . A survey of video pr oces sing t echniques f or t raf fi c appli cat ions [J] .Image and programming f or V is ion Com put ing, 2003, 21( 1) : 359.
[16]Lee J W. A machin e vi sion s yst em f or lane depart u re det ect ion [J] . C om put er V ision Image U nderst , 2002, 86
[17]Isard M A , Blake. Condensat ion con diti on al den sit y propagat ion f or vi sual track ing [J] . Int ern at ional Journal of Computer Vision,1998,29(1):5
[18]Adrian Kaehler, Dr. Gary Rost Bradski.Learnint OpenCV[M].O'RELILY,2008
[19]D.Pomerleau,RALPH:Rapidly Adapting Lateral Position Handler,Proc.IEEE Symposium on Intelligent Vehicles,Detroit,USA,1995:506-511
[20]M.Bertozzi and A.Broggi.GOLD:A Parallel Real Time Stereo Vision System for Generic Obstacle and Lane Detection,IEEE Trans.On Image Proc.1998,7(1):62-81
[21]You Feng.Intelligent vehicle automatically change the way and automatic control method of overtaking[D].JiLin university.2005:44-46
[22]Guan xin.High-speed car lane departure warning system algorithms.Jilin university Phd.paper.2004:12-23
车道线识别程序源代码,编译要求:Linux Kernel2.6或以上,Glibc2.13或以上,OpenCV2.1或以上,Gcc4.7或以上版本。
#include
#include
#include
#include
#include
#include
#define RED cvScalar(0,0,255,0)
#define GREEN cvScalar(0,255,0,0)
#define BLUE cvScalar(255,0,0,0)
#define ROI cvRect(0,(int)src->height / 3,src->width,src->height)
#define LOW_THRESH 80
#define HIGH_THRESH 255
#define MAXDISTANCE 10
//img为加载的源图像,src为处理过程中所用的灰度图像
//temp为临时需要时的中转图像,out为最后输出的三通道图像
IplImage *img,*src,*temp,*out;
CvMemStorage *storage;
CvSeq *lines;
CvSeq *contours;
typedef struct
{
float a;
float b;
float c;
int is_vertical;
}Line;
Line *get_tangent(CvSeq *seq,int n);
Line get_line_func(CvPoint p1,CvPoint p2);
CvPoint *get_join(CvPoint *p1,Line l1,CvPoint *p2,Line l2);
CvPoint get_center(CvPoint p1,CvPoint p2);
double points_distance(CvPoint p1,CvPoint p2);
int is_ellipse(CvSeq *);
int main(int argc,char *argv[])
{
//图像的加载过程
if (argc != 2)
{
puts("Usage : ./mark filename");
exit(1);
}
if ((img = cvLoadImage(argv[1],CV_LOAD_IMAGE_UNCHANGED)) == NULL)
{
printf("can not load image %s.\n",argv[1]);
exit(1);
}
src = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
temp = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
out = cvCreateImage(cvGetSize(img),img->depth,3);
if (img->nChannels >= 3)
{
cvCvtColor(img,src,CV_RGB2GRAY);
cvCopy(img,out,NULL);
}
else
{
cvCopy(img,src,0);
cvCvtColor(img,out,CV_GRAY2RGB);
}
storage = cvCreateMemStorage(0);
//设置ROI
cvSetImageROI(src,ROI);
//滤波
cvSmooth(src,src,CV_MEDIAN,5,5,0,0);
cvSmooth(src,src,CV_GAUSSIAN,5,5,0,0);
//大津法阈值分割
cvThreshold(src,src,LOW_THRESH,HIGH_THRESH,CV_THRESH_OTSU);
//边缘检测
cvSetImageROI(temp,ROI);
cvCanny(src,temp,LOW_THRESH,HIGH_THRESH,3);
cvResetImageROI(src);
cvResetImageROI(temp);
cvCopy(temp,src,0);
cvSetImageROI(src,ROI);
//hough直线检测
lines=cvHoughLines2(src,storage,CV_HOUGH_PROBABILISTIC,0.5,CV_PI / 180,5,10,5);
//绘制检测到的直线
int i;
cvSetImageROI(out,ROI);
for (i = 0;i < lines->total;i++)
{
CvPoint *endpoints = (CvPoint *)cvGetSeqElem(lines,i);
cvLine(out,endpoints[0],endpoints[1],RED,2,8,0);
}
//边缘提取
int sum_of_area = 0;
sum_of_area=cvFindContours(src,storage,&contours,sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0));
for (i = 0;i < sum_of_area;contours = contours->h_next,i++)
{
if (contours->total <= 50)
continue;
if (is_ellipse(contours))
{
puts("OK");
cvDrawContours(out,contours,BLUE,GREEN,LOW_THRESH,2,8,cvPoint(0,0));
}
}
cvResetImageROI(out);
cvRectangle(out,cvPoint(0,(int)out->height / 3),cvPoint(out->width,out->height),GREEN,2,8,0);
cvNamedWindow("IMG",0);
cvNamedWindow("OUT",0);
cvShowImage("IMG",img);
cvShowImage("OUT",out);
cvWaitKey(0);
return 0;
}
Line *get_tangent(CvSeq *seq,int n)
{
Line *result = (Line *)malloc(sizeof(Line));
if (n >= seq->total || n < 4)
return (Line *)NULL;
CvPoint *p = (CvPoint *)cvGetSeqElem(seq,n);
double sum_of_xy = 0.0,sum_of_x = 0.0,sum_of_y = 0.0,sum_of_x2 = 0.0;
int i;
for (i = n - 4;i <= n + 4;i++)
{
CvPoint *temp = (CvPoint *)cvGetSeqElem(seq,i);
sum_of_xy += (double)(temp->x * temp->y);
sum_of_x += (double)temp->x;
sum_of_y += (double)temp->y;
sum_of_x2 += (double)(temp->x * temp->x);
}
#ifndef SIZEOFTANGENTAREA
#define SIZEOFTANGENTAREA 9
result->a = (SIZEOFTANGENTAREA * sum_of_xy - sum_of_x * sum_of_y) / (SIZEOFTANGENTAREA * sum_of_x2 - sum_of_x * sum_of_x);
result->c = sum_of_y / SIZEOFTANGENTAREA - result->a * sum_of_x / SIZEOFTANGENTAREA;
result->b = -1.0;
result->is_vertical = 0;
#endif
return result;
}
Line get_line_func(CvPoint p1,CvPoint p2)
{
Line result;
if (p1.x == p2.x)
result.is_vertical = 1;
else
{
result.a = p2.y - p1.y;
result.b = p1.x - p2.x;
result.c = -(result.a * p1.x + result.b * p1.y);
result.is_vertical = 0;
}
return result;
}
CvPoint *get_join(CvPoint *p1,Line l1,CvPoint *p2,Line l2)
{
CvPoint *p = (CvPoint *)malloc(sizeof(CvPoint));
if (l1.is_vertical != 1 && l2.is_vertical != 1)
{
if (fabs(l1.a / l1.b - l2.a / l2.b) >= 0.000001)
{
p->x = (l1.b * l2.c - l2.b * l1.c) / (l2.b * l1.a - l1.b * l2.a);
p->y = (l1.a * l2.c - l1.c * l2.a) / (l1.b * l2.a - l1.a * l2.b);
return p;
}
else
return NULL;
}
else if (l1.is_vertical && l2.is_vertical != 1)
{
p->x = p1->x;
p->y = p1->x * l2.a / (-l2.b) + l2.c / (-l2.b);
return p;
}
else if (l2.is_vertical && l1.is_vertical != 1)
{
p->x = p2->x;
p->y = p2->x * l1.a / (-l1.b) + l1.c / (-l1.b);
return p;
}
else return NULL;
}
CvPoint get_center(CvPoint p1,CvPoint p2)
{
CvPoint center;
center.x = (p1.x + p2.x) >> 1;
center.y = (p1.y + p2.y) >> 1;
return center;
}
int is_ellipse(CvSeq *contours)
{
if (contours->total <= 50)
return 0;
int pos1 = contours->total - 10;
int pos2 = pos1 -20;
int pos3 = pos1 - 30;//在曲线上选三个点
CvPoint *p1 = (CvPoint *)cvGetSeqElem(contours,pos1);
CvPoint *p2 = (CvPoint *)cvGetSeqElem(contours,pos2);
CvPoint *p3 = (CvPoint *)cvGetSeqElem(contours,pos3);//得到三个点
Line *tangent1 = get_tangent(contours,pos1);
Line *tangent2 = get_tangent(contours,pos2);
Line *tangent3 = get_tangent(contours,pos3);//分别得到相应的三条切线
CvPoint *join1_2,*join1_3,*join2_3;//三条切线的交点
if (tangent1 && tangent2)
join1_2 = get_join(p1,*tangent1,p2,*tangent2);
if (tangent1 && tangent3)
join1_3 = get_join(p1,*tangent1,p3,*tangent3);
if (tangent2 && tangent3)
join2_3 = get_join(p2,*tangent2,p3,*tangent3);
CvPoint cp1_2 = get_center(*p1,*p2);
CvPoint cp1_3 = get_center(*p1,*p3);
CvPoint cp2_3 = get_center(*p2,*p3);//得到相应的三个中点
Line join_center1_2,join_center1_3,join_center2_3;
if (join1_2)
join_center1_2 = get_line_func(*join1_2,cp1_2);
if (join1_3)
join_center1_3 = get_line_func(*join1_3,cp1_3);
if (join2_3)
join_center2_3 = get_line_func(*join2_3,cp2_3);
CvPoint *last_join1 = get_join(&cp1_2,join_center1_2,&cp1_3,join_center1_3);
CvPoint *last_join2 = get_join(&cp1_2,join_center1_2,&cp2_3,join_center2_3);
CvPoint *last_join3 = get_join(&cp1_3,join_center1_3,&cp2_3,join_center2_3);
CvPoint CENTER;
if (last_join1 && last_join2 && last_join3)
CENTER = cvPoint((last_join1->x+last_join2->x+last_join3->x) / 3,(last_join1->y+last_join2->y+last_join3->y) / 3);
if (last_join1 && last_join2 && last_join3)
{
if (points_distance(*last_join1,CENTER)<= MAXDISTANCE
&& points_distance(*last_join1,CENTER) <= MAXDISTANCE && points_distance(*last_join2,CENTER) <= MAXDISTANCE)
{
if (last_join1)
{
printf("last_join1->x = %d,last_join1->y = %d\n",last_join1->x,last_join1->y);
CvPoint p_temp;
p_temp.x = p1->x + 80;
p_temp.y = (int)(80 * tangent1->a + p1->y);
}
if (last_join2)
{
printf("last_join2->x = %d,last_join2->y = %d\n",last_join2->x,last_join2->y);
CvPoint p_temp;
p_temp.x = p2->x + 80;
p_temp.y = (int)(80 * tangent2->a + p2->y);
}
if (last_join3)
{
printf("last_join3->x = %d,last_join3->y = %d\n",last_join3->x,last_join3->y);
CvPoint p_temp;
p_temp.x = p3->x + 80;
p_temp.y = (int)(80 * tangent3->a + p3->y);
}
srand(time(0));
int ran_pos = contours->total - rand() % 30;
CvPoint *p4 = (CvPoint *)cvGetSeqElem(contours,ran_pos);//在一定范围内得到一个随机的点
Line *tangent4 = get_tangent(contours,ran_pos);
CvPoint *join1_4;
if (tangent1 && tangent4)
join1_4 = get_join(p1,*tangent1,p4,*tangent4);
CvPoint cp1_4 = get_center(*p1,*p4);
Line join_center1_4 = get_line_func(*join1_4,cp1_4);
CvPoint *last_join4 = get_join(&cp1_2,join_center1_2,&cp1_4,join_center1_4);
if (points_distance(*last_join1,CENTER) <= MAXDISTANCE)
return 1;
else
{
printf("dis =
%f\n",points_distance(*last_join1,*last_join4));
return 0;
}
}
}
}
double points_distance(CvPoint p1,CvPoint p2)
{
double distance;
distance = fabs(sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)));
return distance;
}