![](https://img.haomeiwen.com/i8207483/972192b96770c404.jpg)
甜品
为什么喜欢无人驾驶任务,主要原因无人驾驶任务是机器学习为主的一门前沿领域,在无人驾驶领域中机器学习的各种算法随处可见,其实这么说也不算严谨。而于其他机器学习应用的场景中,机器学习多半是一种辅助或者锦上添花的技术。
分享内容
以后分享主要分几个部分
- 目标
- 原理(做任务基本思路)
- TODO 也就是随后要做的事以及要优化的内容
- 难点,关于这个任务难点,我们可以一起讨论
车道线检测
在无人驾驶领域每一个任务都是相当复杂,看上去无从下手。那么面对这样极其复杂问题,我们解决问题方式从先尝试简化问题,然后由简入难一步一步尝试来一个一个地解决问题。车道线检测在无人驾驶中应该算是比较简单的任务,依赖计算机视觉一些相关技术,通过读取 camera 传入的图像数据进行分析,识别出车道线位置,我想这个对于 lidar 可能是无能为力。所以今天我们就从最简单任务说起,看看有哪些技术可以帮助我们检出车道线。
我们先把问题简化,所谓简化问题就是用一些条件限制来缩小车道线检测的问题。我们先看数据,也就是输入算法是车辆行驶的图像,输出车道线位置。
更多时候我们如何处理一件比较困难任务,可能有时候我们拿到任务时还没有任何思路,不要着急也不用想太多,我们先开始一步一步地做,从最简单的开始做起,随着做就会有思路,同样一些问题也会暴露出来。我们先找一段视频,这段视频是我从网上一个关于车道线检测项目中拿到的,也参考他的思路来做这件事。好现在就开始做这件事,那么最简单的事就是先读取视频,然后将其显示在屏幕以便于调试。
目标
检测图像中车道线位置,将车道线信息提供路径规划。
思路
- 图像灰度处理
- 图像高斯平滑处理
- canny 边缘检测
- 区域 Mask
- 霍夫变换
- 绘制车道线
import cv2
import numpy as np
import sys
import pygame
from pygame.locals import *
class Display(object):
def __init__(self,Width,Height):
pygame.init()
pygame.display.set_caption('Drive Video')
self.screen = pygame.display.set_mode((Width,Height),0,32)
def paint(self,draw):
self.screen.fill([0,0,0])
draw = cv2.transpose(draw)
draw = pygame.surfarray.make_surface(draw)
self.screen.blit(draw,(0,0))
pygame.display.update()
if __name__ == "__main__":
solid_white_right_video_path = "test_videos/solidWhiteRight.mp4"
cap = cv2.VideoCapture(solid_white_right_video_path)
Width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
Height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
display = Display(Width,Height)
while True:
ret, draw = cap.read()
draw = cv2.cvtColor(draw,cv2.COLOR_BGR2RGB)
if ret == False:
break
display.paint(draw)
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
上面代码我就不多说了,默认您对 python 是有所了解,关于如何使用 opencv 读取图片网上代码示例也很多,大家一看就懂。这里因为我用的是 mac 有时候显示视频图像可能会有些问题,所以我们用 pygame 来显示 opencv 读取图像。这个大家根据自己实际情况而定吧。值得说一句的是 opencv 读取图像是 BGR 格式,要想在 pygame 中正确显示图像就需要将 BGR 转换为 RGB 格式。
车道线区域
现在这个区域是我们根据观测图像绘制出来,
![](https://img.haomeiwen.com/i8207483/853b5e2102f1e28f.jpg)
<img src="images/drive_on_road.jpg">
颜色选择
def color_select(img,red_threshold=200,green_threshold=200,blue_threshold=200):
ysize,xsize = img.shape[:2]
color_select = np.copy(img)
rgb_threshold = [red_threshold, green_threshold, blue_threshold]
thresholds = (img[:,:,0] < rgb_threshold[0]) \
| (img[:,:,1] < rgb_threshold[1]) \
| (img[:,:,2] < rgb_threshold[2])
color_select[thresholds] = [0,0,0]
return color_select
<img src="images/color_select.jpg">
![](https://img.haomeiwen.com/i8207483/7f602711efff7c13.jpg)
draw = color_select(draw)
区域
我们要检测车道线位置相对比较固定,通常出现车的前方,所以我们通过绘制,也就是仅检测我们关心区域。通过创建 mask 来过滤掉那些不关心的区域保留关心区域。
canny 边缘检测
有关边缘检测也是计算机视觉。首先利用梯度变化来检测图像中的边,如何识别图像的梯度变化呢,答案是卷积核。卷积核是就是不连续的像素上找到梯度变化较大位置。我们知道 sobal 核可以很好检测边缘,那么 canny 就是 sobal 核检测上进行优化。
def canny_edge_detect(img):
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
low_threshold = 180
high_threshold = 240
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)
return edges
<img src="images/canny_edge_detect.jpg"/>
![](https://img.haomeiwen.com/i8207483/73933de2307c9d85.jpg)
霍夫变换(Hough transform)
霍夫变换是将 x 和 y 坐标系中的线映射表示在霍夫空间的点(m,b)。所以霍夫变换实际上一种由繁到简(类似降维)的操作。当使用 canny 进行边缘检测后图像可以交给霍夫变换进行简单图形(线、圆)等的识别。这里用霍夫变换在 canny 边缘检测结果中寻找直线。
mask = np.zeros_like(edges)
ignore_mask_color = 255
# 获取图片尺寸
imshape = img.shape
# 定义 mask 顶点
vertices = np.array([[(0,imshape[0]),(450, 290), (490, 290), (imshape[1],imshape[0])]], dtype=np.int32)
# 使用 fillpoly 来绘制 mask
cv2.fillPoly(mask, vertices, ignore_mask_color)
masked_edges = cv2.bitwise_and(edges, mask)
# 定义Hough 变换的参数
rho = 1
theta = np.pi/180
threshold = 2
min_line_length = 4 # 组成一条线的最小像素数
max_line_gap = 5 # 可连接线段之间的最大像素间距
# 创建一个用于绘制车道线的图片
line_image = np.copy(img)*0
# 对于 canny 边缘检测结果应用 Hough 变换
# 输出“线”是一个数组,其中包含检测到的线段的端点
lines = cv2.HoughLinesP(masked_edges, rho, theta, threshold, np.array([]),
min_line_length, max_line_gap)
# 遍历“线”的数组来在 line_image 上绘制
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)
color_edges = np.dstack((edges, edges, edges))
<img src="images/simple_lane_detector.jpg">
import math
import cv2
import numpy as np
"""
Gray Scale
Gaussian Smoothing
Canny Edge Detection
Region Masking
Hough Transform
Draw Lines [Mark Lane Lines with different Color]
"""
class SimpleLaneLineDetector(object):
def __init__(self):
pass
def detect(self,img):
# 图像灰度处理
gray_img = self.grayscale(img)
print(gray_img)
#图像高斯平滑处理
smoothed_img = self.gaussian_blur(img = gray_img, kernel_size = 5)
#canny 边缘检测
canny_img = self.canny(img = smoothed_img, low_threshold = 180, high_threshold = 240)
#区域 Mask
masked_img = self.region_of_interest(img = canny_img, vertices = self.get_vertices(img))
#霍夫变换
houghed_lines = self.hough_lines(img = masked_img, rho = 1, theta = np.pi/180, threshold = 20, min_line_len = 20, max_line_gap = 180)
# 绘制车道线
output = self.weighted_img(img = houghed_lines, initial_img = img, alpha=0.8, beta=1., gamma=0.)
return output
def grayscale(self,img):
return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
def canny(self,img, low_threshold, high_threshold):
return cv2.Canny(img, low_threshold, high_threshold)
def gaussian_blur(self,img, kernel_size):
return cv2.GaussianBlur(img, (kernel_size, kernel_size), 0)
def region_of_interest(self,img, vertices):
mask = np.zeros_like(img)
if len(img.shape) > 2:
channel_count = img.shape[2]
ignore_mask_color = (255,) * channel_count
else:
ignore_mask_color = 255
cv2.fillPoly(mask, vertices, ignore_mask_color)
masked_image = cv2.bitwise_and(img, mask)
return masked_image
def draw_lines(self,img, lines, color=[255, 0, 0], thickness=10):
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(img, (x1, y1), (x2, y2), color, thickness)
def slope_lines(self,image,lines):
img = image.copy()
poly_vertices = []
order = [0,1,3,2]
left_lines = []
right_lines = []
for line in lines:
for x1,y1,x2,y2 in line:
if x1 == x2:
pass
else:
m = (y2 - y1) / (x2 - x1)
c = y1 - m * x1
if m < 0:
left_lines.append((m,c))
elif m >= 0:
right_lines.append((m,c))
left_line = np.mean(left_lines, axis=0)
right_line = np.mean(right_lines, axis=0)
for slope, intercept in [left_line, right_line]:
rows, cols = image.shape[:2]
y1= int(rows)
y2= int(rows*0.6)
x1=int((y1-intercept)/slope)
x2=int((y2-intercept)/slope)
poly_vertices.append((x1, y1))
poly_vertices.append((x2, y2))
self.draw_lines(img, np.array([[[x1,y1,x2,y2]]]))
poly_vertices = [poly_vertices[i] for i in order]
cv2.fillPoly(img, pts = np.array([poly_vertices],'int32'), color = (0,255,0))
return cv2.addWeighted(image,0.7,img,0.4,0.)
def hough_lines(self,img, rho, theta, threshold, min_line_len, max_line_gap):
lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap)
line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
line_img = self.slope_lines(line_img,lines)
return line_img
def weighted_img(self,img, initial_img, alpha=0.1, beta=1., gamma=0.):
lines_edges = cv2.addWeighted(initial_img, alpha, img, beta, gamma)
return lines_edges
def get_vertices(self,image):
rows, cols = image.shape[:2]
bottom_left = [cols*0.15, rows]
top_left = [cols*0.45, rows*0.6]
bottom_right = [cols*0.95, rows]
top_right = [cols*0.55, rows*0.6]
ver = np.array([[bottom_left, top_left, top_right, bottom_right]], dtype=np.int32)
return ver
![](https://img.haomeiwen.com/i8207483/20e46a170c08dbb5.jpg)
TODO
- 车道线区域
困难
- 复杂路况,城市路况
- 车道线被前车覆盖
- 雨雪天气
网友评论