無人駕駛技術之車道檢測

Submitted by wangqingqing on Sun, 07/09/2017 - 15:20
車道識別

? ? ? ?因為本文是自動駕駛技術的第一篇,這里的車道檢測是基礎版本,需要滿足幾個先決條件:

? ? ? ? ? ? ?(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

代碼:https://github.com/feixia586/zhihu_material/tree/master/car_lane_detection

?

從圖片開始談起

無人車往往配備有數個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.

車道圖片2

Gaussian Smoothing

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

公式
梯度矩陣圖

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

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

blur_ksize = 5 # Gaussian blur kernel size

blur_gray = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0, 0)

車道圖片3

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

車道圖片4

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)

車道圖片5

Hough Transformation

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

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

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

車道圖片7

在實際操作中,我們往往將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)

車道圖片8

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

Lane Extrapolation

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

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

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

車道圖片9

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

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

車道圖片10

圖片任務完成!

回到視頻上

現在我們將前面的代碼打個包放到叫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上,看看結果!

?

視頻截圖1

視頻截圖1

視頻截圖2

視頻截圖2

視頻截圖3

視頻截圖3

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

參考資料:

1、High ISO Noise Reduction ? ?http://www.learn.usa.canon.com/resources/articles/2011/high_iso_noise_reduction_article.shtml

2、Gaussian Smoothing ? ? ? ? ? ? ?http://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm

冯仰妍破处门