梯度:
之前前言里的边界检测能检测到很多边界,不仅是道路线的还包括了各种景物,车,路标等干扰因素。
但是我们知道我们要找的道路线应该是相对比较垂直于路面的,所以我们可以把找到的线和图片的x轴和y轴再计算梯度。
索贝尔算子(Sobel Operator):
两个大于等于3并且大小为奇数的方形矩阵,用和图像的乘积来判断在矩阵范围内,图像的颜色在x轴和y轴方向上的变化大小。
如果结果矩阵各数值之和为0说明图像颜色在该轴上没有变化。
默认的算子矩阵大小(kernel size)是3,如果加大的话可以平滑一些无用的强烈梯度。
有用的函数:
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
注:如果是用mpimg.imread()读的图片那么用cv2.COLOR_RGB2GRAY,如果用cv2.imread()读的图片那么用cv2.COLOR_BGR2GRAY。
在x和y方向上的梯度:
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)
abs_sobelx = np.absolute(sobelx)
scaled_sobel = np.uint8(255*abs_sobelx/np.max(abs_sobelx)) (注:这虽然不是必须的但是统一规格有助于以后统一阈值)
thresh_min = 20
thresh_max = 100
sbinary = np.zeros_like(scaled_sobel)
sbinary[(scaled_sobel >= thresh_min) & (scaled_sobel <= thresh_max)] = 1
plt.imshow(sxbinary, cmap='gray')
由实验可知,道路线会在x轴梯度上更明显,但是y轴梯度上也能看到。
梯度的大小(绝对值)可通过梯度的平方开根号得到。如果是计算两轴的梯度大小,那么就是梯度平方和开根号。
梯度的方向就是arctan(sobely/sobelx),理论上计算结果的范围在+/−π之间,但是由于sobelx会被取绝对值所以结果会在+/−π/2之间。
代码中用np.arctan2().
将梯度大小,两轴梯度大小和方向的阈值结合,调整参数至可以检测出道路线
颜色空间:
前面我们将图片转化为灰度图之后损失了颜色信息,有时候这可能会让我们更难分辨出道路线(如黄色的线变成灰度之后颜色会和灰色的道路很接近),所以我们需要颜色空间为我们提供更多的信息。
除了RGB之外,还有HSV (hue色相, saturation饱和度, value明度) 和HLS (hue色相, lightness亮度, saturation饱和度) 颜色空间。
色相指的是和亮度无关的颜色,也就是说往该颜色上加黑色或者白色,将颜色变得更深或者更浅,对色相是没有影响的。
明度和亮度都是用来表示颜色的明暗或者深浅。
饱和度指的是颜色的鲜艳程度,比如和白色更接近的颜色饱和度更低,和亮红,亮黄,亮蓝接近的颜色饱和度高。
这些颜色的表达方法是通过人类视觉感知或者是为了电视视频/电脑图像的显示而被开发出来的。
有用的函数:
hls = cv2.cvtColor(im, cv2.COLOR_RGB2HLS) (这里顺便会把RGB值转化为0-1之间的值)
也可以使用纯数学的方法完成转换,要知道的是RGB值,算出其中最大的值(Vmax = max(R,G,B))和最小的值(Vmin = min(R,G,B))之后进行一系列运算。
通过比较用颜色值的阈值(分0,1)提取R,G,B,灰度图,H,L,S图中的道路线,会发现S图出的效果最好,而且稳定性也好(不会因为光线影响结果)。
但是R图在白线的处理上可能比S图的结果还好,所以可以考虑结合各种图像的阈值得到一个稳定的结果。