(图片的读取、展示与保存)
代码解释:
import cv2 //引入OpenCV库
cv2.imread('',)//读取图片命令
里面有两个参数,分别是文件名和标志位(指定了Opencv以什么样的色彩格式来读取原图像)
这里是读取了文件里的图片,以黑白色彩格式来读取的。(.jpg很重要不能省去)这里读取的是一个矩阵格式。
cv2.imshow('',)//展示图片的命令
里面有两个参数,分别是展示的窗体名和图片矩阵。
cv2.waitKey()//等待键盘传出字符
这里可以让图片一直展示在这里,直到输入一个字符。
cv2.imwrite()//将内存中的图片写入文件中
第一个参数是文件名,第二个参数是图像矩阵
这里可以看到已经把读取的灰色图像保存进了文件中。
Canny边缘检测-算法
Canny算法是通过求取图像上每一个像素点周边图像像素变化的梯度来确定这个点是否是边缘
通过梯度计算观测梯度(值越大,越有可能是边缘)
(双阈值法)
上面为边缘、中间为弱边缘,下面不是边缘(受光照等因素大)
在Opencv中使用函数
cv2.Canny()//实现边缘检测
第一个参数是要进行边缘检测的图片矩阵,第二个参数和第三个参数分别是上边缘和下边缘(阈值参数需要经验来确定)
这里上边缘下边缘分别取的50和100,如果判断这个点是边缘时,那么这个点的像素就是255,反之则为零,为后续做车道检测的作用还是挺大的,这里检测效果还行。
这里也对车道线进行了一个检测算法,车道线检测的还是非常明显的,就是图不太好,树阴影太多了。
ROI(region of interest 感兴趣的区域) mask
方式:
数组切片(矩形)
布尔运算(利用掩码)
目的:剔除无关信息
就是把原图扣出一小部分,可以理解为抠图。
图像以矩阵np.array形式存储在内存中(构建掩码需要)
np.zeros.like(传入一个矩阵获得一个新矩阵)
import numpy as np
mask=np.zeros_like(edge_img)
cv2.fillPoly()//填充多边形
参数一是构建的新矩阵(mask),参数二是构建的四个坐标位置(自己选择),参数三是颜色
这样可以得到一个区域的掩码
cv2.bitwise_and()//布尔运算,ROI
第一个参数是原图,第二个参数是掩码。
这样就可以看出来我们完成的图像就只有车道线的信息。
代码与效果如下:
原图(边缘检测与ROI后)
进行了很长时间的试错,最开始出来的图只能是梯形类型的,最前面的一部分一直出不来,通过换很多坐标也不行,查阅资料发现有ROI可以六边形设置的,但是这个代码中只能加入四个参数。
霍夫变换(把圆或者直线从直角坐标系映射到极坐标系上,根据极坐标的性质来寻找直线)——去除一些噪点——只针对灰度图
代码:
cv2.HoughLinesP(edge_img,1,np.pi/180,15,minLineLength=40,maxLineGap=20)
参数一:要检测的图片矩阵
参数二:距离r的精度,值越大,考虑越多的线
参数三:距离theta的精度,值越大,考虑越多的线
参数四:累加数阈值,值越小,考虑越多的线
minLineLength:最短长度阈值,短于这个长度的线会被剔除
maxLineGap:同一直线两点之间的最大距离
返回值为矩阵形式
这里通过霍夫变换对图片的直线进行了计算,一共有666条,这里也说明了霍夫变换的局限性,因为这个图片存在的噪点比较多,并且同一直线有可能出现重叠的直线。(宽度问题)
这里再根据两条直线的斜率来分类。
这里定义了一个函数用来计算直线的斜率。
通过line for line循环计算出左车道线有74条,右车道线有587条,这说明误差还是很大
离群值过滤
目的是:剔除掉因为误差而被识别的直线。
def reject_abnormal_lines(lines,threshold): """定义一个函数剔除斜率不一致的斜率""" slopes=[calculate_slope(line) for line in lines] while len(lines)>0: mean=np.mean(slopes) """求出所有斜率的平均值""" diff=[abs(s-mean) for s in slopes] """求每个斜率与平均值的差值""" idx=np.argmax(diff) if diff[idx]>threshold: """超过了插值""" slopes.pop(idx) """重新计算斜率""" lines.pop(idx) """删除该斜率""" else: break return lines
这里设置的阈值比较大0.2,可以看到左右车道线分别剔除了很多线段
最小二乘拟合
识别车道线的最后一步——分别将所有左右车道线拟合为对应的一条车道线
定义一个函数将lines中的线段拟合成一条线段
def least_squares_fit(lines): """将lines中的线段拟合成一条线段""" #取出所有坐标点 x_coords = np.ravel([[line[0][0],line[0][2]] for line in lines]) y_coords = np.ravel([[line[0][1], line[0][3]] for line in lines]) #进行直线拟合,得到多项式系数 poly=np.polyfit(x_coords,y_coords,deg=1) #根据多项式系数,计算两个直线上的点,用于唯一确定这条直线 point_min=(np.min(x_coords),np.polyval(poly,np.min(x_coords))) point_max=(np.max(x_coords),np.polyval(poly,np.max(x_coords))) return np.array([point_min,point_max],dtype=np.int
这里已经通过最小二乘法得到了拟合的两条左右车道线
直线绘制:cv2.line
前面的所有步骤都是为了这一步,画出车道线,这一步比较简单
cv2.line(img,(10,10),(200,100),255,3)
五个参数分别是1.你要在哪个图上绘制2.起始点3.结束点4.颜色5.厚度
之前那个图生成效果不太好,重新换了个图感觉效果还不错。
总结:
总的分析一下利用Opencv进行车道线的检测:
图片读取展示与保存:把有车道线的图片保存为黑白形式的,便于机器检测
Canny边缘检测:通过求取图像上每一个像素点周边图像像素变化的梯度来确定这个点是否是边缘,因为车道线就是属于边缘线,所以车道线能够被检测出来并且在黑白图里显示出来。
ROI mask:把边缘检测后的整张图片剪切为一个梯形(含车道线),减少其他噪音的影响
霍夫变换:把图片内所有含直线特征的提取出来,这里也是去除一些点的噪音影响。(他只能处理灰度图)
离群值过滤:将所有提取的直线根据斜率分为左右车道线并剔除掉斜率相差较大的误差。
最小二乘拟合:分别将所有左右车道线拟合为对应的一条车道线,为后续直线绘制做好准备
直线绘制:这个就比较简单的一个函数根据最小二乘拟合的点就可以在原图绘画出车道线了
最后就完成了对车道线的检测