车道线分割检测

利用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()

你可能感兴趣的:(计算机视觉(车道线分割),计算机视觉,python,opencv)