利用opencv,使用边缘检测、全局变化梯度阈值过滤、算子角度过滤、HLS阈值过滤的方法进行车道线分割检测,综合多种阈值过滤进行检测提高检测精度。
1.利用cv2.Sobel()计算图像梯度(边缘检测)
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
src = np.float32([[200, 720], [1100, 720], [595, 450], [685, 450]]) #src 输入图像
dst = np.float32([[300, 720], [980, 720], [300, 0], [980, 0]]) #dst 输出图像
m_inv = cv2.getPerspectiveTransform(dst, src)
m = cv2.getPerspectiveTransform(src, dst)
# 利用cv2.Sobel()计算图像梯度(边缘检测)
def abs_sobel_threshold(img, orient='x', thresh_min=40, thresh_max=255):
###利用X,y方向上sobel,二值化图像######
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
if orient == 'x':
abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 1, 0))
if orient == 'y':
########参考求x方向的sobel算子,计算y方向上sobel算子#######
#############填空1 (1行代码)########################
abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 0, 1))
#############填空1 (1行代码)########################
scaled_sobel = np.uint8(255 * abs_sobel / np.max(abs_sobel))
binary_output = np.zeros_like(scaled_sobel)
#############二值图像,大于最小阈值并且小于最大阈值的区间置为255, 其余为0,可通过修改最大最小值查看差异######
##############填空2(1行代码)########################
binary_output[(scaled_sobel >= thresh_min) & (scaled_sobel <= thresh_max)] = 255
#############填空2 (1行代码)########################
return binary_output
使用检测纵向边缘(x方向的梯度)
path = r"d:\Users\WYN\Desktop\temp\week1HomeWork\testImage"
path_list = os.listdir(path)
plt.figure(figsize=(16, 9))
for i in range(len(path_list)):
path_now = "\\".join([str(path), str(path_list[i])])
img = cv2.imread(path_now)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.subplot(2, 3, i + 1)
plt.imshow(abs_sobel_threshold(img, orient='x', thresh_min=40, thresh_max=255))
plt.xticks([])
plt.yticks([])
plt.subplots_adjust(wspace=0, hspace=0)
plt.show()
使用检测纵向边缘(y方向的梯度)
# 检测纵向边缘(y方向的梯度)
plt.figure(figsize=(16, 9))
for i in range(len(path_list)):
path_now = "\\".join([str(path), str(path_list[i])])
img = cv2.imread(path_now)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.subplot(2, 3, i + 1)
plt.imshow(abs_sobel_threshold(img, orient='y', thresh_min=40, thresh_max=255))
plt.xticks([])
plt.yticks([])
plt.subplots_adjust(wspace=0, hspace=0)
plt.show()
2.使用全局的变化梯度来进行阈值过滤
def mag_threshold(img, sobel_kernel=3, mag_threshold=(50, 255)):
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)
########根据x方向的sobel算子和y方向上sobel算子,计算梯度,公式为sqrt(x^2 + y ^2)#######
#############填空3 (1行代码)########################
gradmag = np.sqrt(sobelx ** 2 + sobely ** 2)
#############填空3 (1行代码)########################
scale_factor = np.max(gradmag) / 255
gradmag = (gradmag / scale_factor).astype(np.uint8)
binary_out = np.zeros_like(gradmag)
########转换为二值图,最大最小值可调,kernel_size也可以调整看看差异#######
#############填空4 (1行代码)########################
binary_out[(gradmag >= mag_threshold[0]) & (gradmag <= mag_threshold[1])] = 255
#############填空4 (1行代码)########################
return binary_out
通过全局阈值过滤来检测车道线
path = r"d:\Users\WYN\Desktop\temp\week1HomeWork\testImage"
path_list = os.listdir(path)
plt.figure(figsize=(16, 9))
plt.figure(1)
for i in range(len(path_list)):
path_now = "\\".join([str(path), str(path_list[i])])
img = cv2.imread(path_now)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.subplot(2, 3, i + 1)
plt.imshow(mag_threshold(img, sobel_kernel=3, mag_threshold=(50, 255)))
plt.xticks([])
plt.yticks([])
plt.subplots_adjust(wspace=0, hspace=0)
plt.show()
3.通过算子角度来进行阈值过滤
def dir_threshold(img, sobel_kernel=5, thresh=(0.7, 1.3)):
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)
########根据x方向的sobel算子和y方向上sobel算子,计算角度,公式为arctan(y/x),将倾斜角度过大的过滤掉#######
#############填空5 (1行代码)########################
absgraddir = np.arctan(sobely / sobelx)
#############填空5 (1行代码)########################
binary_output = np.zeros_like(absgraddir)
########转换为二值图,最大最小值可调,kernel_size也可以调整看看差异#######
#############填空6 (1行代码)########################
binary_output[((absgraddir >= thresh[0]) & (absgraddir <= thresh[1]))] = 255
#############填空6 (1行代码)########################
return binary_output
通过全局阈值过滤来检测车道线
path = r"d:\Users\WYN\Desktop\temp\week1HomeWork\testImage"
path_list = os.listdir(path)
plt.figure(figsize=(16, 9))
plt.figure(1)
for i in range(len(path_list)):
path_now = "\\".join([str(path), str(path_list[i])])
img = cv2.imread(path_now)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.subplot(2, 3, i + 1)
plt.imshow(dir_threshold(img, sobel_kernel=5, thresh=(np.pi/4, np.pi/3)))
plt.xticks([])
plt.yticks([])
plt.subplots_adjust(wspace=0, hspace=0)
plt.show()
4.使用HLS进行阈值过滤
def hls_thresh(img, thresh=(100, 255)):
hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)
########分离出s通道s_channel#######
#############填空7 (1行代码)########################
_, _, s_channel = cv2.split(hls)
#############填空7 (1行代码)########################
binary_output = np.zeros_like(s_channel)
########转换为二值图,最大最小值可调#####################
#############填空8 (1行代码)########################
binary_output[(s_channel > thresh[0]) & (s_channel < thresh[1])] = 255
#############填空8 (1行代码)########################
return binary_output
5.综合多种阈值过滤方法
def combined_threshold(img):
abs_bin = abs_sobel_threshold(img, orient='x', thresh_min=50, thresh_max=255)
mag_bin = mag_threshold(img, sobel_kernel=3, mag_threshold=(50, 255))
dir_bin = dir_threshold(img, sobel_kernel=15, thresh=(0.7, 1.3))
hls_bin = hls_thresh(img, thresh=(170, 255))
combined = np.zeros_like(dir_bin)
#############组合四个阈值结果,判定车道线,##########
#########例如(abs_bin == 255 | ((mag_bin == 255) & (dir_bin == 255))) | hls_bin == 25)#
##########可以尝试不同的组合######################
#############填空9(1行代码)########################
# combined[(abs_bin == 255 | ((mag_bin == 255) & (dir_bin == 255))) | hls_bin == 255] = 255
combined[(abs_bin == 255) & (mag_bin == 255) & (dir_bin == 255) | (hls_bin == 255)] = 255
#############填空9 (1行代码)########################
return combined, abs_bin, mag_bin, dir_bin, hls_bin
6.滑动窗口扫描
def line_fit_and_draw_line(binary_warped):
# "查找拟合直线"
# 对图像对下半部分查找直方图
#############填空10(1行代码)截取图像高度的下方1/2处########################
histogram = np.sum(binary_warped[int(binary_warped.shape[0]//2):, :], axis=0)
#############填空10(1行代码)截取图像高度的下方1/2处########################
out_img = (np.dstack((binary_warped, binary_warped, binary_warped)) * 255).astype('uint8')
#查找直方图中左右两侧对峰值
midpoint = np.int(histogram.shape[0] / 2)
#左侧从100到 midpoint的最大值,转换成图像坐标还要加上100哦~############
#右侧从midpoint到图像宽度减100的最大值,转换成图像坐标还要加上midpoint哦~############
####也就是图像左右边缘100像素内不查找车道线##################
#############填空11(2行代码)查找左侧右侧最大值基本点########################
leftx_base = np.argmax(histogram[100:midpoint])+100
rightx_base = np.argmax(histogram[midpoint:binary_warped.shape[1]-100])+midpoint
#############填空11(2行代码)查找左侧右侧最大值基本点########################
##########以下是关于滑动窗口查找车道线的代码#####################
nwindows = 9 # 将窗口划分为9行
window_height = np.int(binary_warped.shape[0] / nwindows)
nonzero = binary_warped.nonzero()
nonzeroy = np.array(nonzero[0]) # 非零元素所在行
nonzerox = np.array(nonzero[1]) # 非零元素所在列
leftx_current = leftx_base
rightx_current = rightx_base
margin = 100
minpix = 10
left_lane_inds = []
right_lane_inds = []
for window in range(nwindows):
win_y_low = binary_warped.shape[0] - (window + 1) * window_height
win_y_high = binary_warped.shape[0] - window * window_height
win_xleft_low = leftx_current - margin
win_xleft_high = leftx_current + margin
win_xright_low = rightx_current - margin
win_xright_high = rightx_current + margin
good_left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox > win_xleft_low) & (nonzerox < win_xleft_high)).nonzero()[0]
good_right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox >= win_xright_low) & (nonzerox < win_xright_high)).nonzero()[0]
left_lane_inds.append(good_left_inds)
right_lane_inds.append(good_right_inds)
if len(good_left_inds) > minpix:
leftx_current = np.int(np.mean(nonzerox[good_left_inds]))
if len(good_right_inds) > minpix:
rightx_current = np.int(np.mean(nonzerox[good_right_inds]))
left_lane_inds = np.concatenate(left_lane_inds)
right_lane_inds = np.concatenate(right_lane_inds)
leftx = nonzerox[left_lane_inds]
lefty = nonzeroy[left_lane_inds]
rightx = nonzerox[right_lane_inds]
righty = nonzeroy[right_lane_inds]
##########以上是关于滑动窗口查找车道线的代码#####################
#将左侧,右侧车道线3次拟合,用函数np.polyfit##########
#############填空12(2行代码)左侧、右侧车道线拟合#######################
para_l = np.polyfit(lefty, leftx, 3) #得到曲线参数
para_r = np.polyfit(righty, rightx, 3)
#############填空12(2行代码)左侧、右侧车道线拟合#######################
################在图上画出拟合的线########################
ploty = np.linspace(0, undist.shape[0]-1, undist.shape[0])
#########对y进行拟合,x = a * y ^ 2 + b * y + C
#############填空13(2行代码)左侧、右侧车道线方程坐标#######################
left_fitx = para_l[0] * ploty**3 + para_l[1] * ploty**2 + para_l[2] * ploty + para_l[3]
right_fitx = para_r[0] * ploty**3 + para_r[1] * ploty**2 + para_r[2] * ploty + para_r[3]
#############填空13(2行代码)左侧、右侧车道线方程坐标#######################
######生成一张黑图,做mask,将车道线区域标注出来##########
color_warp = np.zeros((720, 1280, 3), dtype='uint8')
pts_left = np.array([np.transpose(np.vstack([left_fitx, ploty]))])
pts_right = np.array([np.flipud(np.transpose(np.vstack([right_fitx, ploty])))])
pts = np.hstack((pts_left, pts_right))
# 在透射变换后的图上画出车道线
cv2.fillPoly(color_warp, np.int_([pts]), (0, 255, 0))
# 将画出的车道线的图,逆变换到原来的图上,将color_warp逆变换为newwarp
#############填空14(1行代码)#######################
newwarp = cv2.warpPerspective(color_warp, m_inv, imgOut_size, flags=cv2.INTER_LINEAR)
#############填空14(1行代码)#######################
# 将原来的图和标注好车道线的图叠加,用cv2.addWeighted,可画成半透明,最终图为result
#############填空15(1行代码)#######################
result = cv2.addWeighted(img, 0.5, newwarp, 0.5, 0)
#############填空15(1行代码)#######################
plt.figure(figsize = (30, 30))
plt.title('lane')
plt.subplot(1, 1, 1)
plt.imshow(result)
plt.axis('off')
7.图像显示
img = cv2.imread("./testImage/test6.jpg")
out1 = mag_threshold(img)
out2 = abs_sobel_threshold(img)
out3 = dir_threshold(img)
out4 = hls_thresh(img)
imgOut, abs_bin, mab_bin, dir_bin, hls_bin = combined_threshold(img)
plt.figure(figsize = (30, 30))
plt.title('calibration')
plt.subplot(1, 5, 1)
plt.imshow(out1)
plt.title("mag_threshold")
plt.subplot(1, 5, 2)
plt.imshow(out2)
plt.title("abs_sobel_threshold")
plt.subplot(1, 5, 3)
plt.imshow(out3)
plt.title("dir_threshold")
plt.subplot(1, 5, 4)
plt.imshow(out4)
plt.title("hls_thresh")
plt.subplot(1, 5, 5)
plt.imshow(imgOut)
plt.title("combined_threshold")
plt.axis('off')
imgOut_size = (imgOut.shape[1], imgOut.shape[0])
binary_warped = cv2.warpPerspective(imgOut, m, imgOut_size, flags=cv2.INTER_LINEAR)
undist = cv2.imread("./testImage/test6.jpg")
line_fit_and_draw_line(binary_warped)
plt.show()
8.创建空白画布,并绘制指定点
import numpy as np
import cv2
import matplotlib.pyplot as plt
###在图中把点标记出来
plt.figure(figsize=(30, 30))
img = np.zeros((1000,1000,3),dtype=np.uint8)
point_list = [(200, 720), (1100, 720), (595, 450), (685, 450)] # src
point_list2 =[(300, 720), (980, 720), (300, 0), (980, 0)] # dst
for point in point_list:
cv2.circle(img, point, 10, (255, 0, 0), 3)
for point in point_list2:
cv2.circle(img, point, 10, (0, 0, 255), 3)
plt.imshow(img)
plt.show()