目录
8.1 分别提取yellow pixels(HSV) & white pixels
利用OpenCV工具库,采用一些图像处理方法,实现车道线的简单检测。
用到的cv库函数:
cv2.fillPoly()
for regions selectioncv2.line()
to draw lines on an image given endpointscv2.addWeighted()
to coadd / overlay two imagescv2.cvtColor()
to grayscale or change colorcv2.bitwise_and()
to apply a mask to an image
思路:
- Grayscale将图片灰度化(方便后续的边缘检测)
- Canny Edge Detection边缘检测(Canny 边缘检测,检测出边缘像素点)
- Hough Transform 霍夫变换(检测边缘像素点的可能直线)
- Group Left and Right Lines 根据直线斜率将霍夫变换检测出的直线分组:left or right
- Fit and Draw 将左右两组的线分别拟合为一条线,并设定 y_bottom 和y_top,根据拟合方程分别求出left和right的 x_bottom和x_top,完成检测,左线:(left_x_bottom ,y_bottom,left_x_top,y_top),右线:(right_x_bottom ,y_bottom,right_x_top,y_top)
1 导入工具库,查看图片
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import cv2
# reading in an image
image = mpimg.imread('whiteCarLaneSwitch.jpg')
# printing out some stats and plotting the image
print('This image is:', type(image), 'with dimensions:', image.shape)
plt.imshow(image)
plt.show()
2 图片灰度处理
读入的图像是RGB格式,channel=3,后续进行边缘检测时只关注color的强度变化,对于color是什么并不会影响检测结果。所以先将图像灰度化,作为后续边缘检测的预处理。
灰度化的结果是只有一个通道的图像,但是如果要查看灰度图片,需要 plt.imshow(gray_img,cmap='gray')。
def grayscale(img):
"""
将图像灰度化,作为后续边缘检测的预处理
"""
return cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
gray_img=grayscale(image)
plt.imshow(gray_img)
3 Canny边缘检测
边缘检测的目的,是检测出最类似边缘的像素点,其结果是一些pixels。这些pixels的特点是,像素值变化大,也就是梯度较大。Canny边缘检测基于x y方向的像素梯度来实现检测。
cv2.Canny(img,low_threshold,high_threshold):
大于 high_threshold: edge;
小于 low_threshold: not edge;
中间值(low_threshold, high_threshold):若与edge相连,则认为是edge,不相连则 not edge
def canny(img,low_threshold=100,high_threshold=200):
"""
input是经过灰度处理的灰度图像,不关注颜色,只关注颜色强度
边缘检测,原理:计算颜色值的梯度和方向,找到梯度变化较大的像素
>high_threshold: edge
<low_threshold:not edge
中间值:若与edge相连,则认为是edge,不相连则not edge
"""
return cv2.Canny(img,low_threshold,high_threshold)
canny_img=canny(gray_img)
plt.imshow(canny_img)
4 划定ROI
根据行车记录仪图像特点,我们将目标区域缩小为一个三角形(底边在bottom上,顶点大约在图像中心位置),划定该区域为ROI(Region of Interests),将其他区域里的目标进行遮挡(mask 掩膜操作)。
def region_of_interest(img):
"""
分割出ROI,并填充,再对图像掩膜处理,将其他object遮挡
"""
#draw the polygon with three vertices
height=img.shape[0]
width=img.shape[1]
region_of_interest_vertices = [
(0, height),
(width / 2, height / 2),
(width, height)]
# Define a blank matrix that matches the image height/width.
mask = np.zeros_like(img)
# Retrieve the number of color channels of the image.
#channel_count = img.shape[2]
# Create a match color with the same color channel counts.
#match_mask_color = (255,) * channel_count #彩色图像时的color
match_mask_color = 255
# Fill inside the polygon
#cv2.fillPoly(mask, np.array([region_of_interest_vertices],np.int32),match_mask_color)#多边形填充
cv2.fillPoly(mask, np.array([region_of_interest_vertices],np.int32), match_mask_color)
#按位与操作,对图片进行mask淹膜操作,ROI区域正常显示,其他区域置零
masked_image = cv2.bitwise_and(img, mask)
return masked_image
ROI_img=region_of_interest(canny_img)
plt.imshow(ROI_img)
5 霍夫变换,直线检测
Canny 边缘检测结果,只是一些类似边缘的pixels,利用霍夫变换求出其中可能存在的直线。霍夫变换的原理:将该点映射到霍夫空间(r θ坐标系),该空间的每条线都代表过图像空间中一点的所有直线集合,求空间里线与线的交点,即为图像空间的过某几个点的直线。霍夫变换的结果是多条直线的坐标[ [x1,y1,x2,y2], [x x y y]...]
可以看到,霍夫变换的结果中线可能是不连续的,是由多条线组成的。
def hough_lines(img,rho=6,theta=np.pi/180,threshold=160,
min_line_len=40,max_line_gap=25):
"""
将canny边缘检测后的像素点连接成线,并调用划线函数,返回画线的图片
hough transform原理:转换到 hough space,r,θ极坐标,正弦曲线,
每条线代表个image space中过一点的所有线,
lines的交点代表共线,找到交点再返回到image space空间
lines:[[x1,y1,x2,y2],[x x y y]...]
"""
lines=cv2.HoughLinesP(img,rho,theta,threshold, np.array([]),
minLineLength=min_line_len, maxLineGap=max_line_gap)
return lines
def draw_lines(img,lines,color=[255,0,0],thickness=3):
"""
生成一个zeros图划线,并与原图混合
"""
if lines is None:
return
img=np.copy(img)
#生成待划线的图,zeros,addweighted混合的时候,0值不会显示
img_channels=img.shape[2]
line_img=np.zeros((img.shape[0],img.shape[1],img_channels),dtype=np.uint8)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(line_img,(x1,y1),(x2,y2),color,thickness)
#将划线图与原图合并
#cv2.addWeighted(initial_img, α, img, β , γ)
#output: initial_img * α + img * β + γ
img=cv2.addWeighted(img, 0.8, line_img, 1.0, 0.0) #叠加的两幅图片尺寸和channel需要一致
return img
lines=hough_lines(ROI_img)
line_img=draw_lines(image,lines)
plt.imshow(line_img)