從零開始學習無人駕駛技術 --- 車道檢測


 

從零開始學習無人駕駛技術 --- 車道檢測

 

前言

個人興趣愛好,最近在學習一些無人駕駛相關的技術,便萌生了循序漸進的寫一系列文章的想法,這是第一篇。文章主要會以Udacity為主線,綜合自己在學習過程中搜集的各種材料,取其精華,補其不足,力求通俗易懂,理論明確,實戰有效,即作為一個學習總結,potentially又可以幫助對無人駕駛有興趣但是零基礎的朋友們 —— 注意這里的零基礎是指未接觸過無人駕駛領域,本系列還是需要一些簡單的數學和機器學習知識。

因為本文是從零開始的第一篇,這里的車道檢測是基礎版本,需要滿足幾個先決條件:(1)無人車保持在同車道的高速路中行駛(2)車道線清晰可見(3)無人車與同車道內前車保持足夠遠的距離。

TLDR (or the take-away)

一個基礎版的車道檢測步驟主要分為以下幾點:

  • Gray Scale Transformation
  • Gaussian Smoothing
  • Canny Edge Detection
  • ROI (Region of Interest) Based Edge Filtering
  • Hough Transformation
  • Lane Extrapolation

代碼:Github鏈接

最后的效果如下視頻所示,其中紅線表示自動檢測到的車道。

車道檢測基礎版 - 騰訊視頻https://v.qq.com/x/page/u0377sik121.html

從圖片開始談起

無人車往往配備有數個camera,常見的情況是有一個camera固定在車的前方,用來perceive前方道路情況,生成視頻。計算機對該視頻進行分析,綜合其他sensor的信息,對車輛行為進行指導。視頻是由圖片組成,如果能夠成功檢測圖片上的車道,那我們就幾乎解決了車道檢測問題。下面是一張車輛行駛過程中的圖片,讓我們動手吧!

import matplotlib.image as mplimg img = mplimg.imread('lane.jpg') 


Gray Scale Transformation

這個變換比較簡單,是將RGB圖片轉換成灰度圖片,用來作為Canny Edge Detection的輸入。

import cv2 gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # Note that if you use cv2.imread() to read image, the image will # be in format BGR. 


Gaussian Smoothing

Gaussian Smoothing是對圖片apply一個Gaussian Filter,可以起到模糊圖片和消除噪聲的效果。其基本原理是重新計算圖片中每個點的值,計算時取該點及其附近點的加權平均,權重符合高斯分布。下圖左側展示了一個kernel_size = 5的Gaussian Filter,55是高斯分布的中心點,341是網格中所有值的和。假設網格矩陣為Q,圖片為I,新圖片為I',則:

I'_{ij} = \frac{1}{341}\sum^{i+2}_{m = i-2}\ \sum^{j+2}_{n=j-2} Q_{mn} I_{mn}

Gaussian Filter是一種低通過濾器,能夠抑制圖片中的高頻部分,而讓低頻部分順利通過。那什么是圖片的高頻部分呢?下圖給出了一個比較極端的例子。愛好攝影的朋友們都知道相機ISO適當時能夠得到右側圖片,畫質細膩;如果ISO過大,就會導致產生左側圖片,畫質差,噪點多。這些噪點就是圖片中的高頻部分,表示了像素值劇烈升高或降低。

介紹完了Gaussian Filter,現在可以將其應用到我們的灰度圖片上:

blur_ksize = 5 # Gaussian blur kernel size blur_gray = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0, 0) 

Canny Edge Detection

John F. Canny在1986年發明了Canny Edge Detection技術,其基本原理是對圖片中各個點求gradient,gradient較大的地方往往是edge。Canny Edge Detection精妙的地方在於它有兩個參數:low_threshold和high_threshold。算法先比較gradient與這兩個threshold的關系,如果gradient > high_threshold,就承認這是一個edge point;如果gradient < low_threshold,就斷定這不是edge point;對於其他的點,如果與edge point相連接,那么這個點被認為也是edge point,否則不是。

canny_lthreshold = 50 # Canny edge detection low threshold canny_hthreshold = 150 # Canny edge detection high threshold edges = cv2.Canny(blur_gray, low_threshold, high_threshold) 

ROI Based Edge Filtering

Woohoo! It's awesome! 經過了Canny Edge Detection,我們發現物體的輪廓都被檢測到了!但是似乎東西有點兒太多了… 沒關系,還有一個重要的條件沒有用:camera相對於車是固定的,而無人車相對於車道的左右位置也是基本固定的,所以車道在camera視頻中基本保持在一個固定區域內!據此我們可以畫出一個大概的Region of Interest (ROI),過濾掉ROI之外的edges。

def roi_mask(img, vertices): mask = np.zeros_like(img) mask_color = 255 cv2.fillPoly(mask, vertices, mask_color) masked_img = cv2.bitwise_and(img, mask) return masked_img roi_vtx = np.array([[(0, img.shape[0]), (460, 325), (520, 325), (img.shape[1], img.shape[0])]]) roi_edges = roi_mask(edges, roi_vtx) 

Hough Transformation

目前看起來我們似乎已經得到了車道線了呢,然而…並沒有! 因為最終目標是得到exactly兩條直線!而目前現在圖中不僅有多條線,還有一些點狀和塊狀區域,Hough Transformation的目的就是找到圖中的線。

下圖中左側是image space,中間和右側是Hough space。先看左側和中間的圖(右側圖見本節備注),image space中的一個點對應Hough space的一條線;image space中的兩個點(x_1 \neq x_2)對應Hough space的兩條相交線,且交點對應的線必經過image space的這兩個點。

那么,如果Hough space中有多條線相交於一點,則在image space中對應的那些點應位於同一條線上,例如:


在實際操作中,我們往往將Hough space划分為網格狀,如果經過一個格子的線的數目大於某threshold,我們認為這個經過這個格子的線在原image space對應的點應在同一條線上。具備了這些知識,我們可以用Hough Transformation來找線啦!

# Hough transform parameters rho = 1 theta = np.pi / 180 threshold = 15 min_line_length = 40 max_line_gap = 20 def draw_lines(img, lines, color=[255, 0, 0], thickness=2): for line in lines: for x1, y1, x2, y2 in line: cv2.line(img, (x1, y1), (x2, y2), color, thickness) def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap): lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap) line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8) draw_lines(line_img, lines) return line_img line_img = hough_lines(roi_edges, rho, theta, threshold, min_line_length, max_line_gap) 

備注:如果image space中兩個點x_1 = x_2,則其形成的直線斜率無限大,無法用中間的圖表示,可以采用右側的極坐標表示方式。

Lane Extrapolation

Almost there! 現在我們要根據得到的線計算出左車道和右車道,一種可以采用的步驟是:

  1. 根據斜率正負划分某條線屬於左車道或右車道
  2. 分別對左右車道線移除outlier:迭代計算各條線的斜率與斜率均值的差,逐一移除差值過大的線
  3. 分別對左右車道線的頂點集合做linear regression,得到最終車道。

因為這部分代碼有點兒多,就不貼在這里了,請參見我的Github代碼。結果如下:


最最后,我們將結果和原圖疊加:

cv2.addWeighted(img, 0.8, line_img, 1, 0) 

圖片任務完成!

回到視頻上

現在我們將前面的代碼打個包放到叫process_an_image的函數中,然后

from moviepy.editor import VideoFileClip output = 'video_1_sol.mp4' clip = VideoFileClip("video_1.mp4") out_clip = clip.fl_image(process_an_image) out_clip.write_videofile(output, audio=False) 

將代碼應用到三個不同的video上,看看結果!

車道檢測基礎版 - 騰訊視頻https://v.qq.com/x/page/u0377sik121.html

車道檢測基礎版2 - 騰訊視頻https://v.qq.com/x/page/r037764yts3.html

車道檢測基礎版3 - 騰訊視頻https://v.qq.com/x/page/o0377jc77sr.html

注意:對於不同的情況,有些參數可能需要調節,第三個視頻的處理需要一些額外的操作,會在我的Github代碼中有具體描述。

參考資料

1. High ISO Noise Reduction

2. Gaussian Smoothing


免責聲明!

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



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