# opencv入门项目——车道线检测

### Canny 边缘检测

``````import cv2

edge_img = cv2.Canny(img, 50, 100)

cv2.imshow('edges', edge_img)
cv2.waitkey(0)

``````

``````edge_img = cv2.Canny(img, 70, 120)

cv2.imshow('edges', edge_img)
cv2.waitkey(0)
``````

#### 小程序

``````import cv2

cv2.namedWindow('edge_detection')
cv2.createTrackbar('minThreshold', 'edge_detection', 50, 1000, lambda x: x)
cv2.createTrackbar('maxThreshold', 'edge_detection', 100, 1000, lambda x: x)

while True:
minThreshold = cv2.getTrackbarPos('minThreshold', 'edge_detection')
maxThreshold = cv2.getTrackbarPos('maxThreshold', 'edge_detection')
edges = cv2.Canny(img, minThreshold, maxThreshold)
cv2.imshow('edge_detection', edges)
cv2.waitKey(10)A
``````

#### 实现

• ROI（region of interest，感兴趣的区域）
• 数组切片
• 布尔运算（与运算）
• 图像以矩阵 np.array 形式存储在内存中
• np.zeros_like
• cv2.fillPoly
• cv2.bitwise_and / np.bitwise_and
``````import cv2
import numpy as np

cv2.fillPoly(mask, np.array([[[0 ,368], [240, 210], [300, 210], [640, 368]]], color=255)
``````

``````masked_edge_img = cv2.bitwise_and(edge_img, mask)
cv2.waitKey(0)
``````

### 霍夫变换

#### 实现

``````import cv2
import numpy as np

img = np.zeros((200, 400))
cv2.line(img, (10, 10), (200， 100)， 255， 3)
cv2.line(img, (30, 50), (350, 10), 255, 2)

cv2.imshow('img', img)
cv2.imwrite('lines.jpg', img)
cv2.waitKey(0)
``````

``````import cv2
import numpy as np

lines = cv2.HoughLinesP(img, 1, np.pi/180, 15, minLineLength=40, maxLineGap=20)
``````
``````print(len(lines))
``````

32

``````def calculate_slope(line):
"""
计算线段line的斜率
:param line: np.array([[x_1, y_1, x_2, y_2]])
:return:
"""
x_1, y_1, x_2, y_2 = line[0]
if x_1 == x_2:
return (y_2-y_1) / (x_2-x_1+1)
else:
return (y_2-y_1) / (x_2-x_1)

# 获取所有线段
lines = cv2.HoughlinesP(edge_img, 1, np.pi/180, 15, minLineLength=40, maxLineGap=20)
# 按照斜率分成车道线
left_lines = [line for line in lines if calculate_slope(line) > 0]
right_lines = [line for line in lines if calculate_slope(line) < 0]
``````

### 离群值过滤

``````def reject_abnormal_lines(lines, threshold):
"""
剔除斜率不一致的线段
:param lines：线段集合，[np.array([[x_1, y_1, x_2, y_2]]), np.array([[x_1, y_1, x_2, y_2]])
"""
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

reject_abnormal_lines(left_lines, threshold=0.2)
reject_abnormal_lines(right_lines, threshold=0.2)
``````

### 最小二乘拟合

#### API

• np.ravel 将高维数组拉成一维
• np.polyfit 多项式拟合
• np.polyval 多项式求值
``````a = np.array([[2, 3], [4, 5]])
print(a)
``````

``````a.ravel()
``````

``````poly = np.polyfit([0, 3, 6, 9], [0, 5, 9, 14], deg=1)	# deg 为几维矩阵
print(poly)
``````

``````x = 0
print(poly[0] * x + poly[1])
np.polyval(poly, 10)	# 上下两种形式一样
``````

#### 实现

``````def least_squares_fit(lines):
"""
将lines中的线段拟合成一条线段
:param lines：线段集合，[np.array([[x_1, y_1, x_2, y_2]]), np.array([[x_1, y_1, x_2, y_2]]),...,np.array([[x_1, y_1, x_2, y_2]])]
:return：线段上的两点，np.array([[xmin, ymin], [xmax, ymax]])
"""

# 1. 去除所有坐标点
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])
# 2. 进行直线拟合，得到多项式系数
poly = np.polyfit(x_coords, y_coords, deg=1)
# 3. 根据多项式系数，计算两个直线上的点，用于唯一确定这条直线
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)

print("left lane")
print(least_squares_fit(left_lines))
print("right lane")
print(least_squares_fit(right_lines))
``````

### 直线绘制

#### API

• 绘制直线：cv2.line
``````img = cv2.imread('img.jpg', cv2.IMREAD_COLOR)
cv2.line(img, tuple(left_line[0], tuple(left_line[1]), color=(0, 255, 255), thickness=5)
cv2.line(img, tuple(right_line[0], tuple(left_line[1]), color=(0, 255, 255), thickness=5)

cv2.imshow('lane', img)
cv2.waitKey(0)
``````

### 视频流读写

#### API

• cv2.VideoCapture

• cv2.VideoWriter

• writer.write

#### 实现

``````def show_lane(color_img):
"""
在 color_img 上画出车道线
:param color_img：彩色图，channels=3
:return:
"""
edge_img = get_edge_img(color_img)
draw_lines(color_img, lines)
return color_img

capture = cv2.VideoCapture('video.mp4')
# capture = cv2.VideoCapture(0)	# 读取当前设备

while True:
ret, frame = capture.read()	# ret 表示当前图像是否关闭，frame 当前帧
frame = show_lane(frame)
cv2.imshow('frame', frame)
cv2.waitKey(10)	# 可以表示当前视频帧率
``````

THE END