1. 程式人生 > >無人駕駛:Term-1 p1 lane-detection

無人駕駛:Term-1 p1 lane-detection

簡介

  Udacity是矽谷的一個線上教育網站,主要以介紹AI技術為主,除了基本的課程錄影還會有專業老師進行程式碼review,通過課程會頒發nano degree。其中最為火爆的一門課程就是自動駕駛課程CarNd,這門課程由簡入繁分了三個term,第一個term主要是以機器視覺識別道路、交通標誌為主,需要掌握基本的深度學習和機器視覺知識;第二個term是介紹感測器以及使用卡爾曼濾波進行定位和速度控制;第三個term主要是路徑規劃以及系統整合,據說最後會把學員的程式碼放入真車進行實際測試。Ucacity的課程質量很高,但是有些小貴,出於無奈只好從網上搜集資料,開源的資料包含了前兩個term,學完可以對無人駕駛有基本的瞭解,感興趣的同學可以試試。

處理流程

  無人駕駛車輛的基本功能之一就是檢測車道,之前參加過飛思卡爾智慧車競賽的同學對這個專案應該並不陌生,與攝像頭組識別賽道的原理類似,可惜當時我在紅外組,沒有深入瞭解相關原理。

  先簡單說下利用影象處理技術識別車道的基本流程


處理流程

  p1相對來說比較簡單,使用opencv的一些操作函式即可實現檢測功能。下面詳細介紹每個步驟的實現

讀入影象

  首先讀入影象

//匯入opencv
import cv2

//讀取影象
image = cv2.imread("./test_images/solidYellowCurve.jpg")
//展示影象
cv2.imshow('lane'
,image) //等待鍵盤按鍵 cv2.waitKey() //銷燬圖片展示視窗 cv2.destroyAllWindows()

效果如下


車道原圖

二值化處理

  opencv對於彩色影象的儲存使用GBR格式,與一般RGB格式順序不太一樣,據說是為了做硬體相容。二值化處理是把讀入的彩色影象轉成灰度圖,方便後續計算。

//彩色影象轉化為灰度圖
gray_image = cv2.cvtColor(image,cv2.COLOR_GBR2GRAY)

效果如下


二值化影象

高斯模糊

  高斯模糊是一種影象平滑技術,基本原理是在以中心畫素為原點取一定半徑內畫素點值求加權平均值,權重根據高斯函式求得,結果就是高斯模糊處理後的圖片。從數值上看,整體上更加平緩,從圖片效果上看變得模糊了。

blured_image = cv2.GaussianBlur(gray_image,(5,5),0) //第二個引數表示模糊半徑,第三個引數代表高斯函式標準差,半徑越大標準差越大,圖片越模糊

高斯模糊

Canny邊緣檢測

  由於車道具有明顯的邊緣,所以使用邊緣檢測可以提取車道資訊,opencv提供了canny邊緣檢測函式可以實現該功能。

canny = cv2.cv2.Canny(blur,250,300)

效果如下


canny邊緣檢測

ROI區域提取

  在理想情況下(直路且行駛平穩),車道的位置在拍攝的圖片中的相對位置不變,我們並不關心除車道以外的其他位置,所以可以通過ROI區域提取獲取車道所在區域。針對p1所用到的圖片,其ROI區域頂點座標分別為(0,540),(465,320),(475,320),(960,320),其所形成的不規則區域如下所示


roi區域

與邊緣檢測結果進行疊加

roi_range = np.array([[(0,540),(465,320),(475,320),(960,320)]],dtype = np.int32)
mask = np.zeros_like(canny)  //複製一個和canny影象大小一樣的疊加矩陣
cv2.fillPoly(mask,roi_range,255) //設定roi區域的畫素值為255,其他區域為0
img_masked = cv2.bitwise_and(canny,mask) //將canny影象和疊加影象求並,ROI區域外的部分都變為0

效果如下


roi區域提取

Hough直線檢測

  現在經過處理後的影象基本只包含ROI區域內的車道資訊以及其他干擾資訊,這兩種資訊的區別在於車道由多條直線構成,而干擾資訊則不具備這樣明顯的條件,所以直線檢測可以提取ROI區域內的車道。
  opencv提供了霍夫變換函式HoughLines和HoughLinesP用於直線檢測,目前先不去深究其原理,後續我再補充。其函式定義如下:

HoughLinesP(image, rho, theta, threshold, minLineLength=None, maxLineGap=None)

  • image:必須是二值影象,推薦使用canny邊緣檢測的結果影象;
  • rho: 線段以畫素為單位的距離精度,double型別的,推薦用1.0
  • theta: 線段以弧度為單位的角度精度,推薦用numpy.pi/180
  • threshod: 累加平面的閾值引數,int型別,超過設定閾值才被檢測出線段,值越大,基本上意味著檢出的線段越長,檢出的線段個數越少。根據情況推薦先用100試試
  • minLineLength:線段以畫素為單位的最小長度,根據應用場景設定
  • maxLineGap:同一方向上兩條線段判定為一條線段的最大允許間隔(斷裂),超過了設定值,則把兩條線段當成一條線段,值越大,允許線段上的斷裂越大,越有可能檢出潛在的直線段

    lines = cv2.HoughLinesP(img_mashed,1,np.pi/180,15,25,20)
    for line in lines:
        for x1,y1,x2,y2 in line:
            cv2.line(img_masked,(x1,y1),(x2,y2),255,12)
    cv2.imshow('img_masked',img_masked)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    效果如下


    直線檢測

車道標記

  直線檢測完成後,需要對檢測後的直線進行過濾,把誤差較大的點移除,並且還要將這些點連成直線,進行車道標記。

車道左右邊線檢測

  首先需要區分車道左邊線和右邊線,我們已經拿到了直線檢測後的直線端點,可以利用斜率進行區分。由於opencv預設原點位於影象左上角頂點,所以左邊線斜率為負,右邊線斜率為正,並計算每段線段的斜率和截距。

“`
positive_slop_intercept = [] //左邊線點構成直線的斜率和截距
negative_slop_intercept = [] //右邊線點構成直線的斜率和截距
for line in lines:
for x1,y1,x2,y2 in line:
slop = np.float((y2-y1))/np.float((x2-x1)) //計算斜率
if slop > 0:
positive_slop_intercept.append([slop,y1-slop*x1]) //根據點的座標和斜率計算截距
elif slop < 0 :
negative_slop_intercept.append([slop,y1-slop*x1])

“`

計算左右邊線斜率和截距

  上一步完成了斜率和截距的計算,還要對其進行篩選,把誤差較大的線段移除

legal_slop=[]
legal_intercept=[]
slopes=[pair[0] for pair in slop_intercept]
slop_mean = np.mean(slopes)                    //斜率的均值
slop_std = np.std(slopes)                           //斜率的標準差
for pair in slop_intercept:
    if pair[0] - slop_mean < 3 * slop_std:             //挑選出平均值3個標準差誤差範圍內的斜率和截距
        legal_slop.append(pair[0])
        legal_intercept.append(pair[1])
if not legal_slop:                                 //如果沒有合理範圍內的斜率,則使用原始斜率,最終的斜率就是均值
    legal_slop = slopes
    legal_intercept = [pair[1] for pair in slop_intercept]
slop = np.mean(legal_slop)
intercept = np.mean(legal_intercept)

計算車道兩端端點

  計算除了車道的斜率和截距,還需要兩個端點才能確定車道的範圍。由於我們只關注ROI區域範圍內的車道,所以車道左右邊線的上頂點的值為ymin=320、下頂點的值為ymax=540,從而求得每條邊線的兩端頂點。

xmin = int((ymin - intercept)/slop)
xmax = int((ymax-intercept)/slop)

標記車道

  把標記過車道的圖片與原始圖片疊加,實現車道的標記。影象疊加通過opencv的addWeight函式實現,該函式定義如下:
cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])

  • alpha : 第一幅圖片中元素的權重
  • beta : 是第二個的權重
  • gamma : 加到最後結果上的值
//imgae為原始影象,line_image為車道標記影象
res_image = cv2.addWeighted(image,0.8,line_image,1,0)

最終結果如下


車道標記

處理視訊

  處理視訊需要使用moviepy的VideoFileClip函式

    video = VideoFileClip(path) //待處理影象路徑
    video_after_process = video.fl_image(process_picture) //處理每張圖片的函式作為引數
    video_after_process.write_videofile("./line_detection.mp4",audio=False) //生成新視訊

程式碼參考