飞道的博客

OpenCV实战案例——车道线识别

432人阅读  评论(0)

目录

一、首先进行canny边缘检测,为获取车道线边缘做准备

二、进行ROI提取获取确切的车道线边缘(红色线内部)

三、利用概率霍夫变换获取直线,并将斜率正数和复数的线段给分割开来

四、离群值过滤,剔除斜率相差过大的线段

五、最小二乘拟合,实现将左边和右边的线段互相拟合成一条直线,形成车道线

六、绘制线段

全部代码(视频显示)


一、首先进行canny边缘检测,为获取车道线边缘做准备


  
  1. import cv2
  2. gray_img = cv2.imread( 'img.jpg',cv2.IMREAD_GRAYSCALE)
  3. canny_img = cv2.Canny(gray_img, 50, 100)
  4. cv2.imwrite( 'canny_img.jpg',canny_img)
  5. cv2.imshow( 'canny',canny_img)
  6. cv2.waitKey( 0)

二、进行ROI提取获取确切的车道线边缘(红色线内部)

方法:在图像中,黑色表示0,白色为1,那么要保留矩形内的白色线,就使用逻辑与,当然前提是图像矩形外也是0,那么就采用创建一个全0图像,然后在矩形内全1,之后与之前的canny图像进行与操作,即可得到需要的车道线边缘。


  
  1. import cv2
  2. import numpy as np
  3. canny_img = cv2.imread( 'canny_img.jpg',cv2.IMREAD_GRAYSCALE)
  4. roi = np.zeros_like(canny_img)
  5. roi = cv2.fillPoly(roi,np.array( [[[0, 368],[300, 210], [340, 210], [640, 368]]]),color= 255)
  6. roi_img = cv2.bitwise_and(canny_img, roi)
  7. cv2.imwrite( 'roi_img.jpg',roi_img)
  8. cv2.imshow( 'roi_img',roi_img)
  9. cv2.waitKey( 0)

三、利用概率霍夫变换获取直线,并将斜率正数和复数的线段给分割开来

TIPs:使用霍夫变换需要将图像先二值化

概率霍夫变换函数:

  • lines=cv2.HoughLinesP(image, rho,theta,threshold,minLineLength, maxLineGap)
  • image:图像,必须是8位单通道二值图像
  • rho:以像素为单位的距离r的精度,一般情况下是使用1
  • theta:表示搜索可能的角度,使用的精度是np.pi/180
  • threshold:阈值,该值越小,判定的直线越多,相反则直线越少
  • minLineLength:默认为0,控制接受直线的最小长度
  • maxLineGap:控制接受共线线段的最小间隔,如果两点间隔超过了参数,就认为两点不在同一直线上,默认为0
  • lines:返回值由numpy.ndarray构成,每一对都是一对浮点数,表示线段的两个端点

  
  1. import cv2
  2. import numpy as np
  3. #计算斜率
  4. def calculate_slope( line):
  5. x_1, y_1, x_2, y_2 = line[ 0]
  6. return (y_2 - y_1) / (x_2 - x_1)
  7. edge_img = cv2.imread( 'masked_edge_img.jpg', cv2.IMREAD_GRAYSCALE)
  8. #霍夫变换获取所有线段
  9. lines = cv2.HoughLinesP(edge_img, 1, np.pi / 180, 15, minLineLength= 40,
  10. maxLineGap= 20)
  11. #利用斜率划分线段
  12. left_lines = [line for line in lines if calculate_slope(line) < 0]
  13. right_lines = [line for line in lines if calculate_slope(line) > 0]

四、离群值过滤,剔除斜率相差过大的线段

流程:

  • 获取所有的线段的斜率,然后计算斜率的平均值
  • 遍历所有斜率,计算和平均斜率的差值,寻找最大的那个斜率对应的直线,如果差值大于阈值,那么就从列表中剔除对应的线段和斜率
  • 循环执行操作,直到剩下的全部都是小于阈值的线段

  
  1. def reject_abnormal_lines(lines, threshold):
  2. slopes = [ calculate_slope(line) for line in lines]
  3. while len(lines) > 0:
  4. mean = np. mean(slopes)
  5. diff = [ abs(s - mean) for s in slopes]
  6. idx = np. argmax(diff)
  7. if diff[idx] > threshold:
  8. slopes. pop(idx)
  9. lines. pop(idx)
  10. else:
  11. break
  12. return lines
  13. reject_abnormal_lines(left_lines, threshold= 0.2)
  14. reject_abnormal_lines(right_lines, threshold= 0.2)

五、最小二乘拟合,实现将左边和右边的线段互相拟合成一条直线,形成车道线

流程:

  • 取出所有的直线的x和y坐标,组成列表,利用np.ravel进行将高维转一维数组
  • 利用np.polyfit进行直线的拟合,最终得到拟合后的直线的斜率和截距,类似y=kx+b的(k,b)
  • 最终要返回(x_min,y_min,x_max,y_max)的一个np.array的数据,那么就是用np.polyval求多项式的值,举个example,np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1,即可以获得对应x坐标的y坐标。

  
  1. def least_squares_fit( lines):
  2. # 1. 取出所有坐标点
  3. x_coords = np.ravel( [[line[0][0], line[0][2]] for line in lines])
  4. y_coords = np.ravel( [[line[0][1], line[0][3]] for line in lines])
  5. # 2. 进行直线拟合.得到多项式系数
  6. poly = np.polyfit(x_coords, y_coords, deg= 1)
  7. print(poly)
  8. # 3. 根据多项式系数,计算两个直线上的点,用于唯一确定这条直线
  9. point_min = (np. min(x_coords), np.polyval(poly, np. min(x_coords)))
  10. point_max = (np. max(x_coords), np.polyval(poly, np. max(x_coords)))
  11. return np.array([point_min, point_max], dtype=np.int)
  12. print( "left lane")
  13. print(least_squares_fit(left_lines))
  14. print( "right lane")
  15. print(least_squares_fit(right_lines))

六、绘制线段


  
  1. cv2 .line(img, tuple(left_line[ 0]), tuple(left_line[ 1]), color=( 0, 255, 255), thickness= 5)
  2. cv2 .line(img, tuple(right_line[ 0]), tuple(right_line[ 1]), color=( 0, 255, 255), thickness= 5)

全部代码(视频显示)


  
  1. import cv2
  2. import numpy as np
  3. def get_edge_img( color_img, gaussian_ksize=5, gaussian_sigmax=1,
  4. canny_threshold1=50, canny_threshold2=100):
  5. """
  6. 灰度化,模糊,canny变换,提取边缘
  7. :param color_img: 彩色图,channels=3
  8. """
  9. gaussian = cv2.GaussianBlur(color_img, (gaussian_ksize, gaussian_ksize),
  10. gaussian_sigmax)
  11. gray_img = cv2.cvtColor(gaussian, cv2.COLOR_BGR2GRAY)
  12. edges_img = cv2.Canny(gray_img, canny_threshold1, canny_threshold2)
  13. return edges_img
  14. def roi_mask( gray_img):
  15. """
  16. 对gray_img进行掩膜
  17. :param gray_img: 灰度图,channels=1
  18. """
  19. poly_pts = np.array([[[ 0, 368], [ 300, 210], [ 340, 210], [ 640, 368]]])
  20. mask = np.zeros_like(gray_img)
  21. mask = cv2.fillPoly(mask, pts=poly_pts, color= 255)
  22. img_mask = cv2.bitwise_and(gray_img, mask)
  23. return img_mask
  24. def get_lines( edge_img):
  25. """
  26. 获取edge_img中的所有线段
  27. :param edge_img: 标记边缘的灰度图
  28. """
  29. def calculate_slope( line):
  30. """
  31. 计算线段line的斜率
  32. :param line: np.array([[x_1, y_1, x_2, y_2]])
  33. :return:
  34. """
  35. x_1, y_1, x_2, y_2 = line[ 0]
  36. return (y_2 - y_1) / (x_2 - x_1)
  37. def reject_abnormal_lines( lines, threshold=0.2):
  38. """
  39. 剔除斜率不一致的线段
  40. :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]])]
  41. """
  42. slopes = [calculate_slope(line) for line in lines]
  43. while len(lines) > 0:
  44. mean = np.mean(slopes)
  45. diff = [ abs(s - mean) for s in slopes]
  46. idx = np.argmax(diff)
  47. if diff[idx] > threshold:
  48. slopes.pop(idx)
  49. lines.pop(idx)
  50. else:
  51. break
  52. return lines
  53. def least_squares_fit( lines):
  54. """
  55. 将lines中的线段拟合成一条线段
  56. :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]])]
  57. :return: 线段上的两点,np.array([[xmin, ymin], [xmax, ymax]])
  58. """
  59. x_coords = np.ravel([[line[ 0][ 0], line[ 0][ 2]] for line in lines])
  60. y_coords = np.ravel([[line[ 0][ 1], line[ 0][ 3]] for line in lines])
  61. poly = np.polyfit(x_coords, y_coords, deg= 1)
  62. point_min = (np. min(x_coords), np.polyval(poly, np. min(x_coords)))
  63. point_max = (np. max(x_coords), np.polyval(poly, np. max(x_coords)))
  64. return np.array([point_min, point_max], dtype=np. int)
  65. # 获取所有线段
  66. lines = cv2.HoughLinesP(edge_img, 1, np.pi / 180, 15, minLineLength= 40,
  67. maxLineGap= 20)
  68. # 按照斜率分成车道线
  69. left_lines = [line for line in lines if calculate_slope(line) > 0]
  70. right_lines = [line for line in lines if calculate_slope(line) < 0]
  71. # 剔除离群线段
  72. left_lines = reject_abnormal_lines(left_lines)
  73. right_lines = reject_abnormal_lines(right_lines)
  74. return least_squares_fit(left_lines), least_squares_fit(right_lines)
  75. def draw_lines( img, lines):
  76. left_line, right_line = lines
  77. cv2.line(img, tuple(left_line[ 0]), tuple(left_line[ 1]), color=( 0, 255, 255),
  78. thickness= 5)
  79. cv2.line(img, tuple(right_line[ 0]), tuple(right_line[ 1]),
  80. color=( 0, 255, 255), thickness= 5)
  81. def show_lane( color_img):
  82. edge_img = get_edge_img(color_img)
  83. mask_gray_img = roi_mask(edge_img)
  84. lines = get_lines(mask_gray_img)
  85. draw_lines(color_img, lines)
  86. return color_img
  87. capture = cv2.VideoCapture( 'video.mp4')
  88. while True:
  89. ret, frame = capture.read()
  90. if not ret:
  91. break
  92. frame = show_lane(frame)
  93. cv2.imshow( 'frame', frame)
  94. cv2.waitKey( 10)


转载:https://blog.csdn.net/weixin_54627824/article/details/127203408
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场