車道線檢測


檢測步驟:

  • 相機標定
  • 圖片失真校正
  • 圖像閾值化
  • 透視變換
  • 檢測車道像素並擬合邊界
  • 計算車道的曲率和車輛相對位置
  • 車道邊界彎曲回原始圖像

一、相機標定

1.1 角點檢測

我從准備object points開始,它將是世界棋盤角落的(x, y, z)坐標。這里我假設棋盤固定在z=0的(x, y)平面上,這樣每個校准圖像的目標點都是相同的。因此objp只是一個復制的坐標數組,每當我成功地檢測到測試圖像中的所有棋盤角時,objpoints將附加一個它的副本。imgpoints將與(x, y)像素位置的每一個角落在圖像平面與每一個成功的棋盤檢測。

# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*9,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)

# Arrays to store object points and image points from all the images.
objpoints = [] # 3d points in real world space
imgpoints = [] # 2d points in image plane.

其中imgpoints的獲得,通過cv2.findChessboardCorners()函數。

1.2 標定

然后,我使用輸出objpointsimgpoints使用cv2.calibrateCamera()函數計算相機校准和失真系數。我使用 cv2. undistort()函數對測試圖像進行失真校正,

# Test undistortion on an image
img = cv2.imread('./calibration.jpg')
img_size = (img.shape[1], img.shape[0])

# Do camera calibration given object points and image points
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size,None,None)

dst = cv2.undistort(img, mtx, dist, None, mtx)

得到如下結果:


Figure 1 去畸變

二、圖片的Pipeline

2.1 圖片獲取

將一張原始圖片通過之前標定的參數進行矯正,再對矯正后的圖片進行余下操作,矯正的函數為cv2.undistort(),結果如Figure 2所示,


Figure 2 車道線矯正

2.2 圖像閾值化

我使用顏色和梯度閾值的組合來生成一個二進制圖像。下面是這個步驟的輸出示例。我使用binary_select()函數對測試圖像應用了顏色變換和漸變,並得到了這個結果。

# Define a function that thresholds the S-channel of HLS
def binary_select(image, s_thresh=(170, 255), sx_thresh=(20, 100)):
    hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS) #Convert to HLS color space
    s_channel = hls[:,:,2] #Apply a threshold to the S channel
    
    # Threshold color channel
    s_binary = np.zeros_like(s_channel)
    s_binary[(s_channel >= s_thresh[0]) & (s_channel <= s_thresh[1])] = 255

    # Sobel x
    sobelx = cv2.Sobel(s_channel, cv2.CV_64F, 1, 0) # Take the derivative in x
    abs_sobelx = np.absolute(sobelx) # Absolute x derivative to accentuate lines away from horizontal
    scaled_sobel = np.uint8(255*abs_sobelx/np.max(abs_sobelx))
    
    # Threshold x gradient
    sxbinary = np.zeros_like(scaled_sobel)
    sxbinary[(scaled_sobel >= sx_thresh[0]) & (scaled_sobel <= sx_thresh[1])] = 255
    
    #the threshold result of combine s_binary and sxbinary
    binary_output = np.zeros_like(s_channel)
    binary_output[(s_binary >= 255) & (sxbinary >= 255)] = 255
    
    return s_channel, s_binary, sxbinary, binary_output 


Figure 3 閾值化效果

2.3 透視變換

我的透視圖轉換代碼包含一個名為birds_eye_warp()的函數。函數birds_eye_warp()接收圖像(img)、Source(src)和 Destination(dst)作為輸入。 其中Source(src)為圖片上的像素坐標,而Destination(dst)是實際場景的比例關系估算獲得。

Source(src) Destination(dst)
575,464 150,0
707,464 1130,0
1049,682 1130,720
258,682 150,720

接着通過函數cv2.getPerspectiveTransform()獲得Source點和Destination的變換關系,

# the function of "birds-eye view"
def birds_eye_warp(img, src, dst): 
    h,w = img.shape[:2]
    M = cv2.getPerspectiveTransform(src, dst)
    Minv = cv2.getPerspectiveTransform(dst, src)
    # use cv2.warpPerspective() to warp your image to a top-down view
    warped = cv2.warpPerspective(img, M, (w,h), flags=cv2.INTER_LINEAR)
    return warped, M, Minv

最后,我將srcdst點繪制到測試圖像及其翹曲的對應圖像上,驗證了透視圖轉換如預期的那樣工作,以及這些車道線經過透視圖轉換后看起來是平行的。


Figure 4 鳥瞰圖

2.4 擬合車道線像素

首先,通過代碼hist()函數查找屬於車道線的像素。我選擇了兩個最強的峰作為初始車道線的起點。其次,使用Sliding Windows策略,為每段選擇車道線像素。

def hist(img):   #img是歸一化處理的單通道圖片
    # TO-DO: Grab only the bottom half of the image
    # Lane lines are likely to be mostly vertical nearest to the car
    bottom_half = None
    bottom_half = img[img.shape[0]//2:,]
    # TO-DO: Sum across image pixels vertically - make sure to set `axis`
    # i.e. the highest areas of vertical lines should be larger values
    histogram = None
    histogram = np.sum(bottom_half, axis = 0)

    return histogram


Figure 5 統計頻數直方圖

最后,我借助函數numpy.polyfit()通過一個二階多項式來擬合車道線像素。使用它們的x和y像素位置來擬合一個二階多項式曲線\(f(y) = Ay^2 + By + C\)


Figure 6 擬合曲線

2.5 車道曲率和車輛位置

通過曲率半徑的參考公式

編寫函數measure re_ature_real()計算曲線x=f(y)任意點x處曲率半徑的直線

def measure_curvature_real(left_fitx, right_fitx, ploty, left_fit, right_fit):

    # Define conversions in x and y from pixels space to meters
    ym_per_pix = 16.0/720 # meters per pixel in y dimension
    xm_per_pix = 3.7/1000 # meters per pixel in x dimension
    
    leftx = left_fitx*xm_per_pix
    rightx = right_fitx*xm_per_pix
    ploty = ploty*ym_per_pix
    
    left_fit_cr = np.polyfit(ploty, leftx, 2)
    right_fit_cr = np.polyfit(ploty, rightx, 2)

    # Define y-value where we want radius of curvature
    # We'll choose the maximum y-value, corresponding to the bottom of the image
    y_eval = np.max(ploty)
    
    # Implement the calculation of R_curve (radius of curvature) 
    left_curverad = ((1 + (2*left_fit_cr[0]*y_eval*ym_per_pix + left_fit_cr[1])**2)**1.5) / np.absolute(2*left_fit_cr[0])
    right_curverad = ((1 + (2*right_fit_cr[0]*y_eval*ym_per_pix + right_fit_cr[1])**2)**1.5) / np.absolute(2*right_fit_cr[0])
    
    return left_curverad, right_curverad
  • \(car_{p}\) 是汽車在圖片中的位置,因為相機安裝在汽車的中心,這就相當於取圖像寬度的一半car_position = img.shape[1]/2
  • \(xm_{pp}\) 是圖像像素與實際距離之間的關系,即代碼中的xm_per_pix = 3.7/1000,其中3.7是車道線實際寬度3.7米,1000是圖片像素的像素距離。
  • \(lanecenter_{p}\) 是車道線中心,通過檢測圖片中沿x軸的車道線來計算的,即lane_center_position = (r_fit_x_int + l_fit_x_int) /2

最后,車輛相對於車道中心的位置\(center_{d}\)計算公式如下:

\(center_{d} = (car_{p} - lanecenter_{p}) * xm_{pp}\)

2.6 檢測效果


Figure 6 檢測結果

三、視頻的Pipeline

將圖片的檢測方法運用到車輛行駛的視頻上,可以獲得輕微抖動的檢測效果,沒有會導致汽車駛離道路的災難性故障。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM