女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

用于無人駕駛技術中的車道線檢測技術

ml8z_IV_Technol ? 來源:cc ? 2019-01-23 10:27 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在本次分享中,我將以優達學城(Udacity)無人駕駛工程師學位中提供的高級車道線檢測項目為例,介紹普適性更好,且更為魯棒的車道線檢測技術,用于處理那些無人駕駛中常見的(如路面顏色變化、路邊障礙物陰影等導致的)光線變化劇烈和彎道的場景。

按照慣例,在介紹計算機視覺技術前,我們先討論一下這次分享的輸入和輸出。

輸入

一個連續的視頻,視頻中的左車道線為黃色實線,右車道線為白色虛線。無人車會經過路面顏色突變、路邊樹木影子干擾、車道線不清晰和急轉彎的路況。

輸出

左、右車道線的三次曲線方程,及其有效距離。最后將車道線圍成的區域顯示在圖像上,如下圖所示。

輸入和輸出都定義清楚后,我們開始探討高級車道線檢測的算法,并對每幀視頻圖像中的車道線進行檢測。

攝像機標定

相信大家都多少聽說過魚眼相機,最常見的魚眼相機是輔助駕駛員倒車的后向攝像頭。也有很多攝影愛好者會使用魚眼相機拍攝圖像,最終會有高大上的大片效果,如下圖所示。

圖片來源:優達學城(Udacity)無人駕駛工程師課程

使用魚眼相機拍攝的圖像雖然高大上,但存在一個很大的問題——畸變(Distortion)。如上圖所示,走道上的欄桿應該是筆直延伸出去的。然而,欄桿在圖像上的成像卻是彎曲的,這就是圖像畸變,畸變會導致圖像失真。

使用車載攝像機拍攝出的圖像,雖然沒有魚眼相機的畸變這么夸張,但是畸變是客觀存在的,只是人眼難以察覺。使用有畸變的圖像做車道線的檢測,檢測結果的精度將會受到影響,因此進行圖像處理的第一步工作就是去畸變。

為了解決車載攝像機圖像的畸變問題,攝像機標定技術應運而生。

攝像機標定是通過對已知的形狀進行拍照,通過計算該形狀在真實世界中位置與在圖像中位置的偏差量(畸變系數),進而用這個偏差量去修正其他畸變圖像的技術。

原則上,可以選用任何的已知形狀去校準攝像機,不過業內的標定方法都是基于棋盤格的。因為它具備規則的、高對比度圖案,能非常方便地自動化檢測各個棋盤格的交點,十分適合標定攝像機的標定工作。如下圖所示為標準的10x7(7行10列)的棋盤格。

OpenCV庫為攝像機標定提供了函數cv2.findChessboardCorners(),它能自動地檢測棋盤格內4個棋盤格的交點(2白2黑的交接點)。我們只需要輸入攝像機拍攝的完整棋盤格圖像和交點在橫縱向上的數量即可。隨后我們可以使用函數cv2.drawChessboardCorners()繪制出檢測的結果。

棋盤格原圖如下所示:

圖片出處:https://github.com/udacity/CarND-Advanced-Lane-Lines/blob/master/camera_cal/calibration2.jpg

使用OpenCV自動交點檢測的結果如下:

獲取交點的檢測結果后,使用函數cv2.calibrateCamera()即可得到相機的畸變系數。

為了使攝像機標定得到的畸變系數更加準確,我們使用車載攝像機從不同的角度拍攝20張棋盤格,將所有的交點檢測結果保存,再進行畸變系數的的計算。

我們將讀入圖片、預處理圖片、檢測交點、標定相機的一系列操作,封裝成一個函數,如下所示:

# Step 1 : Calculate camera distortion coefficientsdef getCameraCalibrationCoefficients(chessboardname, nx, ny): # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0) objp = np.zeros((ny * nx, 3), np.float32) objp[:,:2] = np.mgrid[0:nx, 0:ny].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. images = glob.glob(chessboardname) if len(images) > 0: print("images num for calibration : ", len(images)) else: print("No image for calibration.") return ret_count = 0 for idx, fname in enumerate(images): img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img_size = (img.shape[1], img.shape[0]) # Finde the chessboard corners ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None) # If found, add object points, image points if ret == True: ret_count += 1 objpoints.append(objp) imgpoints.append(corners) ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None, None) print('Do calibration successfully') return ret, mtx, dist, rvecs, tvecs

需要標定的一系列圖片如下圖所示:

圖片出處:https://github.com/udacity/CarND-Advanced-Lane-Lines/tree/master/camera_cal

調用之前封裝好的函數,獲取畸變參數。

nx = 9 ny = 6 ret, mtx, dist, rvecs, tvecs = getCameraCalibrationCoefficients('camera_cal/calibration*.jpg', nx, ny)

隨后,使用OpenCV提供的函數cv2.undistort(),傳入剛剛計算得到的畸變參數,即可將畸變的圖像進行畸變修正處理。

# Step 2 : Undistort image def undistortImage(distortImage, mtx, dist): return cv2.undistort(distortImage, mtx, dist, None, mtx)

以畸變的棋盤格圖像為例,進行畸變修正處理

# Read distorted chessboard image test_distort_image = cv2.imread('./camera_cal/calibration4.jpg') # Do undistortion test_undistort_image = undistortImage(test_distort_image, mtx, dist)

畸變圖像如下圖所示:

圖像出處:https://github.com/udacity/CarND-Advanced-Lane-Lines/blob/master/camera_cal/calibration4.jpg

復原后的圖像如下圖所示:

同理,我們將攝像機拍攝到的實際路況進行畸變修正處理。

test_distort_image = cv2.imread('test_images/straight_lines1.jpg') # Do undistortion test_undistort_image = undistortImage(test_distort_image, mtx, dist)

原始畸變圖像如下所示:

圖片出處:https://github.com/udacity/CarND-Advanced-Lane-Lines/blob/master/test_images/straight_lines1.jpg

畸變修正后的圖像如下所示:

可以看到離鏡頭更近的左側、右側和下側的圖像比遠處的畸變修正更明顯。

篩選圖像

從我們作為輸入的視頻可以看出,車輛會經歷顛簸、車道線不清晰、路面顏色突變,路邊障礙物陰影干擾等復雜工況。因此,需要將這些復雜的場景篩選出來,確保后續的算法能夠在這些復雜場景中正確地檢測出車道線。

使用以下代碼將視頻中的圖像數據提取,進行畸變修正處理后,存儲在名為original_image的文件夾中,以供挑選。

video_input = 'project_video.mp4' cap = cv2.VideoCapture(video_input) count = 1 while(True): ret, image = cap.read() if ret: undistort_image = undistortImage(image, mtx, dist) cv2.imwrite('original_image/' + str(count) + '.jpg', undistort_image) count += 1 else: break cap.release()

在original_image文件夾中,挑選出以下6個場景進行檢測。這6個場景既包含了視頻中常見的正常直道、正常彎道工況,也包含了具有挑戰性的陰影、明暗劇烈變化的工況。如下圖所示:

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

如果后續的高級車道線檢測算法能夠完美處理以上六種工況,那將算法應用到視頻中,也會得到完美的車道線檢測效果。

透視變換

在完成圖像的畸變修正后,就要將注意力轉移到車道線。與《無人駕駛技術入門(十四)| 初識圖像之初級車道線檢測》中技術類似,這里需要定義一個感興趣區域。很顯然,我們的感興趣區域就是車輛正前方的這個車道。為了獲取感興趣區域,我們需要對自車正前方的道路使用一種叫做透視變換的技術。

“透視”是圖像成像時,物體距離攝像機越遠,看起來越小的一種現象。在真實世界中,左右互相平行的車道線,會在圖像的最遠處交匯成一個點。這個現象就是“透視成像”的原理造成的。

以立在路邊的交通標志牌為例,它在攝像機所拍攝的圖像中的成像結果一般如下下圖所示:

圖片來源:優達學城(Udacity)無人駕駛工程師課程

在這幅圖像上,原本應該是正八邊形的標志牌,成像成為一個不規則的八邊形。

通過使用透視變換技術,可以將不規則的八邊形投影成規則的正八邊形。應用透視變換后的結果對比如吐下:

圖片來源:優達學城(Udacity)無人駕駛工程師課程

透視變換的原理:首先新建一幅跟左圖同樣大小的右圖,隨后在做圖中選擇標志牌位于兩側的四個點(如圖中的紅點),記錄這4個點的坐標,我們稱這4個點為src_points。圖中的4個點組成的是一個平行四邊形。

由先驗知識可知,左圖中4個點所圍成的平行四邊形,在現實世界中是一個長方形,因此在右邊的圖中,選擇一個合適的位置,選擇一個長方形區域,這個長方形的4個端點一一對應著原圖中的src_points,我們稱新的這4個點為dst_points。

得到src_points,dst_points后,我們就可以使用OpenCV中計算投影矩陣的函數cv2.getPerspectiveTransform(src_points, dst_points)算出src_points到dst_points的投影矩陣和投影變換后的圖像了。

使用OpenCV庫實現透視變換的代碼如下:

# Step 3 : Warp image based on src_points and dst_points # The type of src_points & dst_points should be like # np.float32([ [0,0], [100,200], [200, 300], [300,400]]) def warpImage(image, src_points, dst_points): image_size = (image.shape[1], image.shape[0]) # rows = img.shape[0] 720 # cols = img.shape[1] 1280 M = cv2.getPerspectiveTransform(src, dst) Minv = cv2.getPerspectiveTransform(dst, src) warped_image = cv2.warpPerspective(image, M,image_size, flags=cv2.INTER_LINEAR) return warped_image, M, Minv

同理,對于畸變修正過的道路圖像,我們同樣使用相同的方法,將我們感興趣的區域做透視變換。

如下圖所示,我們選用一張在直線道路上行駛的圖像,沿著左右車道線的邊緣,選擇一個梯形區域,這個區域在真實的道路中應該是一個長方形,因此我們選擇將這個梯形區域投影成為一個長方形,在右圖橫坐標的合適位置設置長方形的4個端點。最終的投影結果就像“鳥瞰圖”一樣。

圖片出處:https://github.com/udacity/CarND-Advanced-Lane-Lines/tree/master/examples/warped_straight_lines.jpg

使用以下代碼,通過不斷調整src和dst的值,確保在直線道路上,能夠調試出滿意的透視變換圖像。

test_distort_image = cv2.imread('test_images/test4.jpg')# 畸變修正test_undistort_image = undistortImage(test_distort_image, mtx, dist)# 左圖梯形區域的四個端點src = np.float32([[580, 460], [700, 460], [1096, 720], [200, 720]])# 右圖矩形區域的四個端點dst = np.float32([[300, 0], [950, 0], [950, 720], [300, 720])test_warp_image, M, Minv = warpImage(test_undistort_image, src, dst)

最終,我們把篩選出的6幅圖統一應用調整好的src、dst做透視變換,結果如下:

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

可以看到,越靠圖片下方的圖像越清晰,越上方的圖像越模糊。這是因為越遠的地方,左圖中的像素點越少。而無論是遠處還是近處,需要在右圖中填充的像素點數量是一樣的。左圖近處有足夠多的點去填充右圖,而左圖遠處的點有限,只能通過插值的方式創造“假的”像素點進行填充,所以就不那么清晰了。

提取車道線

在《無人駕駛技術入門(十四)| 初識圖像之初級車道線檢測》中,我們介紹了通過Canny邊緣提取算法獲取車道線待選點的方法,隨后使用霍夫直線變換進行了車道線的檢測。在這里,我們也嘗試使用邊緣提取的方法進行車道線提取。

需要注意的是,Canny邊緣提取算法會將圖像中各個方向、明暗交替位置的邊緣都提取出來,很明顯,Canny邊緣提取算法在處理有樹木陰影的道路時,會將樹木影子的輪廓也提取出來,這是我們不愿意看到的。

因此我們選用Sobel邊緣提取算法。Sobel相比于Canny的優秀之處在于,它可以選擇橫向或縱向的邊緣進行提取。從投影變換后的圖像可以看出,我們關心的正是車道線在橫向上的邊緣突變。

封裝一下OpenCV提供的cv2.Sobel()函數,將進行邊緣提取后的圖像做二進制圖的轉化,即提取到邊緣的像素點顯示為白色(值為1),未提取到邊緣的像素點顯示為黑色(值為0)。

def absSobelThreshold(img, orient='x', thresh_min=30, thresh_max=100): # Convert to grayscale gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # Apply x or y gradient with the OpenCV Sobel() function # and take the absolute value if orient == 'x': abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 1, 0)) if orient == 'y': abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 0, 1)) # Rescale back to 8 bit integer scaled_sobel = np.uint8(255*abs_sobel/np.max(abs_sobel)) # Create a copy and apply the threshold binary_output = np.zeros_like(scaled_sobel) # Here I'm using inclusive (>=, <=) thresholds, but exclusive is ok too ? ?binary_output[(scaled_sobel >= thresh_min) & (scaled_sobel <= thresh_max)] = 1 ? ?# Return the result ? ?return binary_output

使用同一組閾值對以上6幅做過投影變換的圖像進行x方向的邊緣提取,可以得到如下結果:

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

由以上結果可以看出,在明暗交替明顯的路面上,如圖1和圖2,橫向的Sobel邊緣提取算法在提取車道線的表現上還不錯。不過一旦道路的明暗交替不那么明顯了,如圖3和圖4的白色路面區域,很難提取到有效的車道線待選點。當面對有樹木陰影覆蓋的區域時,如圖5和圖6,雖然能提取出車道線的大致輪廓,但會同時引入的噪聲,給后續處理帶來麻煩。

因此,橫向的Sobel邊緣提取算法,無法很好地處理路面陰影、明暗交替的道路工況。

無法使用邊緣提取的方法提取車道線后,我們開始將顏色空間作為突破口。

在以上6個場景中,雖然路面明暗交替,而且偶爾會有陰影覆蓋,但黃色和白色的車道線是一直都存在的。因此,我們如果能將圖中的黃色和白色分割出來,然后將兩種顏色組合在一幅圖上,就能夠得到一個比較好的處理結果。

一幅圖像除了用RGB(紅綠藍)三個顏色通道表示以外,還可以使用HSL(H色相、S飽和度、L亮度)和Lab(L亮度、a紅綠通道、b藍黃)模型來描述圖像,三通道的值與實際的成像顏色如下圖所示。

圖片出處:https://blog.csdn.net/wsp_1138886114/article/details/80660014

我們可以根據HSL模型中的L(亮度)通道來分割出圖像中的白色車道線,同時可以根據Lab模型中的b(藍黃)通道來分割出圖像中的黃色車道線,再將兩次的分割結果,去合集,疊加到一幅圖上,就能得到兩條完整的車道線了。

使用OpenCV提供的cv2.cvtColor()接口,將RGB通道的圖,轉為HLS通道的圖,隨后對L通道進行分割處理,提取圖像中白色的車道線。封裝成代碼如下:

def hlsLSelect(img, thresh=(220, 255)): hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS) l_channel = hls[:,:,1] l_channel = l_channel*(255/np.max(l_channel)) binary_output = np.zeros_like(l_channel) binary_output[(l_channel > thresh[0]) & (l_channel <= thresh[1])] = 1 ? ?return binary_output

使用同一組閾值對以上6種工況進行處理,處理結果如下圖所示。

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

使用OpenCV提供的cv2.cvtColor()接口,將RGB通道的圖,轉為Lab通道的圖,隨后對b通道進行分割處理,提取圖像中黃色的車道線。封裝成代碼如下:

def labBSelect(img, thresh=(195, 255)): # 1) Convert to LAB color space lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab) lab_b = lab[:,:,2] # don't normalize if there are no yellows in the image if np.max(lab_b) > 100: lab_b = lab_b*(255/np.max(lab_b)) # 2) Apply a threshold to the L channel binary_output = np.zeros_like(lab_b) binary_output[((lab_b > thresh[0]) & (lab_b <= thresh[1]))] = 1 ? ?# 3) Return a binary image of threshold result ? ?return binary_output

使用同一組閾值對以上6種工況進行處理,處理結果如下圖所示。

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

根據以上試驗可知,L通道能夠較好地分割出圖像中的白色車道線,b通道能夠較好地分割出圖像中的黃色車道線。即使面對樹木陰影和路面顏色突變的場景,也能盡可能少地引入噪聲。

最后,我們使用以下代碼,將兩個通道分割的圖像合并

hlsL_binary = hlsLSelect(test_warp_image) labB_binary = labBSelect(test_warp_image) combined_binary = np.zeros_like(hlsL_binary) combined_binary[(hlsL_binary == 1) | (labB_binary == 1)] = 1

最終合并的效果如下圖所示:

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由亮到暗的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

以上僅僅是車道線提取的方法之一。除了可以通過HSL和Lab顏色通道,這種基于規則的方法,分割出車道線外,還可以使用基于深度學習的方法。它們目的都是為了能夠穩定地將車道線從圖像中分割出來。

檢測車道線

在檢測車道線前,需要粗定位車道線的位置。為了方便理解,這里引入一個概念——直方圖。

以下面這幅包含噪點的圖像為例,進行直方圖的介紹。

圖片出處:優達學城(Udacity)無人駕駛工程師學位

我們知道,我們處理的圖像的分辨率為1280*720,即720行,1280列。如果我將每一列的白色的點數量進行統計,即可得到1280個值。將這1280個值繪制在一個坐標系中,橫坐標為1-1280,縱坐標表示每列中白色點的數量,那么這幅圖就是“直方圖”,如下圖所示:

圖片出處:優達學城(Udacity)無人駕駛工程師學位

將兩幅圖疊加,效果如下:

圖片出處:優達學城(Udacity)無人駕駛工程師學位

找到直方圖左半邊最大值所對應的列數,即為左車道線所在的大致位置;找到直方圖右半邊最大值所對應的列數,即為右車道線所在的大致位置。

使用直方圖找左右車道線大致位置的代碼如下,其中leftx_base和rightx_base即為左右車道線所在列的大致位置。

# Take a histogram of the bottom half of the image histogram = np.sum(combined_binary[combined_binary.shape[0]//2:,:], axis=0) # Create an output image to draw on and visualize the result out_img = np.dstack((combined_binary, combined_binary, combined_binary)) # 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

確定了左右車道線的大致位置后,使用一種叫做“滑動窗口”的技術,在圖中對左右車道線的點進行搜索。先看一個介紹"滑動窗口"原理的視頻(視頻大小1.18M)。

滑動窗口原理

首先根據前面介紹的直方圖方法,找到左右車道線的大致位置,將這兩個大致位置作為起始點。定義一個矩形區域,稱之為“窗口”(圖中棕色的部分),分別以兩個起始點作為窗口的下邊線中點,存儲所有在方塊中的白色點的橫坐標。

隨后對存儲的橫坐標取均值,將該均值所在的列以及第一個”窗口“的上邊緣所在的位置,作為下一個“窗口”的下邊線中點,繼續搜索。

以此往復,直到把所有的行都搜索完畢

所有落在窗口(圖中棕色區域)中的白點,即為左右車道線的待選點,如下圖藍色和紅色所示。隨后將藍色點和紅色點做三次曲線擬合,即可得到車道線的曲線方程。

使用直方圖、滑動窗口檢測車道線的代碼如下:

# Step 5 : Detect lane lines through moving window def find_lane_pixels(binary_warped, nwindows, margin, minpix): # Take a histogram of the bottom half of the image histogram = np.sum(binary_warped[binary_warped.shape[0]//2:,:], axis=0) # Create an output image to draw on and visualize the result out_img = np.dstack((binary_warped, binary_warped, binary_warped)) # 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 # Set height of windows - based on nwindows above and image shape window_height = np.int(binary_warped.shape[0]//nwindows) # Identify the x and y positions of all nonzero pixels in the image nonzero = binary_warped.nonzero() nonzeroy = np.array(nonzero[0]) nonzerox = np.array(nonzero[1]) # Current positions to be updated later for each window in nwindows leftx_current = leftx_base rightx_current = rightx_base # Create empty lists to receive left and right lane pixel indices left_lane_inds = [] right_lane_inds = [] # Step through the windows one by one for window in range(nwindows): # 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 # Draw the windows on the visualization image cv2.rectangle(out_img,(win_xleft_low,win_y_low), (win_xleft_high,win_y_high),(0,255,0), 2) cv2.rectangle(out_img,(win_xright_low,win_y_low), (win_xright_high,win_y_high),(0,255,0), 2) # 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) ? ? ? ? ? ? ? ?# If you found > minpix pixels, recenter next window on their mean position if len(good_left_inds) > minpix: leftx_current = np.int(np.mean(nonzerox[good_left_inds])) if len(good_right_inds) > minpix: rightx_current = np.int(np.mean(nonzerox[good_right_inds])) # Concatenate the arrays of indices (previously was a list of lists of pixels) try: left_lane_inds = np.concatenate(left_lane_inds) right_lane_inds = np.concatenate(right_lane_inds) except ValueError: # Avoids an error if the above is not implemented fully pass # 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] return leftx, lefty, rightx, righty, out_img def fit_polynomial(binary_warped, nwindows=9, margin=100, minpix=50): # Find our lane pixels first leftx, lefty, rightx, righty, out_img = find_lane_pixels( binary_warped, nwindows, margin, minpix) # Fit a second order polynomial to each using `np.polyfit` left_fit = np.polyfit(lefty, leftx, 2) right_fit = np.polyfit(righty, rightx, 2) # Generate x and y values for plotting ploty = np.linspace(0, binary_warped.shape[0]-1, binary_warped.shape[0] ) try: left_fitx = left_fit[0]*ploty**2 + left_fit[1]*ploty + left_fit[2] right_fitx = right_fit[0]*ploty**2 + right_fit[1]*ploty + right_fit[2] except TypeError: # Avoids an error if `left` and `right_fit` are still none or incorrect print('The function failed to fit a line!') left_fitx = 1*ploty**2 + 1*ploty right_fitx = 1*ploty**2 + 1*ploty ## Visualization ## # Colors in the left and right lane regions out_img[lefty, leftx] = [255, 0, 0] out_img[righty, rightx] = [0, 0, 255] # Plots the left and right polynomials on the lane lines #plt.plot(left_fitx, ploty, color='yellow') #plt.plot(right_fitx, ploty, color='yellow') return out_img, left_fit, right_fit, ploty

對以上6種工況進行車道線檢測,處理結果如下圖所示。

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

跟蹤車道線

視頻數據是連續的圖片,基于連續兩幀圖像中的車道線不會突變的先驗知識,我們可以使用上一幀檢測到的車道線結果,作為下一幀圖像處理的輸入,搜索上一幀車道線檢測結果附近的點,這樣不僅可以減少計算量,而且得到的車道線結果也更穩定,如下圖所示。

圖片出處:優達學城(Udacity)無人駕駛工程師學位

圖中的細黃線為上一幀檢測到的車道線結果,綠色陰影區域為細黃線橫向擴展的一個區域,通過搜索該區域內的白點坐標,即可快速確定當前幀中左右車道線的待選點。

使用上一幀的車道線檢測結果進行車道線跟蹤的代碼如下:

# Step 6 : Track lane lines based the latest lane line result def fit_poly(img_shape, leftx, lefty, rightx, righty): ### TO-DO: Fit a second order polynomial to each with np.polyfit() ### left_fit = np.polyfit(lefty, leftx, 2) right_fit = np.polyfit(righty, rightx, 2) # Generate x and y values for plotting ploty = np.linspace(0, img_shape[0]-1, img_shape[0]) ### TO-DO: Calc both polynomials using ploty, left_fit and right_fit ### left_fitx = left_fit[0]*ploty**2 + left_fit[1]*ploty + left_fit[2] right_fitx = right_fit[0]*ploty**2 + right_fit[1]*ploty + right_fit[2] return left_fitx, right_fitx, ploty, left_fit, right_fit def search_around_poly(binary_warped, left_fit, right_fit): # HYPERPARAMETER # Choose the width of the margin around the previous polynomial to search # The quiz grader expects 100 here, but feel free to tune on your own! margin = 60 # Grab activated pixels nonzero = binary_warped.nonzero() nonzeroy = np.array(nonzero[0]) nonzerox = np.array(nonzero[1]) ### TO-DO: Set the area of search based on activated x-values ### ### within the +/- margin of our polynomial function ### ### Hint: consider the window areas for the similarly named variables ### ### in the previous quiz, but change the windows to our new search area ### left_lane_inds = ((nonzerox > (left_fit[0]*(nonzeroy**2) + left_fit[1]*nonzeroy + left_fit[2] - margin)) & (nonzerox < (left_fit[0]*(nonzeroy**2) + ? ? ? ? ? ? ? ? ? ?left_fit[1]*nonzeroy + left_fit[2] + margin))) ? ?right_lane_inds = ((nonzerox > (right_fit[0]*(nonzeroy**2) + right_fit[1]*nonzeroy + right_fit[2] - margin)) & (nonzerox < (right_fit[0]*(nonzeroy**2) + ? ? ? ? ? ? ? ? ? ?right_fit[1]*nonzeroy + right_fit[2] + margin))) ? ? ? ?# 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] ? ?# Fit new polynomials ? ?left_fitx, right_fitx, ploty, left_fit, right_fit = fit_poly(binary_warped.shape, leftx, lefty, rightx, righty) ? ? ? ?## Visualization ## ? ?# Create an image to draw on and an image to show the selection window ? ?out_img = np.dstack((binary_warped, binary_warped, binary_warped))*255 ? ?window_img = np.zeros_like(out_img) ? ?# Color in left and right line pixels ? ?out_img[nonzeroy[left_lane_inds], nonzerox[left_lane_inds]] = [255, 0, 0] ? ?out_img[nonzeroy[right_lane_inds], nonzerox[right_lane_inds]] = [0, 0, 255] ? ?# Generate a polygon to illustrate the search window area ? ?# And recast the x and y points into usable format for cv2.fillPoly() ? ?left_line_window1 = np.array([np.transpose(np.vstack([left_fitx-margin, ploty]))]) ? ?left_line_window2 = np.array([np.flipud(np.transpose(np.vstack([left_fitx+margin, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ploty])))]) ? ?left_line_pts = np.hstack((left_line_window1, left_line_window2)) ? ?right_line_window1 = np.array([np.transpose(np.vstack([right_fitx-margin, ploty]))]) ? ?right_line_window2 = np.array([np.flipud(np.transpose(np.vstack([right_fitx+margin, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ploty])))]) ? ?right_line_pts = np.hstack((right_line_window1, right_line_window2)) ? ?# Draw the lane onto the warped blank image ? ?cv2.fillPoly(window_img, np.int_([left_line_pts]), (0,255, 0)) ? ?cv2.fillPoly(window_img, np.int_([right_line_pts]), (0,255, 0)) ? ?result = cv2.addWeighted(out_img, 1, window_img, 0.3, 0) ? ? ? ?# Plot the polynomial lines onto the image ? ?#plt.plot(left_fitx, ploty, color='yellow') ? ?#plt.plot(right_fitx, ploty, color='yellow') ? ?## End visualization steps ## ? ? ? ?return result, left_fit, right_fit, ploty

對以上6種工況進行車道線跟蹤,處理結果如下圖所示。

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

以上,我們就完成了在透視變換結果上的車道線檢測和跟蹤。

逆投影到原圖

我們在計算透視變換矩陣時計算了兩個矩陣M和Minv,使用M能夠實現透視變換,使用Minv能夠實現逆透視變換。

M = cv2.getPerspectiveTransform(src, dst) Minv = cv2.getPerspectiveTransform(dst, src)

我們將兩條車道線所圍成的區域涂成綠色,并將結果繪制在“鳥瞰圖”上后,使用逆透視變換矩陣反投到原圖上,即可實現在原圖上的可視化效果。代碼如下:

# Step 7 : Draw lane line result on undistorted image def drawing(undist, bin_warped, color_warp, left_fitx, right_fitx): # Create an image to draw the lines on warp_zero = np.zeros_like(bin_warped).astype(np.uint8) color_warp = np.dstack((warp_zero, warp_zero, warp_zero)) # Recast the x and y points into usable format for cv2.fillPoly() pts_left = np.array([np.transpose(np.vstack([left_fitx, ploty]))]) pts_right = np.array([np.flipud(np.transpose(np.vstack([right_fitx, ploty])))]) pts = np.hstack((pts_left, pts_right)) # Draw the lane onto the warped blank image cv2.fillPoly(color_warp, np.int_([pts]), (0,255, 0)) # Warp the blank back to original image space using inverse perspective matrix (Minv) newwarp = cv2.warpPerspective(color_warp, Minv, (undist.shape[1], undist.shape[0])) # Combine the result with the original image result = cv2.addWeighted(undist, 1, newwarp, 0.3, 0) return result

以上6個場景的左右車道線繪制結果如下所示:

無陰影、顏色無明暗變換的直道

無陰影、顏色無明暗變換的彎道

有小面積陰影、顏色由暗到亮的直道

無陰影、道路標志線不清晰的彎道

有大面積陰影、顏色由暗到亮的彎道

有大面積陰影、顏色由亮到暗的彎道

處理視頻

在一步步完成攝像機標定、圖像畸變校正、透視變換、提取車道線、檢測車道線、跟蹤車道線后,我們在圖像上實現了復雜環境下的車道線檢測算法。現在我們將視頻轉化為圖片,然后一幀幀地對視頻數據進行處理,然后將車道線檢測結果存為另一段視頻。

高級車道線檢測算法效果

視頻中左上角出現的道路曲率和車道偏離量的計算都是獲取車道線曲線方程后的具體應用,這里不做詳細討論。

結語

以上就是《再識圖像之高級車道線檢測》的全部內容,本次分享中介紹的攝像機標定、投影變換、顏色通道、滑動窗口等技術,在計算機視覺領域均得到了廣泛應用。

處理復雜道路場景下的視頻數據是一項及其艱巨的任務。僅以提取車道線的過程為例,使用設定規則的方式提取車道線,雖然能夠處理項目視頻中的場景,但面對變化更為惡劣的場景時,還是無能為力。現階段解決該問題的方法就是通過深度學習的方法,拿足夠多的標注數據去訓練模型,才能盡可能多地達到穩定的檢測效果。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 無人駕駛
    +關注

    關注

    99

    文章

    4172

    瀏覽量

    123491

原文標題:無人駕駛技術入門 —— 再識圖像之高級車道線檢測

文章出處:【微信號:IV_Technology,微信公眾號:智車科技】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    低空物流:無人機開啟未來配送新篇章

    ,解決“最后一公里”配送難題 02齊發力 中國在低空物流領域已走上快車道,政策、技術和企業三駕馬車齊發力: 政策加持:2023年《無人駕駛航空器飛行管理暫行條例》為低空物流松綁,深圳、上海、成都
    發表于 07-04 10:42

    無人駕駛技術未來在哪里?低速才是突破口

    ? 無人駕駛技術這幾年從實驗室迅速走向現實。在大眾印象無人駕駛常被等同于在城市道路上自由穿梭的智能汽車,但實際率先實現商業化落地的,是低速無人駕
    的頭像 發表于 05-23 15:38 ?235次閱讀
    <b class='flag-5'>無人駕駛</b><b class='flag-5'>技術</b>未來在哪里?低速才是突破口

    易控智駕無人駕駛技術再次取得新突破

    近日,易控智駕無人駕駛技術再次取得新突破,“混編+混行”技術已在國內多個礦區成功落地并常態化“下人”運行,成為目前行業內唯一實現無人礦卡與人工礦卡在“裝載區混裝+道路混行+卸載區混卸”
    的頭像 發表于 05-09 15:44 ?984次閱讀

    派歌銳無人駕駛車輛束:智能駕駛的神經網絡

    派歌銳是專業的新能源束解決方案提供商,為無人駕駛車輛提供穩定可靠的束解決方案
    的頭像 發表于 04-25 17:21 ?275次閱讀

    易控智駕發布礦山無人駕駛應用落地成果

    近日,“易路相伴 智約共贏”無人駕駛礦用車規模化應用成果發布會在三亞順利召開。作為全球領先的礦山無人駕駛公司,易控智駕發布了礦山無人駕駛應用落地成果,成為行業首個突破落地1000臺無人駕駛
    的頭像 發表于 03-04 11:25 ?588次閱讀

    為什么聊自動駕駛的越來越多,聊無人駕駛的越來越少?

    無人駕駛”與“自動駕駛”,傻傻分不清楚?就在之前的一篇文章,引用了王傳福的一句話,其說的是無人駕駛是“扯淡”( 相關閱讀: 無人駕駛是“
    的頭像 發表于 02-23 10:52 ?550次閱讀
    為什么聊自動<b class='flag-5'>駕駛</b>的越來越多,聊<b class='flag-5'>無人駕駛</b>的越來越少?

    易控智駕無人駕駛技術深度賦能首鋼集團水廠鐵礦項目

    ?日前,易控智駕無人駕駛技術賦能的首鋼集團水廠鐵礦項目順利通過客戶驗收。該項目2024年11月便已滿足客戶要求的效率指標,并實現客戶獨立自主運行。
    的頭像 發表于 02-19 09:31 ?589次閱讀

    魯渝能源無線充電技術在低速無人駕駛環衛車的創新應用

    低速無人駕駛環衛車采用無線充電技術提升智能化與綠色化,面臨成本、效率、自由度與環境適應性挑戰。青島魯渝能源創新技術,實現高效充電、多車協同,為城市智能化和綠色化提供支持。
    的頭像 發表于 01-04 10:34 ?497次閱讀

    【實戰】Python+OpenCV車道檢測識別項目:實現L2級別自動駕駛必備(配套課程+平臺實踐)

    的一個必備技能——車道檢測。本文將詳細介紹一個車道檢測項目的過程,從圖像采集到
    的頭像 發表于 12-16 15:42 ?1073次閱讀
    【實戰】Python+OpenCV<b class='flag-5'>車道</b><b class='flag-5'>線</b><b class='flag-5'>檢測</b>識別項目:實現L2級別自動<b class='flag-5'>駕駛</b>必備(配套課程+平臺實踐)

    測速雷達與無人駕駛技術的結合 測速雷達故障排除技巧

    測速雷達與無人駕駛技術的結合 測速雷達作為無人駕駛汽車環境感知系統的核心技術之一,發揮著至關重要的作用。它通過發射無線電波并接收其反射信號,能夠精確測量車輛的速度,并獲取周圍環境
    的頭像 發表于 12-05 17:18 ?1023次閱讀

    UWB模塊如何助力無人駕駛技術

    ,它使用超過500MHz的帶寬進行數據傳輸。與傳統的窄帶通信技術相比,UWB技術具有以下特點: 高精度定位 :UWB技術能夠提供厘米級的定位精度,這對于無人駕駛車輛在復雜環境
    的頭像 發表于 10-31 14:05 ?903次閱讀

    無線充電技術為低速無人駕駛清掃車注入無限動力

    無人駕駛清掃機器人采用無線充電技術,提高運行效率、安全性和耐候性,降低維護成本。青島魯渝能源推出專為低速無人駕駛車設計的無線充電器,快速高效且安全,支持智能優化充電,助力智慧城市發展。
    的頭像 發表于 10-25 10:53 ?672次閱讀

    MT6825磁編碼IC在無人駕駛電動收割機的應用

    隨著科技的飛速發展,無人駕駛技術正在逐漸滲透到各個行業,其中農業領域尤為引人注目。無人駕駛電動收割機的出現,不僅提高了農業生產的效率,也降低了農民的勞動強度,使得農業生產更加智能化、
    的頭像 發表于 07-25 16:47 ?710次閱讀

    5G賦能車聯網,無人駕駛引領未來出行

    無人駕駛車聯網應用已成為智能交通領域的重要發展趨勢。隨著無人駕駛技術的不斷進步和5G網絡的廣泛部署,5G工業路由器在無人駕駛車聯網的應用日
    的頭像 發表于 07-24 10:10 ?1193次閱讀
    5G賦能車聯網,<b class='flag-5'>無人駕駛</b>引領未來出行