【车道线检测】一、传统车道线检测:基于霍夫变换的车道线检测史诗级详细教 ...

打印 上一主题 下一主题

主题 834|帖子 834|积分 2502

1、定义图像显示函数

        起首定义一个函数,函数的作用是通过plt库显示两幅图,为后续实验做准备。该函数的主要功能是:

  • 从指定路径加载图像
  • 显示图像的基本信息
  • 将图像从BGR格式转换为RGB格式
  • 并在一个图形窗口中显示两幅图像进行对比
  1. import numpy as np
  2. import cv2
  3. import matplotlib.pyplot as plt
  4. import pandas as pd
  5. import time
  6. import warnings
  7. warnings.filterwarnings("ignore")
  8. def load_image(path):
  9.     """
  10.     从指定路径加载图像并返回。
  11.     :param path: 图像文件的路径
  12.     :return: 加载的图像
  13.     """
  14.     img = cv2.imread(path)
  15.     if img is None:
  16.         raise FileNotFoundError(f"Image not found at {path}")
  17.     return img
  18. def display_image_info(img, title="Image Info"):
  19.     """
  20.     打印图像的基本信息。
  21.     :param img: 图像数据
  22.     :param title: 打印信息的标题
  23.     """
  24.     print(f"{title}: Shape = {img.shape}")
  25. def convert_to_rgb(img):
  26.     """
  27.     将图像从 BGR 格式转换为 RGB 格式。
  28.     :param img: 输入图像
  29.     :return: 转换后的图像
  30.     """
  31.     return cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  32. def compare_images(img1, img2, titles=("Original Image", "Processed Image"), figsize=(16, 5)):
  33.     """
  34.     在一个图形窗口中显示两幅图像进行对比。
  35.     :param img1: 第一幅图像
  36.     :param img2: 第二幅图像
  37.     :param titles: 两幅图像的标题
  38.     :param figsize: 图形窗口的大小
  39.     """
  40.     fig, axes = plt.subplots(1, 2, figsize=figsize)
  41.     fig.tight_layout()
  42.     # 显示第一幅图像
  43.     axes[0].imshow(convert_to_rgb(img1))
  44.     axes[0].set_title(titles[0])
  45.     axes[0].axis('off')
  46.     # 显示第二幅图像
  47.     axes[1].imshow(convert_to_rgb(img2))
  48.     axes[1].set_title(titles[1])
  49.     axes[1].axis('off')
  50.     plt.show()
  51. # 主程序
  52. if __name__ == "__main__":
  53.     # 加载图像
  54.     image_path = 'dataset/test3.jpg'
  55.     img = load_image(image_path)
  56.    
  57.     # 显示图像信息
  58.     display_image_info(img)
  59.    
  60.     # 对比显示原始图像和处理后的图像
  61.     compare_images(img, img, titles=("Original Image", "RGB Image"))
复制代码


  • 运行效果如下:(原图和RGB图)

2、将图像转换为灰度图



  • 将图像转换为灰度图:
  1. #将图像转换为灰度图
  2. def convert_to_gray(img):
  3.     temp = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
  4.     return cv2.cvtColor(temp,cv2.COLOR_BGR2RGB)
  5. gray_img = convert_to_gray(img)
  6. #对比显示原始图像和灰度图像
  7. compare_images(img, gray_img, titles=("Original Image", "Grayscale Image"))
复制代码


  • 运行效果如下:(原图和灰度图)


3、高斯模糊

               高斯模糊是一种图像处置处罚效果,通常用于减少图像噪声以及降低细节层次。它是一种平滑和淡化图像部门的技术,通常用于减少噪声或为图像的前景添加焦点。高斯模糊如下:
   
  1. #高斯模糊
  2. def gaussian_blur(image, kernel_size):
  3.     """
  4.     对图像进行高斯模糊处理。
  5.     :param image: 输入图像
  6.     :param kernel_size: 高斯核的大小,必须是奇数
  7.     :return: 高斯模糊后的图像
  8.     """
  9.     if kernel_size % 2 == 0:
  10.         raise ValueError("Kernel size must be an odd number.")
  11.     return cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
  12. # 对灰度图像进行高斯模糊处理
  13. gauss_img = gaussian_blur(gray_img, 5)
  14. # 对比显示灰度图像和高斯模糊后的图像
  15. compare_images(gray_img, gauss_img, titles=("Grayscale Image", "Gaussian Blurred Image"))
复制代码


  • 运行效果如下:(灰度图和高斯处置处罚效果图)


4、边沿检测

               边沿检测是图形图像处置处罚、盘算机视觉和呆板视觉中的一个基本工具,通常用于特征提取和特征检测,旨在检测一张数字图像中有明显变化的边沿大概不一连的区域,在一维空间中,类似的操纵被称作步长检测(step detection)。边沿是一幅图像中不同屈原之间的边界限,通常一个边沿图像是一个二值图像。边沿检测的目标是捕捉亮度急剧变化的区域,而这些区域通常是我们关注的。
    4.1 灰度图像直方图



  • 绘制灰度图像的像素值分布直方图:
  1. #灰度图直方图
  2. plt.hist(gauss_img.ravel(),256,[0,256])
  3. plt.title('hist of gray pixel')
  4. plt.show()
复制代码


  •  运行效果如下图:

从图像的灰度图像素值分布直方图可以看出像素值主要分布在110到140之间。 
4.2 边沿检测

        图像边沿检测,从上述的图像灰度图的像素值的分布值中我们知道像素值的分布峰值在110-140之间,故canny(gauss_img,110,140),边沿检测如下:
  1. #边缘检测
  2. def canny(image,low_threshold,high_threshold):
  3.     """
  4.     功能: 使用Canny算法进行边缘检测。
  5.     参数:
  6.         image: 输入图像,通常应该是灰度图像。
  7.         low_threshold: 低阈值,用于检测弱边缘。
  8.         high_threshold: 高阈值,用于检测强边缘。
  9.     返回: 边缘检测后的图像。
  10.     """
  11.     return cv2.Canny(image,low_threshold,high_threshold)
  12. cannyd_img = canny(gauss_img,110,140)
  13. compare_images(gauss_img,cannyd_img, titles=("Grayscale Image", "Canny Image"))
复制代码


  • 运行效果如下:

5、提取感爱好区域(ROI)

5.1 原图提取ROI



  • 代码先容:
        下列代码实现了一个功能,该功能可以基于给定的多边形顶点来提取图像中的特定区域。这个过程包括创建一个与输入图像相同大小的掩模,然后在这个掩模上添补多边形区域,并使用这个掩模与原始图像进行按位与操纵,从而只保留多边形区域内的图像部门。


  • 封装了一个region_of_interest 函数:

    • 创建一个与输入图像相同大小的黑色掩模。
    • 根据图像的通道数确定掩模的颜色。
    • 添补多边形区域为白色,以创建掩模。
    • 将原图像与掩模进行按位与操纵,以保留爱好区域。



  • ROI区域实现如下:
  1. #生成mask掩膜,提取ROI
  2. #ROI即感兴趣区域,英文:Region of interest
  3. def region_of_interest(image: np.ndarray, vertices: np.ndarray) -> np.ndarray:
  4.     """
  5.     应用图像掩模以保留由顶点定义的兴趣区域。
  6.    
  7.     参数:
  8.     :param image: 输入图像(numpy数组)。
  9.     :param vertices: 定义兴趣区域的多边形顶点数组。
  10.     :return: 应用了掩模的图像。
  11.     """
  12.     # 创建与输入图像相同大小的黑色掩模
  13.     mask = np.zeros_like(image)
  14.    
  15.     # 根据图像的通道数确定掩模的颜色
  16.     ignore_mask_color = (255,) * image.shape[2] if len(image.shape) > 2 else 255
  17.    
  18.     # 填充多边形区域为白色,以创建掩模
  19.     cv2.fillPoly(mask, vertices, ignore_mask_color)
  20.    
  21.     # 将原图像与掩模进行按位与操作,以保留兴趣区域
  22.     masked_image = cv2.bitwise_and(image, mask)
  23.    
  24.     return masked_image
  25. # 获取图像尺寸
  26. imshape = img.shape
  27. # 设置掩膜区域
  28. temp_v = np.array([[(0, 350), (350, 225), (530, 245), (800, 355), (950, 1000), (0, 700)]], dtype=np.int32)
  29. # 调用函数裁剪图像
  30. crop_img = region_of_interest(img, temp_v)
  31. # 显示对比图
  32. compare_images(img,crop_img,titles=("Grayscale Image", "ROI Image"))
复制代码


  •  运行效果如下:

5.2 边沿检测后提取ROI



  • 对边沿检测后的图像进行提取ROI,如下:
  1. crop_mask = region_of_interest(cannyd_img,temp_v)
  2. compare_images(cannyd_img,crop_mask,titles=("Canny Image", "ROI Canny Image"))
复制代码


  • 运行效果如下:


6、基于霍夫变换检测直线

6.1 什么是霍夫变换?



  • 定义:
               霍夫变换(Hough Transform)是图像处置处罚中的一种特征提取技术,它通过一种投票算法检测具有特定外形的物体。Hough变换是图像处置处罚中从图像中辨认几何外形的基本方法之一。Hough变换的基本原理在于使用点与线的对偶性,将原始图像空间的给定的曲线通过曲线表达情势变为参数空间的一个点。如许就把原始图像中给定曲线的检测问题转化为寻找参数空间中的峰值问题。也即把检测整体特性转化为检测局部特性。比如直线、椭圆、圆、弧线等。
                    原则上霍夫变换可以检测任何外形,但复杂的外形需要的参数就多,霍夫空间的维数就多,因此在程序实现上所需的内存空间以及运行服从上都不利于把标准霍夫变换应用于实际复杂图形的检测中。霍夫梯度法是霍夫变换的改进(圆检测),它的目标是减小霍夫空间的维度,提高服从。
     
     直线检测原理:将要检测的对象转到霍夫空间中,使用累加器找到最优解,即为所求直线。
     (注:检测前要对图像二值化处置处罚)
     6.2 霍夫变换实现

                   下列代码定义了两个函数 hough_lines 和 lsd_lines,分别用于使用霍夫变换和LSD算法检测图像中的直线,并使用了一个装饰器 timed_function 来测量这两个函数的实行时间。


  • 实现如下:
  1. def timed_function(func):
  2.     """装饰器,用于测量函数执行时间"""
  3.     def wrapper(*args, **kwargs):
  4.         start = time.time()
  5.         result = func(*args, **kwargs)
  6.         end = time.time() - start
  7.         print(f"{func.__name__} took {end:.4f}s")
  8.         return result
  9.     return wrapper
  10. @timed_function
  11. def hough_lines(img: np.ndarray, rho: float, theta: float, threshold: int, min_line_length: int, max_line_gap: int) -> np.ndarray:
  12.     """
  13.     使用霍夫变换检测图像中的直线。
  14.    
  15.     参数:
  16.     :param img: 输入图像(边缘检测后的二值图像)。
  17.     :param rho: 线段以像素为单位的距离精度。
  18.     :param theta: 像素以弧度为单位的角度精度。
  19.     :param threshold: 霍夫平面累加的阈值。
  20.     :param min_line_length: 线段最小长度(像素级)。
  21.     :param max_line_gap: 最大允许断裂长度。
  22.     :return: 检测到的直线数组。
  23.     """
  24.     lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), min_line_length, max_line_gap)
  25.     return lines
  26. # 参数设置
  27. rho = 1
  28. theta = np.pi / 180
  29. hof_threshold = 20
  30. min_line_len = 30
  31. max_line_gap = 100
  32. # 调用函数
  33. lines_hough = hough_lines(crop_mask, rho, theta, hof_threshold, min_line_len, max_line_gap)
复制代码


  • 上述参数先容:

    • rho: 线段以像素为单元的距离精度。
    • theta: 像素以弧度为单元的角度精度。
    • hof_threshold: 霍夫平面累加的阈值。
    • min_line_len: 线段最小长度(像素级)。
    • max_line_gap: 最大允许断裂长度。

  • 代码运行效果如下:


7、车道线检测并盘算斜率



  • 在图像上绘制检测到的车道线,并显示每条线的斜率:
  1. #车道线斜率显示
  2. def draw_lines(image: np.ndarray, lines: np.ndarray) -> np.ndarray:
  3.     """
  4.     在图像上绘制检测到的车道线,并显示每条线的斜率。
  5.    
  6.     参数:
  7.     :param image: 输入图像。
  8.     :param lines: 检测到的直线数组。
  9.     :return: 绘制了车道线的图像。
  10.     """
  11.     for line in lines:
  12.         for x1, y1, x2, y2 in line:
  13.             # 计算直线的斜率
  14.             fit = np.polyfit((x1, x2), (y1, y2), deg=1)
  15.             slope = fit[0]
  16.             slope_str = f"{slope:.2f}"  # 格式化斜率字符串
  17.             
  18.             # 绘制直线
  19.             cv2.line(image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 10)
  20.             
  21.             # 在终点附近绘制斜率文本
  22.             cv2.putText(image, slope_str, (int(x2), int(y2)), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 255, 255), 2)
  23.    
  24.     return image
  25. # 调用函数
  26. lined_image = draw_lines(img.copy(),lines_hough)
  27. # 显示对比图
  28. compare_images(img, lined_image,titles=("Original Image", "Lane line slope Image"))
复制代码


  • 运行效果如下:


8、盘算车道线均匀斜率和截距



  • 盘算了左右车道线和中央线的均匀斜率和截距:
  1. def draw_lines_detection(image: np.ndarray, lines: np.ndarray) -> tuple:
  2.     """
  3.     在图像上绘制检测到的车道线,并计算左右车道线和中心线的平均斜率和截距。
  4.    
  5.     参数:
  6.     :param image: 输入图像。
  7.     :param lines: 检测到的直线数组。
  8.     :return: 绘制了车道线的图像以及左右车道线和中心线的平均斜率和截距。
  9.     """
  10.     middle_x = image.shape[1] // 2  # 图像的中点
  11.     left_slopes = []
  12.     right_slopes = []
  13.     center_slopes = []
  14.     left_biases = []
  15.     right_biases = []
  16.     center_biases = []
  17.     for line in lines:
  18.         for x1, y1, x2, y2 in line:
  19.             fit = np.polyfit((x1, x2), (y1, y2), deg=1)
  20.             slope = fit[0]
  21.             bias = fit[1]
  22.             if -0.41 < slope < -0.30:  # 左边线
  23.                 cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 10)
  24.                 left_slopes.append(slope)
  25.                 left_biases.append(bias)
  26.             elif 0.38 < slope < 0.42:  # 右边线
  27.                 cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 10)
  28.                 right_slopes.append(slope)
  29.                 right_biases.append(bias)
  30.             elif slope > 1:  # 中心线
  31.                 cv2.line(image, (x1, y1), (x2, y2), (255, 0, 0), 10)
  32.                 center_slopes.append(slope)
  33.                 center_biases.append(bias)
  34.     # 计算平均值
  35.     left_slope = np.mean(left_slopes) if left_slopes else 0
  36.     left_bias = np.mean(left_biases) if left_biases else 0
  37.     right_slope = np.mean(right_slopes) if right_slopes else 0
  38.     right_bias = np.mean(right_biases) if right_biases else 0
  39.     center_slope = np.mean(center_slopes) if center_slopes else 0
  40.     center_bias = np.mean(center_biases) if center_biases else 0
  41.     return image, left_slope, left_bias, right_slope, right_bias, center_slope, center_bias
  42. # 调用函数
  43. lined_image, left_slope, left_bias, right_slope, right_bias, center_slope, center_bias = draw_lines_detection(img.copy(), lines_hough)
  44. # 显示对比图
  45. compare_images(img, lined_image,titles=("Original Image", "Lane markings detection Image"))
复制代码


  • 运行效果如下:


 上述代码先容:


  • 功能: 在图像上绘制检测到的车道线,并盘算左右车道线和中央线的均匀斜率和截距。
  • 参数:

    • image: 输入图像。
    • lines: 检测到的直线数组。

  • 返回: 绘制了车道线的图像以及左右车道线和中央线的均匀斜率和截距。
  • 步调:

    • 盘算图像的中点。
    • 初始化存储斜率和截距的列表。
    • 遍历每条直线,盘算斜率和截距。
    • 根据斜率范围判定直线属于左边线、右边线还是中央线,并分别绘制不同颜色的直线。
    • 将斜率和截距添加到相应的列表中。
    • 盘算每个类别的均匀斜率和截距。
    • 返回绘制了车道线的图像和盘算结果。

9、绘制每一条车道线



  • 在图像上绘制左右车道线和中央线:
  1. def draw_all_line(img, slope, bias, color, y2=230):
  2.     # 计算起点
  3.     x1 = 0 if slope == 'left' else img.shape[1] if slope == 'right' else int((img.shape[0] - bias) / slope)
  4.     y1 = int(slope * x1 + bias) if isinstance(slope, (int, float)) else img.shape[0]
  5.    
  6.     # 计算终点
  7.     x2 = int((y2 - bias) / slope) if isinstance(slope, (int, float)) else x1
  8.    
  9.     # 绘制线条
  10.     line_img = cv2.line(img.copy(), (x1, y1), (x2, y2), color, 10)
  11.    
  12.     return line_img
  13. # 左边线
  14. left_lines = draw_all_line(img, left_slope, left_bias, (0, 255, 0))
  15. compare_images(img, left_lines,titles=("Original Image", "Left Lane Image"))
  16. # 右边线
  17. right_lines = draw_all_line(img, right_slope, right_bias, (0, 0, 255))
  18. compare_images(img, right_lines,titles=("Original Image", "Right Lane Image"))
  19. # 中线
  20. center_lines = draw_all_line(img, center_slope, center_bias, (255, 0, 0))
复制代码


  • 运行效果如下:



10、图像融合--绘制最终的车道线



  • 在一个空缺图像上绘制左右车道线和中央线,并将这些线条与原图像进行融合:
  1. import numpy as np
  2. import cv2
  3. def draw_line(blank, slope, bias, color, y2=230):
  4.     # 计算起点
  5.     if slope == 'left':
  6.         x1 = 0
  7.         y1 = int(slope * x1 + bias)
  8.     elif slope == 'right':
  9.         x1 = blank.shape[1]
  10.         y1 = int(slope * x1 + bias)
  11.     else:
  12.         y1 = blank.shape[0]
  13.         x1 = int((y1 - bias) / slope)
  14.    
  15.     # 计算终点
  16.     x2 = int((y2 - bias) / slope) if slope != 'left' and slope != 'right' else x1
  17.    
  18.     # 绘制线条
  19.     cv2.line(blank, (x1, y1), (x2, y2), color, 20)
  20. def draw_lines_and_fuse(img, left_slope, left_bias, right_slope, right_bias, center_slope, center_bias):
  21.     # 创建空白图像
  22.     blank = np.zeros_like(img)
  23.    
  24.     # 绘制左车道线
  25.     draw_line(blank, left_slope, left_bias, (0, 255, 0))
  26.    
  27.     # 绘制右车道线
  28.     draw_line(blank, right_slope, right_bias, (0, 0, 255))
  29.    
  30.     # 绘制中线
  31.     draw_line(blank, center_slope, center_bias, (255, 0, 0))
  32.    
  33.     # 图像融合
  34.     fusion = cv2.addWeighted(img, 0.8, blank, 1, 0)
  35.    
  36.     return fusion
  37. # 示例调用
  38. fusion = draw_lines_and_fuse(img, left_slope, left_bias, right_slope, right_bias, center_slope, center_bias)
  39. compare_images(img, fusion,titles=("Original Image", "Image blending--Lane markings detection results"))
复制代码


  • 运行效果如下:












免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

玛卡巴卡的卡巴卡玛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表