1. 程式人生 > 其它 >讓車輛“學會”識別車道:使用計算機視覺進行車道檢測

讓車輛“學會”識別車道:使用計算機視覺進行車道檢測

所有人在開車時都要注意識別車道,確保車輛行駛時在車道的限制範圍內,保證交通順暢,並儘量減少與附近車道上其他車輛相撞的機率。對於自動駕駛車輛來說,這是一個關鍵任務。事實證明,使用計算機視覺技術可以識別道路上的車道標記。我們將介紹如何使用各種技術來識別和繪製車道的內部,計算車道的曲率,甚至估計車輛相對於車道中心的位置。

為了檢測和繪製一個多邊形(採用汽車當前所在車道的形狀),我們構建了一個管道,由以下步驟組成:

  • 一組棋盤影象的攝像機標定矩陣和畸變係數的計算
  • 影象失真去除;
  • 在車道線路上應用顏色和梯度閾值;
  • 通過透視變換製作鳥瞰圖;
  • 使用滑動視窗尋找熱車道線畫素(hot lane line pixels);
  • 二度多項式擬合,以確定構成車道的左右線;
  • 車道曲率和車道中心偏離的計算;
  • 影象中車道邊界的變形和繪製,以及車道曲率資訊。

我相信一張圖勝過千言萬語,看下圖:

攝像機標定和影象失真去除

第一步是找到校準矩陣,以及用於拍攝道路照片的相機的畸變係數。這是非常必要的,因為相機鏡頭的凸形曲線在進入針孔時彎曲光線,從而造成了真實影象的扭曲。因此,真實世界裡的直線在我們的照片中可能不再是直線了。

為了計算相機的變換矩陣和畸變係數,我們在同一相機拍攝的平面上使用了多個棋盤影象。OpenCV有一種簡便的方法叫做findChessboardCorners ,它可以識別出黑白方塊相交的點,並以這種方式逆向工程畸變矩陣。下圖顯示了示例影象上所標識的棋盤角:

我們可以看到,角點被很好地識別了。接下來,我們在從不同角度拍攝的多個棋盤影象上執行棋盤查詢演算法,通過識別影象和物體點來校準相機。前者是指二維對映中的座標,後者表示三維空間中的影象點的真實座標(棋盤影象的z軸,或深度= 0)。這些對映使我們能夠正確的消除由同一照相機拍攝造成的影象失真。你可以在下圖中看到它的效果:

我們現在可以反轉所有影象失真,如下圖所示:

閾值

我們在這一節中應用顏色和邊緣閾值來更好地檢測線,更容易找到最好的描述左右通道的多項式。

我們首先開始探索應該採用哪些顏色空間來增加探測車道的機會,並促進梯度閾步驟的任務。

顏色閾值

我們用不同的顏色空間進行實驗,看看在最有效的車道線路分離上應該使用哪些顏色空間和通道:

在RGB元件上,我們看到藍色通道在識別黃線時最差,而紅色通道似乎給出了最佳效果。

對於HLS和HSV,顏色相通道產生非常雜亂的輸出,而HLS的飽和通道似乎給出了不錯的結果; 它優於HSV的飽和通道。 相反,HSV的亮度通道給出了非常清晰的灰度影象,特別是在黃線上,比HLS的亮度通道好得多。

LAB的A通道表現差強人意,而B通道在識別黃線方面很強。但它是識別黃線和白線的亮度通道(沒有雙關語)。

在這個階段,我們面臨各種有利弊的選擇。我們的目標是在給定的顏色通道上找到正確的閾值,以突顯車道的黃線和白線。實際上有很多方法可以實現這個結果,我們選擇使用HLS,是因為我們已經知道如何設定專案1:簡單通道檢測中的黃色和白色通道線的閾值。

簡單通道檢測地址:https://github.com/kenshiro-o/CarND-LaneLines-P1

下面的程式碼展示了我們如何在HLS上為白色和黃色(我們的車道顏色)設定閾值,併產生二進位制影象:

def compute_hls_white_yellow_binary(rgb_img):
    """
    Returns a binary thresholded image produced retaining only white and yellow elements on the picture
    The provided image should be in RGB format
    """
    hls_img= to_hls(rgb_img)

    # Compute a binary thresholded image where yellow is isolated from HLS components
    img_hls_yellow_bin= np.zeros_like(hls_img[:,:,0])
    img_hls_yellow_bin[((hls_img[:,:,0] >= 15) & (hls_img[:,:,0] <= 35))
                 & ((hls_img[:,:,1] >= 30) & (hls_img[:,:,1] <= 204))
                 & ((hls_img[:,:,2] >= 115) & (hls_img[:,:,2] <= 255))               
                ]= 1

    # Compute a binary thresholded image where white is isolated from HLS components
    img_hls_white_bin= np.zeros_like(hls_img[:,:,0])
    img_hls_white_bin[((hls_img[:,:,0] >= 0) & (hls_img[:,:,0] <= 255))
                 & ((hls_img[:,:,1] >= 200) & (hls_img[:,:,1] <= 255))
                 & ((hls_img[:,:,2] >= 0) & (hls_img[:,:,2] <= 255))               
                ]= 1

    # Now combine both
    img_hls_white_yellow_bin= np.zeros_like(hls_img[:,:,0])
    img_hls_white_yellow_bin[(img_hls_yellow_bin== 1) | (img_hls_white_bin== 1)]= 1

    return img_hls_white_yellow_bin

結果如下:

正如在上面看到的,HLS顏色閾值在影象上取得了很好的效果。閾值不能確定前面樹的陰影下的黃線。我們相信在這種情況下,梯度閾值可以起到幫助作用。

梯度閾值

我們使用Sobel運算子來識別梯度,它表示影象中顏色強度的變化。較高的值表示強烈的顏色變化。

我們已經決定使用LAB的L通道作為我們的單通道影象,作為下面的sobel函式的輸入。

我們嘗試了許多不同的引數和不同的Sobel操作,並得出了最終的結果:

Sobel操作地址:https://github.com/kenshiro-o/CarND-Advanced-Lane-Lines/blob/master/notebook.ipynb

我們選取底部的第二幅影象作為最佳結果。請注意,我們在選擇的影象上應用了一個15×15畫素的核心,從而有效地平滑了畫素,生成了一個更乾淨的二進位制影象。

結合兩種方法

我們結合了顏色和Sobel閾值二進位制影象,並得到以下結果:

在左邊的影象中,所有的綠色畫素都被Sobel閾值所保留,而藍色的畫素被HLS顏色閾值識別。結果非常令人鼓舞,我們似乎找到了正確的引數去檢測車道。我們把視線轉換到我們的影象上,併產生車道鳥瞰圖。

透視轉換

我們現在需要在二維影象中定義一個梯形區域,它將經過一個透視轉換,轉換成鳥的視角,如下圖所示:

然後我們定義4個額外的點,將其組成一個矩形,這將對映到源梯形中的畫素上:

dst_pts= np.array([[200, bottom_px], [200,0], [1000,0], [1000, bottom_px]], np.float32)

透視變換產生以下型別的影象:

將所有的東西結合在一起

我們可以看到,我們的視角轉換將保持直線,這是一個必要的完整性檢查。然而,以上示例的曲線並不完美,但它們不應該為我們的演算法帶來無法克服的問題。

我們現在可以將閾值應用到我們的鳥瞰圖上:

直方圖

然後,我們在影象的下半部分計算y方向的二進位制閾值影象的直方圖,以識別畫素強度最高的x位置:

找到線路並繪製車道區域

滑動視窗

由於我們現在知道畫素的起始x位置(從影象的底部)最有可能產生一條車道線,我們將執行一個滑動視窗搜尋,試圖“捕獲”車道線的畫素座標。

滑動視窗搜尋地址:https://www.coursera.org/learn/machine-learning/lecture/bQhq3/sliding-windows

我們通過numpy的polyfit簡單地計算二級多項式,以找到最適合左右線路的曲線係數。

polyfit地址:https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.polyfit.html

改進演算法的一種方法是將儲存先前計算過的幀t – 1的係數,並嘗試從這些係數中找到我們的車道畫素。當我們沒有找到足夠的車道畫素(少於85%的非零畫素)時,我們就會回到滑動視窗搜尋,以幫助我們在車道上擬合更好的曲線。

車道曲率

我們也通過計算最小圓的半徑來計算車道曲率,該圓可以與我們的車道線相切——在直行車道上,半徑將會很大。我們通過定義出適當的畫素高度對車道長度和畫素寬對車道寬度的比率,將畫素空間轉換為米(又稱實際單位):

# Height ratio: 32 meters / 720 px
self.ym_per_px= self.real_world_lane_size_meters[0]/ self.img_dimensions[0]
# Width ratio: 3.7 meters / 800 px
self.xm_per_px= self.real_world_lane_size_meters[1]/ self.lane_width_px

我試圖通過參考資源(資源地址:https://www.psychologicalscience.org/news/motr/lines-on-the-road-are-longer-than-you-think.html)的資料來手動估算鳥瞰圖上車道的長度:每次一輛汽車行駛超過40英尺(約12.2米)時。樣本影象中的鳥瞰圖大概覆蓋了32米。按照美國高速公路的標準,寬度仍為3.7米。你可以通過該連結(連結地址:https://www.intmath.com/applications-differentiation/8-radius-curvature.php)在曲率半徑的數學基礎上找到更多的資訊。

我們也可以通過抵消車道左、右線路的起始(即底部)座標的平均值,減去中間點作為偏移量,再乘以車道的象素與真實世界的寬度比,來計算出汽車與車道中心的距離。

展開繪製的車道區域

最後,我們將車道的內部繪製成綠色並展開影象,從鳥瞰圖到原始的無失真影象。此外,我們將這個大影象與我們的車道檢測演算法的小影象疊加在一起,以更好地感知框架上發生了什麼。我們還添加了關於車道曲率和車輛中心位置的文字資訊:

最終結果

下面的gif顯示我們已經構建了一個強大的車道探測管道。

另外,我在Youtube上上傳了一段視訊,在視訊中我繪製了車道,並添加了額外的資訊,比如車道曲率近似值。

視訊地址:https://www.youtube.com/watch?v=fJBHd5S6jgo&feature=youtu.be

結論

我們已經介紹瞭如何執行攝像機標定,顏色和梯度閾值,以及透視變換和滑動視窗來識別車道線。

我們相信這個專案需要很多改進,比如:

  • 用LAB和YUV顏色空間進行實驗來決定我們是否能產生更好的顏色閾值;
  • 使用卷積代替滑動視窗來識別熱畫素;
  • 產生一個前幀的線係數的指數移動平均值,當我們的畫素檢測失敗時使用它;
  • 更好地檢測畫素“捕獲”的異常(例如,一些非零畫素完全脫離了線路)並拒絕它們;
  • 應用其他本專案未涵蓋的相關計算機視覺技術。