“?
在本章中,我將指導您構建一個簡單但有效的車道檢測管道,并將其應用于Carla 模擬器中捕獲的圖像。管道將圖像作為輸入,并產生車道邊界的數學模型作為輸出。圖像由行車記錄儀(固定在車輛擋風玻璃后面的攝像頭)捕獲。車道邊界模型是一個多項式
在這里,x𝑥和𝑦y以米為單位。它們在道路上定義了一個坐標系,如圖1所示。
管道由兩個步驟組成
-
使用神經網絡,檢測圖像中車道邊界的像素
-
將車道邊界像素與道路上的點關聯起來,
? ? ? ? 然后擬合多項式。
該方法的靈感來自參考文獻[?GBN+19?]中描述的“基線”方法,其性能接近最先進的車道檢測方法。
”
圖 1道路坐標系。該視角稱為鳥瞰圖。
01
—
圖像基礎
為了表示圖像,我們使用形狀為 (H,W,3) 的三維數組。我們稱該數組有 H 行、W 列和 3 個顏色通道(紅色、綠色和藍色)。讓我們用 Python 加載圖像并查看一下!
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
import cv2
img_fn = str(Path("images/carla_scene.png"))
img = cv2.imread(img_fn)
# opencv (cv2) stores colors in the order blue, green, red, but we want red, green, blue
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.xlabel("$u$") # horizontal pixel coordinate
plt.ylabel("$v$") # vertical pixel coordinate
print("(H,W,3)=",img.shape)
(H,W,3)= (512, 1024, 3)
讓我們檢查一下第 100 行和第 750 列的像素:
u,v = 750, 100
img[v,u]
array([28, 59, 28], dtype=uint8)
這意味著 處的像素具有紅色強度 28、綠色強度 59 和藍色強度 28。因此,它是綠色的。如果我們看一下圖像,這是有道理的,因為 處有一棵樹。此外,上面的輸出“?”告訴我們紅色、綠色和藍色強度存儲為 8 位無符號整數,即。因此,它們是 0 到 255 之間的整數。u,v?=?750,?100
u,v?=?750,?100
dtype=uint8
uint8
下面的示意圖總結了我們迄今為止學到的關于存儲數字光柵圖像的知識:
如果您的數字圖像是由相機拍攝的,那么數字圖像中的像素與相機圖像傳感器中的“傳感器像素”之間存在直接對應關系。
圖像傳感器由二維光電傳感器陣列組成。每個光電傳感器通過光電效應將入射光轉換為電能,然后通過模數轉換器將其轉換為數字信號。為了獲得顏色信息,一個“傳感器像素”被分成 2×2 的光電傳感器網格,并在這 4 個光電傳感器前面放置不同的顏色濾鏡。一個光電傳感器僅通過藍色濾鏡接收光,一個僅通過紅色濾鏡接收光,兩個通過綠色濾鏡接收光。將這 4 個測量值結合起來可得到一個顏色三重奏:。這就是所謂的拜耳濾鏡。(red_intensity,?green_intensity,?blue_intensity)
針孔相機模型
想象一下將圖像傳感器放在物體前面。
您將無法捕捉到如此清晰的圖像,因為圖像傳感器上的某個點會受到整個環境光線的照射。現在想象一下將圖像傳感器放在一個帶有非常小的針孔(也稱為光圈)的盒子里。
現在大部分光線都被擋住了,我們在圖像傳感器上得到了正確的圖像。圖像被上下翻轉了,但這不應該困擾我們。在理想針孔的情況下,圖像傳感器上的每個點都只會被來自外部的一束光線擊中。
理想針孔:一個很好的近似值
在現實世界中,孔的尺寸不能太小,因為進入盒子的光線不夠。此外,我們還會遭受衍射的影響。孔也不能太大,因為來自不同角度的光線會照射到圖像傳感器上的同一點,圖像會變得模糊。為了防止模糊,真實的相機中會安裝鏡頭。我們將在本節討論的針孔相機模型不包括鏡頭的影響。然而,事實證明,它非常接近帶鏡頭的相機,也是相機的事實模型。因此,在下文中,我們可以繼續將針孔視為理想的針孔。
下面的草圖介紹了圖像平面:
練習:將車道邊界投影到圖像
現在你應該開始做你的第一個練習了。對于這個練習,我為你準備了一些
數據。我使用安裝在車輛上的相機傳感器在 Carla 模擬器中捕捉到了一張
圖片:
參考
-
HZ03
-
Richard Hartley 和 Andrew Zisserman。計算機視覺中的多
視圖幾何。劍橋大學出版社,2003 年。
02
—
車道邊界分割
對于車道檢測流程,我們想要訓練一個神經網絡,該神經網絡會獲取一張圖像,并估計每個像素屬于左車道邊界的概率、屬于右車道邊界的概率以及不屬于任何車道邊界的概率。這個問題稱為語義分割。
先決
對于本節,我假設以下內容
-
你知道什么是神經網絡,并且之前自己訓練過一個
-
你知道語義分割的概念
如果你不滿足先決條件 1,我建議你查看以下免費資源之一
-
CS231n:用于視覺識別的卷積神經網絡
-
對于這門優秀的斯坦福課程,你可以在網上找到所有的學習材料。課程筆記還沒有完成,但確實存在的筆記非常好!請注意,當你點擊詳細課程大綱時,你可以看到所有講座的幻燈片。你可能想使用2017 年的版本,因為其中包含講座視頻。但是,對于練習,你應該使用2020版本(與 2017 年非常相似),因為你可以在Google Colab中進行編程。Google Colab 讓你可以在 Google 服務器上免費使用 GPU(深度學習所需的昂貴硬件)。即使你不想使用 Colab,2020 年的課程也有更好的本地工作說明(包括 anaconda)。對于你可以在 tensorflow 和 pytorch 之間選擇的練習,我建議你使用 pytorch。如果你真的渴望盡快回到這門課程,你可以在了解語義分割后停止 CS231n。
-
為程序員提供實用的深度學習
-
如果您的背景更多的是編碼而不是數學/科學,那么我推薦這門課程。您可以在這里找到視頻講座,在這里找到用 jupyter 筆記本編寫的書(如果您喜歡,還有一個印刷版本)。我建議使用 Google Colab 進行練習。fastai 課程使用 fastai 庫進行講授,該庫可幫助您使用很少的代碼行來訓練 pytorch 模型。即使您選擇不研究 fastai 課程,我也建議您查看fastai 庫,因為它使訓練模型變得非常容易。也許先從閱讀計算機視覺教程開始)。
關于先決條件 2 ,我推薦Jeremy Jordan 撰寫的這篇關于語義分割的非常好的博客文章(主要基于 CS231n)。
最后,你需要有 GPU 才能進行練習。但擁有GPU 并不是先決條件。你可以使用Google Colab,它允許你在 Google 服務器上運行 Python 代碼。要在 Colab 上訪問 GPU,你應該點擊“運行時”,然后點擊“更改運行時類型”,最后選擇“GPU”作為“硬件加速器”。有關如何使用 Colab 的更多詳細信息,請參閱附錄。
練習:訓練神經網絡進行車道邊界
車道分割模型應將形狀為 (512,1024,3) 的圖像作為輸入。這里,512 是圖像高度,1024 是圖像寬度,3 代表紅、綠、藍三個顏色通道。我們使用形狀為 (512,1024) 的輸入圖像和相應的標簽來訓練模型,其中label[v,u]
可以取值為 0、1 或 2,表示像素(𝑢,𝑣)是“無邊界”、“左邊界”還是“右邊界”。
模型的輸出應是output
形狀為 (512,1024,3) 的張量。
-
這個數字
output[v,u,0]
給出了像素(𝑢,𝑣)不屬于任何車道邊界的一部分。 -
這個數字
output[v,u,1]
給出了像素(𝑢,𝑣)是左車道邊界的一部分。 -
這個數字
output[v,u,2]
給出了像素(𝑢,𝑣)是右側車道邊界的一部分。 -
收集訓練
我們可以使用 Carla 模擬器收集訓練數據。我寫了一個
collect_data.py
腳本請注意,從四個數據項(圖像、車道邊界、交通矩陣、標簽圖像)中,只有圖像和標簽圖像對于訓練我們的深度學習模型是必要的。
所有數據均在“Town04”Carla 地圖上收集,因為這是唯一一張有可用高速公路的地圖(“Town06”的高速公路要么完全筆直,要么有 90 度轉彎)。為簡單起見,我們只為高速公路構建一個系統。因此,我們只使用地圖中道路曲率較低的部分,不包括城市道路。
地圖的一部分被任意選為“驗證區”。在此區域中創建的所有數據的文件名都添加了字符串“validation_set”。
現在您需要將一些訓練數據導入到您的機器上!我建議您下載我使用腳本為您創建的一些訓練數據
collect_data.py
。但如果您真的想要,您也可以自己收集數據。推薦:下載數據
只需繼續打開中的啟動代碼
code/exercises/lane_detection/lane_segmentation.ipynb
即可。它將有一個 Python 實用函數,可為您下載數據。替代方案:自己生成數據
-
存儲來自相機傳感器的圖像
-
存儲從 Carla 高清地圖獲得的車道邊界的世界坐標
-
存儲變換矩陣𝑇𝑐𝑤將世界坐標映射到相機參考系中的坐標
-
存儲標簽圖像,該圖像是根據車道邊界坐標和變換矩陣創建的,如上一節練習中所示
-
在 Carla 地圖上創建一輛車
-
將 RGB 攝像頭傳感器安裝到車輛上
-
將車輛移動到不同的位置并
-
-
模型
為了創建和訓練模型,您可以選擇任何您喜歡的深度學習框架。
如果你需要一些指導,我建議使用 fastai。你可以使用fastai 文檔中的語義分割示例,根據手頭的數據集稍微修改一下,它就可以正常工作了!如果你愿意,你可以得到一些提示:
沒有提示
好的,沒有提示。如果您遇到困難,請嘗試查看“有限提示”或“詳細提示”。
基本提示高級提示
存儲你的模型
您將需要訓練好的模型來進行接下來的練習。因此,請將訓練好的模型保存到磁盤。在 pytorch 中,您可以通過 執行此操作
torch.save
。對于 fastai,您可以執行torch.save(learn.model,?'./fastai_model.pth')
可選:參與 kaggle 活動
我為你準備的訓練數據也可以在kaggle上找到。如果你愿意,你可以用 kaggle 筆記本在線創建你的模型。他們還提供免費的 GPU 訪問。一旦你對你的解決方案感到滿意,可以考慮在 kaggle 上發布你的筆記本。我很想看到它😃。
具體指引詳見《https://thomasfermi.github.io/Algorithms-for-Automated-Driving/LaneDetection/Segmentation.html》
03
—
從像素轉換米
import numpy as np
import matplotlib.pyplot as plt
從車道邊界分割中,我們知道我們的語義分割模型將以攝像頭圖像作為輸入,并返回output
形狀為 (H,W,3) 的張量。具體來說,像素prob_left?=?output[v,u,1]
(𝑢,𝑣)是左車道邊界的一部分。我將output[v,u,1]
神經網絡為一些示例圖像計算的張量保存在 npy 文件中。讓我們來看看。
prob_left = np.load("../../data/prob_left.npy")
plt.imshow(prob_left, cmap="gray")
plt.xlabel("$u$");
plt.ylabel("$v$");
上圖顯示prob_left[v,u]
了每個(u,v)
。現在想象一下,(u,v,prob_left[v,u])
我們不是使用三元組,而是使用三元組(x,y,prob_left(x,y))
,其中(𝑥,𝑦)是道路上的坐標,如圖19所示。如果我們有這些三元組,我們可以過濾所有較大的(x,y,prob_left[x,y])
點prob_left[x,y]
。我們將獲得一個點列表(𝑥𝑖,𝑦𝑖)它們是左車道邊界的一部分,我們可以使用這些點來擬合多項式𝑦𝑙(𝑥)!但從 到(u,v,prob_left[v,u])
實際上(x,y,prob_left[x,y])
并不難,因為你uv_to_roadXYZ_roadframe_iso8855
在上一個練習中實現了該函數。此函數將(𝑢,𝑣)進入(𝑥,𝑦,𝑧)(注意𝑧=0對于道路像素)
這意味著我們可以開始編寫一些代碼來收集三元組(x,y,prob_left[x,y])
import sys
sys.path.append('../../code')
from solutions.lane_detection.camera_geometry import CameraGeometry
cg = CameraGeometry()
xyp = []
for v in range(cg.image_height):
for u in range(cg.image_width):
X,Y,Z= cg.uv_to_roadXYZ_roadframe_iso8855(u,v)
xyp.append(np.array([X,Y,prob_left[v,u]]))
xyp = np.array(xyp)
x_arr, y_arr, p_arr = xyp[:,0], xyp[:,1], xyp[:,2]
mask = p_arr > 0.3
coeffs = np.polyfit(x_arr[mask], y_arr[mask], deg=3, w=p_arr[mask])
polynomial = np.poly1d(coeffs)
讓我們繪制多項式:???????
x = np.arange(0,60,0.1)
y = polynomial(x)
plt.plot(x,y)
plt.xlabel("x (m)"); plt.ylabel("y (m)"); plt.axis("equal");
管道封裝成一個
您現在已經了解了車道檢測流程的兩個步驟:車道邊界分割和多項式擬合。為了便于將來使用,將整個流程封裝到一個類中會很方便。在下面的練習中,您將實現這樣一個LaneDetector
類。現在,讓我們看一下LaneDetector
實際操作的示例解決方案。首先,我們加載一個圖像???????
import cv2
img_fn = "images/carla_scene.png"
img = cv2.imread(img_fn)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img);
現在我們導入該類LaneDetector
并創建它的一個實例。為此,我們指定使用 pytorch 函數存儲的模型的路徑save
。???????
from solutions.lane_detection.lane_detector import LaneDetector
model_path ="../../code/solutions/lane_detection/fastai_model.pth"
ld = LaneDetector(model_path=model_path)
從現在開始,我們可以通過將任何圖像(與訓練集沒有太大差別)傳遞給實例來獲取車道邊界多項式ld
。
poly_left, poly_right = ld(img)
Reference
https://thomasfermi.github.io/Algorithms-for-Automated-Driving/LaneDetection/InversePerspectiveMapping.html