基于opencv-python的车道线检测(初级)

运行环境:python3.8, numpy=1.19.5,opencv-python=4.5.1
算法细节解释参考:https://zhuanlan.zhihu.com/p/52623916
代码文件:见文章尾部
效果展示:
在这里插入图片描述

一、文章目录

1、图片读取

2、BGR转灰度图

3、图片边缘提取

4、感兴趣区域选择

5、霍夫变换

6、直线拟合与后处理

7、视频车道线提取

二、实操

注意:后一节代码是在上一节代码的基础上进行修改

1、图片读取

import cv2

img = cv2.imread('whiteCarLaneSwitch.jpg')
cv2.imshow('lane', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

2、BGR转灰度图

import cv2

img = cv2.imread('whiteCarLaneSwitch.jpg')

# BGR转换灰度图,opencv中为BGR格式
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cv2.imshow('lane', gray_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

3、图片边缘提取(横线太多可以选择降低low_threshold值)

import cv2

img = cv2.imread('whiteCarLaneSwitch.jpg')

# BGR转换灰度图,opencv中为BGR格式
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# canny算子进行边缘提取,70没有横线遗留,40有
low_threshold = 40
high_threshold = 150
eager_img = cv2.Canny(gray_img, low_threshold, high_threshold)

cv2.imshow('lane', eager_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

4、感兴趣区域选择
作用:选择一个多边形区域,只保留区域内的线条

import cv2
import numpy as np


# 定义一个感兴趣区域
def region_interest(img, region):
    # 创立一个掩码
    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, region, ignore_mask_color)

    # 进行与操作
    mask_img = cv2.bitwise_and(img, mask)
    return mask_img


if __name__ == '__main__':
    img = cv2.imread('whiteCarLaneSwitch.jpg')
    # BGR转换灰度图,opencv中为BGR格式
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # canny算子进行边缘提取
    low_threshold = 40
    high_threshold = 150
    eager_img = cv2.Canny(gray_img, low_threshold, high_threshold)

    # 感兴趣区域选择
    left_bottom = [0, img.shape[0]]
    right_bottom = [img.shape[1], img.shape[0]]
    apex = [img.shape[1]/2, 310]

    # 一个多边形为2维数组,多个多边形为3维数组
    region = np.array([[left_bottom, right_bottom, apex]], dtype=np.int32)
    print(region)
    mask_img = region_interest(eager_img, region)

    cv2.imshow('lane', mask_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

在这里插入图片描述

5、霍夫变换
作用:能够提取一些直线

import cv2
import numpy as np


# 定义一个感兴趣区域
def region_interest(img, region):
    # 创立一个掩码
    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, region, ignore_mask_color)

    # 进行与操作
    mask_img = cv2.bitwise_and(img, mask)
    return mask_img


if __name__ == '__main__':
    img = cv2.imread('whiteCarLaneSwitch.jpg')
    # BGR转换灰度图,opencv中为BGR格式
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # canny算子进行边缘提取
    low_threshold = 40
    high_threshold = 150
    eager_img = cv2.Canny(gray_img, low_threshold, high_threshold)

    # 感兴趣区域选择
    left_bottom = [0, img.shape[0]]
    right_bottom = [img.shape[1], img.shape[0]]
    apex = [img.shape[1]/2, 310]

    # 一个多边形为2维数组,多个多边形为3维数组
    region = np.array([[left_bottom, right_bottom, apex]], dtype=np.int32)
    print(region)
    mask_img = region_interest(eager_img, region)

    # 霍夫变换->检测直线
    rho = 2  # distance resolution in pixels of the Hough grid
    theta = np.pi/180  # angular resolution in radians of the Hough grid
    threshold = 15     # minimum number of votes (intersections in Hough grid cell)
    min_line_length = 40  # minimum number of pixels making up a line
    max_line_gap = 20    # maximum gap in pixels between connectable line segments

    # Hough Transform 检测线段,线段两个端点的坐标存在lines中
    lines = cv2.HoughLinesP(mask_img, rho, theta, threshold, np.array([]),
                                min_line_length, max_line_gap)

    # 复制一个原图
    img_copy = np.copy(img)
    # 绘制变换后的线
    for line in lines:
        for x1, y1, x2, y2 in line:
            cv2.line(img_copy, (x1, y1), (x2, y2), color=[255, 0, 0], thickness=6)  # 将线段绘制在img上

    # 显示
    cv2.imshow('lane', img_copy)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

在这里插入图片描述

6、直线拟合与后处理

import cv2
import numpy as np


# 定义一个感兴趣区域
def region_interest(img, region):
    # 创立一个掩码
    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, region, ignore_mask_color)

    # 进行与操作
    mask_img = cv2.bitwise_and(img, mask)
    return mask_img


# 计算左右车道线直线方程,计算左右车道线的上下边界
def draw_lines(img, lines, color, thickness):
    left_lines_x = []
    left_lines_y = []
    right_lines_x = []
    right_lines_y = []
    line_y_max = 0
    line_y_min = 999
    for line in lines:
        for x1, y1, x2, y2 in line:
            if y1 > line_y_max:
                line_y_max = y1
            if y2 > line_y_max:
                line_y_max = y2
            if y1 < line_y_min:
                line_y_min = y1
            if y2 < line_y_min:
                line_y_min = y2
            k = (y2 - y1)/(x2 - x1)
            if k < -0.3:
                left_lines_x.append(x1)
                left_lines_y.append(y1)
                left_lines_x.append(x2)
                left_lines_y.append(y2)
            elif k > 0.3:
                right_lines_x.append(x1)
                right_lines_y.append(y1)
                right_lines_x.append(x2)
                right_lines_y.append(y2)
    # 最小二乘直线拟合
    left_line_k, left_line_b = np.polyfit(left_lines_x, left_lines_y, 1)
    right_line_k, right_line_b = np.polyfit(right_lines_x, right_lines_y, 1)

    # 根据直线方程和最大、最小的y值反算对应的x
    cv2.line(img,
             (int((line_y_max - left_line_b)/left_line_k), line_y_max),
             (int((line_y_min - left_line_b)/left_line_k), line_y_min),
             color, thickness)
    cv2.line(img,
             (int((line_y_max - right_line_b)/right_line_k), line_y_max),
             (int((line_y_min - right_line_b)/right_line_k), line_y_min),
             color, thickness)


if __name__ == '__main__':
    img = cv2.imread('whiteCarLaneSwitch.jpg')
    # BGR转换灰度图,opencv中为BGR格式
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # canny算子进行边缘提取,有横线遗留加大low_threshold的值
    low_threshold = 70
    high_threshold = 150
    eager_img = cv2.Canny(gray_img, low_threshold, high_threshold)

    # 感兴趣区域选择
    left_bottom = [0, img.shape[0]]
    right_bottom = [img.shape[1], img.shape[0]]
    apex = [img.shape[1]/2, 310]

    # 一个多边形为2维数组,多个多边形为3维数组
    region = np.array([[left_bottom, right_bottom, apex]], dtype=np.int32)
    mask_img = region_interest(eager_img, region)

    # 霍夫变换->检测直线
    rho = 2  # distance resolution in pixels of the Hough grid
    theta = np.pi/180  # angular resolution in radians of the Hough grid
    threshold = 15     # minimum number of votes (intersections in Hough grid cell)
    min_line_length = 40  # minimum number of pixels making up a line
    max_line_gap = 20    # maximum gap in pixels between connectable line segments

    # Hough Transform 检测线段,线段两个端点的坐标存在lines中
    lines = cv2.HoughLinesP(mask_img, rho, theta, threshold, np.array([]),
                                min_line_length, max_line_gap)

    # 复制一个原图
    img_copy = np.copy(img)
    # 绘制变换后的线(霍夫变换)
    for line in lines:
        for x1, y1, x2, y2 in line:
            cv2.line(img_copy, (x1, y1), (x2, y2), color=[255, 0, 0], thickness=6)  # 将线段绘制在img上

    # 拟合左右车道线方程
    draw_lines(img_copy, lines, color=[255, 0, 0], thickness=6)

    # 显示
    cv2.imshow('lane', img_copy)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

在这里插入图片描述

7、视频车道线提取

import cv2
import numpy as np


# 定义一个感兴趣区域
def region_interest(img, region):
    # 创立一个掩码
    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, region, ignore_mask_color)

    # 进行与操作
    mask_img = cv2.bitwise_and(img, mask)
    return mask_img


# 计算左右车道线直线方程,计算左右车道线的上下边界
def draw_lines(img, lines, color, thickness):
    left_lines_x = []
    left_lines_y = []
    right_lines_x = []
    right_lines_y = []
    line_y_max = 0
    line_y_min = 999
    for line in lines:
        for x1, y1, x2, y2 in line:
            if y1 > line_y_max:
                line_y_max = y1
            if y2 > line_y_max:
                line_y_max = y2
            if y1 < line_y_min:
                line_y_min = y1
            if y2 < line_y_min:
                line_y_min = y2
            k = (y2 - y1)/(x2 - x1)
            if k < -0.3:
                left_lines_x.append(x1)
                left_lines_y.append(y1)
                left_lines_x.append(x2)
                left_lines_y.append(y2)
            elif k > 0.3:
                right_lines_x.append(x1)
                right_lines_y.append(y1)
                right_lines_x.append(x2)
                right_lines_y.append(y2)
    # 最小二乘直线拟合
    left_line_k, left_line_b = np.polyfit(left_lines_x, left_lines_y, 1)
    right_line_k, right_line_b = np.polyfit(right_lines_x, right_lines_y, 1)

    # 根据直线方程和最大、最小的y值反算对应的x
    cv2.line(img,
             (int((line_y_max - left_line_b)/left_line_k), line_y_max),
             (int((line_y_min - left_line_b)/left_line_k), line_y_min),
             color, thickness)
    cv2.line(img,
             (int((line_y_max - right_line_b)/right_line_k), line_y_max),
             (int((line_y_min - right_line_b)/right_line_k), line_y_min),
             color, thickness)


if __name__ == '__main__':

    # 读取视频
    cap = cv2.VideoCapture("solidYellowLeft.mp4")
    ret, frame = cap.read()
    # 结果写入视频
    out = cv2.VideoWriter('output.mp4', cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), 25, (frame.shape[1], frame.shape[0]))

    while ret:
        img = frame

        # BGR转换灰度图,opencv中为BGR格式
        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # canny算子进行边缘提取,"有横线遗留"加大low_threshold的值,40->100
        low_threshold = 100
        high_threshold = 150
        eager_img = cv2.Canny(gray_img, low_threshold, high_threshold)

        # 感兴趣区域选择,报TypeError: expected non-empty vector for x错,将apex的第二个值降低,310->300
        left_bottom = [0, img.shape[0]]
        right_bottom = [img.shape[1], img.shape[0]]
        apex = [img.shape[1]/2, 305]

        # 一个多边形为2维数组,多个多边形为3维数组
        region = np.array([[left_bottom, right_bottom, apex]], dtype=np.int32)
        mask_img = region_interest(eager_img, region)

        # 霍夫变换->检测直线
        rho = 2  # distance resolution in pixels of the Hough grid
        theta = np.pi/180  # angular resolution in radians of the Hough grid
        threshold = 15     # minimum number of votes (intersections in Hough grid cell)
        min_line_length = 40  # minimum number of pixels making up a line
        max_line_gap = 20    # maximum gap in pixels between connectable line segments

        # Hough Transform 检测线段,线段两个端点的坐标存在lines中
        lines = cv2.HoughLinesP(mask_img, rho, theta, threshold, np.array([]),
                                    min_line_length, max_line_gap)

        # 复制一个原图
        img_copy = np.copy(img)
        # 绘制变换后的线(霍夫变换)
        for line in lines:
            for x1, y1, x2, y2 in line:
                cv2.line(img_copy, (x1, y1), (x2, y2), color=[255, 0, 0], thickness=6)  # 将线段绘制在img上

        # 拟合左右车道线方程
        draw_lines(img_copy, lines, color=[255, 0, 0], thickness=6)

        # 显示
        cv2.imshow('lane', img_copy)
        cv2.waitKey(1)

        # 将处理后的帧数写到视频
        out.write(img_copy)

        # 播放结束跳出循环
        ret, frame = cap.read()

    cap.release()
    cv2.destroyAllWindows()

异常:np.polyfit(right_lines_x, right_lines_y, 1),TypeError: expected non-empty vector for x
解决:感兴趣区域范围改变,降低apex第二个值(如310->305)

总结:
处理速度较快,但是只适合直线处理,并且对图片明亮度有一定要求;

以上代码
链接:https://pan.baidu.com/s/1uaoMebL2dBrKyTLsnGcdnQ
提取码:vxn1

资料参考:https://zhuanlan.zhihu.com/p/52623916

猜你喜欢

转载自blog.csdn.net/weixin_45679938/article/details/118968694