計算機視覺算法使用OPENCV圖像處理,攝像頭校準,透視變換,顏色口罩,Sobels和多項式擬合來計算道路曲率和車道車輛的偏移。
在識別前方道路的幾何形狀方面,高級泳道的發現項目離[車道線檢測]較遠。
使用高速公路駕駛的視頻錄製,該項目的目標是計算道路曲率的半徑。彎曲的道路比直線公路更具挑戰性。要正確計算曲率,需要識別車道線,但最重要的是,圖像不得不被關注。圖像轉換對於相機校準和透視轉換是必要的,以獲得鳥類的眼光。
該項目在Python中實現,並使用OpenCV圖像處理庫。源代碼可以在AdvancedLaneFinding.ipynb中找到
[視頻失真]是一種物理現象,發生在圖像記錄中,當通過攝像機鏡頭感知時,直線將直線投影為略微彎曲的現象。高速公路駕駛視頻是使用汽車上的前面攝像頭錄製的,並且圖像被扭曲。失真係數對每個相機都是特異性的,可以使用已知的幾何形式來計算。
用嵌入式相機捕獲的棋盤圖像在camera_cal文件夾中提供。這些圖像的優點是它們具有高對比度和已知的幾何形狀。提供的圖像目前可以使用9 * 6個角落。
# Object points are real world points, here a 3D coordinates matrix is generated
# z coordinates are 0 and x, y are equidistant as it is known that the chessboard is made of identical squares
objp = np.zeros((6*9,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)
對象點是基於共同的理解設置的,即在國際象棋板圖案中,所有正方形都是相等的。這意味著對象點將具有從網格索引生成的x和y坐標,而z始終為0。圖像點代表使用OpenCV的函數findChessboardCorners在圖像中找到的相應對象點。
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Find the chessboard corners
nx = 9
ny = 6
ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)
掃描所有圖像後,圖像點列表具有足夠的數據來與對象點進行比較,以計算攝像機矩陣和失真係數。這導致了使用“ CalibrateCamera”功能的精確攝像機矩陣和失真係數識別。
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
undist = cv2.undistort(img, mtx, dist, None, mtx)
OPENCV undistort函數用於使用攝像機矩陣和失真係數轉換圖像。
比較這些圖片時,可見相機校準技術的結果。在棋盤圖片上,扭曲更為明顯,在路上,它更加微妙。然而,未呈現的圖片會導致不正確的道路曲率計算。
為了縮小曲率,理想的視角是鳥類的視野。這意味著從上方感知道路,而不是通過車輛的擋風玻璃角度來看。
這種透視轉換是使用直線車道的場景計算的,並且先前的常識是車道線實際上是平行的。源和目標點直接從圖像中直接識別出透視轉換。
#Source points taken from images with straight lane lines, these are to become parallel after the warp transform
src = np.float32([
(190, 720), # bottom-left corner
(596, 447), # top-left corner
(685, 447), # top-right corner
(1125, 720) # bottom-right corner
])
# Destination points are to be parallel, taking into account the image size
dst = np.float32([
[offset, img_size[1]], # bottom-left corner
[offset, 0], # top-left corner
[img_size[0]-offset, 0], # top-right corner
[img_size[0]-offset, img_size[1]] # bottom-right corner
])
OPENCV提供了透視轉換函數,以計算給定源和目標點的圖像的轉換矩陣。使用warpPerspective功能,執行了鳥的眼睛視圖轉換。
# Calculate the transformation matrix and it's inverse transformation
M = cv2.getPerspectiveTransform(src, dst)
M_inv = cv2.getPerspectiveTransform(dst, src)
warped = cv2.warpPerspective(undist, M, img_size)
目的是以這樣的方式處理圖像,以使車道線像素被保留並易於與道路區分開。應用四個轉換,然後組合。
第一個轉換將x sobel在灰度尺度的圖像上拍攝。這代表X方向的導數,並有助於檢測傾向於垂直的線。僅保留高於最小閾值的值。
# Transform image to gray scale
gray_img =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply sobel (derivative) in x direction, this is usefull to detect lines that tend to be vertical
sobelx = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0)
abs_sobelx = np.absolute(sobelx)
# Scale result to 0-255
scaled_sobel = np.uint8(255*abs_sobelx/np.max(abs_sobelx))
sx_binary = np.zeros_like(scaled_sobel)
# Keep only derivative values that are in the margin of interest
sx_binary[(scaled_sobel >= 30) & (scaled_sobel <= 255)] = 1
第二個轉換選擇灰色縮放圖像中的白色像素。白色是由200至255之間的值定義的,這些值是在給定圖片上使用反複試驗選擇的。
# Detect pixels that are white in the grayscale image
white_binary = np.zeros_like(gray_img)
white_binary[(gray_img > 200) & (gray_img <= 255)] = 1
第三個轉換是使用HLS Colorspace在飽和組件上。這對於在光混凝土路上檢測黃線尤為重要。
# Convert image to HLS
hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
H = hls[:,:,0]
S = hls[:,:,2]
sat_binary = np.zeros_like(S)
# Detect pixels that have a high saturation value
sat_binary[(S > 90) & (S <= 255)] = 1
第四個轉換在色調分量上,值從10到25,被識別為對應於黃色。
hue_binary = np.zeros_like(H)
# Detect pixels that are yellow using the hue component
hue_binary[(H > 10) & (H <= 25)] = 1
車道線檢測是對已經未經扭曲和扭曲的二元閾值圖像進行的。最初,在圖像上計算直方圖。這意味著在每列上求和,以檢測左右車道線的最可能的X位置。
# Take a histogram of the bottom half of the image
histogram = np.sum(binary_warped[binary_warped.shape[0]//2:,:], axis=0)
# Find the peak of the left and right halves of the histogram
# These will be the starting point for the left and right lines
midpoint = np.int(histogram.shape[0]//2)
leftx_base = np.argmax(histogram[:midpoint])
rightx_base = np.argmax(histogram[midpoint:]) + midpoint
從圖像底部的這些基本位置開始,應用滑動窗口方法向上搜索線像素。當X和Y坐標位於窗口定義的區域內時,請考慮採用車道像素。當檢測到足夠的像素以確信它們是線路的一部分時,它們的平均位置將被計算並保留為下一個上下窗口的起點。
# Choose the number of sliding windows
nwindows = 9
# Set the width of the windows +/- margin
margin = 100
# Set minimum number of pixels found to recenter window
minpix = 50
# Identify window boundaries in x and y (and right and left)
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
# Identify the nonzero pixels in x and y within the window #
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]
# Append these indices to the lists
left_lane_inds.append(good_left_inds)
right_lane_inds.append(good_right_inds)
所有這些像素都放在X和Y坐標的列表中。這是在兩條車道線上對稱完成的。 leftx , lefty , rightx , righty像素位置是從功能返回的,然後在左右側擬合二級多項式,以找到所選像素的最佳線路擬合。
# Fit a second order polynomial to each with np.polyfit() ###
left_fit = np.polyfit(lefty, leftx, 2)
right_fit = np.polyfit(righty, rightx, 2)
在這裡,確定的左和右線像素分別以紅色和藍色標記。第二度多項式在結果圖像上追踪。
為了將車道線搜索從一個視頻框架加快速度,使用了上一個週期中的信息。下一個圖像更有可能具有靠近先前車道線路的車道線。這是對上圖像的左線和右線的多項式擬合來定義搜索區域。
仍使用滑動窗口方法,但是搜索是沿著先前的行進行搜索,而不是從直方圖的峰值開始,並為窗口的寬度提供了給定的邊距。
# Set the area of search based on activated x-values within the +/- margin of our polynomial function
left_lane_inds = ((nonzerox > (prev_left_fit[0]*(nonzeroy**2) + prev_left_fit[1]*nonzeroy +
prev_left_fit[2] - margin)) & (nonzerox < (prev_left_fit[0]*(nonzeroy**2) +
prev_left_fit[1]*nonzeroy + prev_left_fit[2] + margin))).nonzero()[0]
right_lane_inds = ((nonzerox > (prev_right_fit[0]*(nonzeroy**2) + prev_right_fit[1]*nonzeroy +
prev_right_fit[2] - margin)) & (nonzerox < (prev_right_fit[0]*(nonzeroy**2) +
prev_right_fit[1]*nonzeroy + prev_right_fit[2] + margin))).nonzero()[0]
# Again, extract left and right line pixel positions
leftx = nonzerox[left_lane_inds]
lefty = nonzeroy[left_lane_inds]
rightx = nonzerox[right_lane_inds]
righty = nonzeroy[right_lane_inds]
搜索返回leftx , lefty , rightx , righty Pixel坐標,這些坐標擬合了每個左側和右側的二級多項式函數。
為了計算半徑和車輛在道路上的位置以米為單位,需要從像素轉換為縮放因素。相應的縮放值在y方向上為30米至720像素,在x尺寸中為3.7米到700像素。
多項式擬合用於進行轉換。使用從每個左右車道線的擬合線的對齊像素的X坐標,應用了轉換因子,並且在每個線路上進行多項式擬合。
# Define conversions in x and y from pixels space to meters
ym_per_pix = 30/720 # meters per pixel in y dimension
xm_per_pix = 3.7/700 # meters per pixel in x dimension
left_fit_cr = np.polyfit(ploty*ym_per_pix, left_fitx*xm_per_pix, 2)
right_fit_cr = np.polyfit(ploty*ym_per_pix, right_fitx*xm_per_pix, 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)
# 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])
曲率的半徑是使用圖像底部的y點計算的。為了計算車輛的位置,使用像素中的多項式擬合來確定對應於圖像底部y的左右車道的X位置。
# Define conversion in x from pixels space to meters
xm_per_pix = 3.7/700 # meters per pixel in x dimension
# Choose the y value corresponding to the bottom of the image
y_max = binary_warped.shape[0]
# Calculate left and right line positions at the bottom of the image
left_x_pos = left_fit[0]*y_max**2 + left_fit[1]*y_max + left_fit[2]
right_x_pos = right_fit[0]*y_max**2 + right_fit[1]*y_max + right_fit[2]
# Calculate the x position of the center of the lane
center_lanes_x_pos = (left_x_pos + right_x_pos)//2
# Calculate the deviation between the center of the lane and the center of the picture
# The car is assumed to be placed in the center of the picture
# If the deviation is negative, the car is on the felt hand side of the center of the lane
veh_pos = ((binary_warped.shape[1]//2) - center_lanes_x_pos) * xm_per_pix
這兩個值的平均值給出了圖像中車道中心的位置。如果車道的中心通過nbp數量的像素向右移動,則意味著汽車被nbp * xm_per_pix meters向左移動。這是基於假設攝像機安裝在車輛中央軸上的假設。