導(dǎo)讀本文主要介紹如何使用 Python 和 OpenCV實(shí)現(xiàn)一個(gè)實(shí)時(shí)曲線道路檢測(cè)系統(tǒng)。(公眾號(hào):OpenCV與AI深度學(xué)習(xí))
背景介紹
在任何駕駛場(chǎng)景中,車(chē)道線都是指示交通流量和車(chē)輛應(yīng)行駛位置的重要組成部分。這也是開(kāi)發(fā)自動(dòng)駕駛汽車(chē)的一個(gè)很好的起點(diǎn)!在我之前的車(chē)道檢測(cè)項(xiàng)目的基礎(chǔ)上,我實(shí)現(xiàn)了一個(gè)曲線車(chē)道檢測(cè)系統(tǒng),該系統(tǒng)工作得更好,并且對(duì)具有挑戰(zhàn)性的環(huán)境更加穩(wěn)健。車(chē)道檢測(cè)系統(tǒng)是使用 OpenCV 庫(kù)用 Python 編寫(xiě)的。下面是實(shí)現(xiàn)步驟:
- 畸變校正
- 透視變換
- Sobel濾波
- 直方圖峰值檢測(cè)
- 滑動(dòng)窗口搜索
- 曲線擬合
- 覆蓋檢測(cè)車(chē)道
- 應(yīng)用于視頻
畸變矯正
相機(jī)鏡頭扭曲入射光以將其聚焦在相機(jī)傳感器上。盡管這對(duì)于我們捕捉環(huán)境圖像非常有用,但它們最終往往會(huì)稍微不準(zhǔn)確地扭曲光線。這可能導(dǎo)致計(jì)算機(jī)視覺(jué)應(yīng)用中的測(cè)量不準(zhǔn)確。然而,我們可以很容易地糾正這種失真。我們可以使用棋盤(pán)格來(lái)標(biāo)定相機(jī)然后做畸變校正:
測(cè)試視頻中使用的相機(jī)用于拍攝棋盤(pán)格的 20 張照片,用于生成畸變模型。我們首先將圖像轉(zhuǎn)換為灰度,然后應(yīng)用cv2.findChessboardCorners()函數(shù)。我們已經(jīng)知道這個(gè)棋盤(pán)是一個(gè)只有直線的二維對(duì)象,所以我們可以對(duì)檢測(cè)到的角應(yīng)用一些變換來(lái)正確對(duì)齊它們。用cv2.CalibrateCamera()來(lái)獲取畸變系數(shù)和相機(jī)矩陣。相機(jī)已校準(zhǔn)!
然后,您可以使用它cv2.undistort()來(lái)矯正其余的輸入數(shù)據(jù)。您可以在下面看到棋盤(pán)的原始圖像和校正后的圖像之間的差異:
實(shí)現(xiàn)代碼:
def undistort_img():
# Prepare object points 0,0,0 ... 8,5,0
obj_pts = np.zeros((6*9,3), np.float32)
obj_pts[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1,2)
# Stores all object points & img points from all images
objpoints = []
imgpoints = []
# Get directory for all calibration images
images = glob.glob('camera_cal/*.jpg')
for indx, fname in enumerate(images):
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (9,6), None)
if ret == True:
objpoints.append(obj_pts)
imgpoints.append(corners)
# Test undistortion on img
img_size = (img.shape[1], img.shape[0])
# Calibrate camera
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None,None)
dst = cv2.undistort(img, mtx, dist, None, mtx)
# Save camera calibration for later use
dist_pickle = {}
dist_pickle['mtx'] = mtx
dist_pickle['dist'] = dist
pickle.dump( dist_pickle, open('camera_cal/cal_pickle.p', 'wb') )
def undistort(img, cal_dir='camera_cal/cal_pickle.p'):
#cv2.imwrite('camera_cal/test_cal.jpg', dst)
with open(cal_dir, mode='rb') as f:
file = pickle.load(f) mtx = file['mtx']
dist = file['dist']
dst = cv2.undistort(img, mtx, dist, None, mtx)
return dst
undistort_img()
img = cv2.imread('camera_cal/calibration1.jpg')
dst = undistort(img) # Undistorted image
這是應(yīng)用于道路圖像的失真校正。您可能無(wú)法注意到細(xì)微的差異,但它會(huì)對(duì)圖像處理產(chǎn)生巨大影響。
透視變換
在相機(jī)空間中檢測(cè)彎曲車(chē)道并不是很容易。如果我們想鳥(niǎo)瞰車(chē)道怎么辦?這可以通過(guò)對(duì)圖像應(yīng)用透視變換來(lái)完成。這是它的樣子:
注意到什么了嗎?通過(guò)假設(shè)車(chē)道位于平坦的 2D 表面上,我們可以擬合一個(gè)多項(xiàng)式,該多項(xiàng)式可以準(zhǔn)確地表示車(chē)道空間中的車(chē)道!這不是很酷嗎?
您可以使用cv2.getPerspectiveTransform()函數(shù)將這些變換應(yīng)用于任何圖像,以獲取變換矩陣,并將cv2.warpPerspective()其應(yīng)用于圖像。下面是代碼:
def perspective_warp(img,
dst_size=(1280,720),
src=np.float32([(0.43,0.65),(0.58,0.65),(0.1,1),(1,1)]),
dst=np.float32([(0,0), (1, 0), (0,1), (1,1)])):
img_size = np.float32([(img.shape[1],img.shape[0])])
src = src* img_size
# For destination points, I'm arbitrarily choosing some points to be
# a nice fit for displaying our warped result
# again, not exact, but close enough for our purposes
dst = dst * np.float32(dst_size)
# Given src and dst points, calculate the perspective transform matrix
M = cv2.getPerspectiveTransform(src, dst)
# Warp the image using OpenCV warpPerspective()
warped = cv2.warpPerspective(img, M, dst_size)
return warped
Sobel濾波
在之前的版本中,我使用顏色過(guò)濾掉了車(chē)道線。然而,這并不總是最好的選擇。如果道路使用淺色混凝土代替瀝青,道路很容易通過(guò)彩色濾光片,管道會(huì)將其感知為白色車(chē)道線,此方法不夠穩(wěn)健。
相反,我們可以使用類(lèi)似于邊緣檢測(cè)器的方法,這次過(guò)濾掉道路。車(chē)道線通常與道路具有高對(duì)比度,因此我們可以利用這一點(diǎn)。之前版本 1 中使用的Canny邊緣檢測(cè)器利用Sobel 算子來(lái)獲取圖像函數(shù)的梯度。OpenCV 文檔對(duì)它的工作原理有很好的解釋。我們將使用它來(lái)檢測(cè)高對(duì)比度區(qū)域以過(guò)濾車(chē)道標(biāo)記并忽略道路。
我們?nèi)詫⒃俅问褂?HLS 色彩空間,這一次是為了檢測(cè)飽和度和亮度的變化。sobel 算子應(yīng)用于這兩個(gè)通道,我們提取相對(duì)于 x 軸的梯度,并將通過(guò)梯度閾值的像素添加到表示圖像中像素的二進(jìn)制矩陣中。這是它在相機(jī)空間和車(chē)道空間中的樣子:
請(qǐng)注意,遠(yuǎn)離相機(jī)的圖像部分不能很好地保持其質(zhì)量。由于相機(jī)的分辨率限制,來(lái)自更遠(yuǎn)物體的數(shù)據(jù)非常模糊和嘈雜。我們不需要專(zhuān)注于整個(gè)圖像,所以我們可以只使用它的一部分。這是我們將使用的圖像的樣子(ROI):
直方圖峰值檢測(cè)
我們將應(yīng)用一種稱(chēng)為滑動(dòng)窗口算法的特殊算法來(lái)檢測(cè)我們的車(chē)道線。但是,在我們應(yīng)用它之前,我們需要為算法確定一個(gè)好的起點(diǎn)。如果它從存在車(chē)道像素的位置開(kāi)始,它會(huì)很好地工作,但是我們?nèi)绾问紫葯z測(cè)這些車(chē)道像素的位置呢?其實(shí)很簡(jiǎn)單!
我們將獲得圖像相對(duì)于 X 軸的直方圖。下面直方圖的每個(gè)部分都顯示了圖像每列中有多少個(gè)白色像素。然后我們?nèi)D像每一側(cè)的最高峰,每條車(chē)道線一個(gè)。這是直方圖的樣子,在二值圖像旁邊:
滑動(dòng)窗口搜索
滑動(dòng)窗口算法將用于區(qū)分左右車(chē)道邊界,以便我們可以擬合代表車(chē)道邊界的兩條不同曲線。
算法本身非常簡(jiǎn)單。從初始位置開(kāi)始,第一個(gè)窗口測(cè)量有多少像素位于窗口內(nèi)。如果像素?cái)?shù)量達(dá)到某個(gè)閾值,它將下一個(gè)窗口移動(dòng)到檢測(cè)到的像素的平均橫向位置。如果沒(méi)有檢測(cè)到足夠的像素,則下一個(gè)窗口從相同的橫向位置開(kāi)始。這一直持續(xù)到窗口到達(dá)圖像的另一邊緣。
落在窗口內(nèi)的像素被賦予一個(gè)標(biāo)記。在下圖中,藍(lán)色標(biāo)記的像素代表右側(cè)車(chē)道,紅色標(biāo)記的像素代表左側(cè):
曲線擬合
項(xiàng)目的其余部分非常簡(jiǎn)單。我們分別使用 對(duì)紅色和藍(lán)色像素應(yīng)用多項(xiàng)式回歸np.polyfit(),然后檢測(cè)器就完成了!
這是曲線的樣子:
繪制檢測(cè)車(chē)道
這是檢測(cè)系統(tǒng)的最后一部分,用戶(hù)界面!我們只需創(chuàng)建一個(gè)覆蓋層來(lái)填充檢測(cè)到的車(chē)道部分,然后我們最終可以將其應(yīng)用于視頻。一旦應(yīng)用于視頻檢測(cè),您應(yīng)該會(huì)看到以下輸出:
結(jié)論
就是這樣,一個(gè)基本的彎曲車(chē)道檢測(cè)器!它比以前的版本好得多,它甚至可以處理彎曲的車(chē)道!但是,它仍然會(huì)在一定程度上受到陰影和道路紋理劇烈變化的影響。在我的下一個(gè)車(chē)道檢測(cè)項(xiàng)目中,我們將使用一些機(jī)器學(xué)習(xí)技術(shù)來(lái)開(kāi)發(fā)一個(gè)非常強(qiáng)大的車(chē)道和車(chē)輛檢測(cè)系統(tǒng),謝謝!
完整代碼:
https://github.com/kemfic/Curved-Lane-Lines/blob/master/P4.ipynb
參考鏈接:
https://www.hackster.io/kemfic/curved-lane-detection-34f771
-
檢測(cè)器
+關(guān)注
關(guān)注
1文章
868瀏覽量
47733 -
機(jī)器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8428瀏覽量
132835 -
OpenCV
+關(guān)注
關(guān)注
31文章
635瀏覽量
41419
原文標(biāo)題:實(shí)戰(zhàn)|OpenCV實(shí)時(shí)彎道檢測(cè)(詳細(xì)步驟+源碼)
文章出處:【微信號(hào):vision263com,微信公眾號(hào):新機(jī)器視覺(jué)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論