周奇豐,凌莉萍
(蘇州建設交通高等職業(yè)技術學校,江蘇 蘇州 215000)
2020年11月11日,由北京市人民政府、工業(yè)和信息化部、公安部、交通運輸部等共同主辦的“2020世界智能網聯汽車大會”召開,在會中發(fā)布了《智能網聯汽車技術路線圖2.0》。按照規(guī)劃,2025年,L2/L3級智能網聯汽車銷量占當年汽車總銷量的比例超過50%。到2035年,中國方案智能網聯汽車技術和產業(yè)體系全面建成,產業(yè)生態(tài)健全完善,整車智能化水平顯著提升,網聯式高度自動駕駛網聯汽車大規(guī)模應用。而目前在L2/L3級的智能網聯汽車中攝像頭起到了至關重要的作用。基于OpenCV視覺庫的車道線圖像處理技術,能夠在視頻流中準確實現車道線的實時檢測。
OpenCV(Open Source Computer Vision Library),是一個基于(開源)發(fā)行的跨平臺計算機視覺庫,可以運行在Linux、Windows和Mac OS操作系統(tǒng)上,能實現圖像處理和計算機視覺方面的很多通用算法。而NumPy可用來存儲和處理大型矩陣,比使用 Python本身處理要高效得多,支持高維度數組與矩陣的運算,此外也針對數組提供了大量的數學函數庫。在車道線的圖像處理過程中,如圖1所示,重點在霍夫變換,難點在如何處理像素點。
圖1 車道線圖像識別主要步驟
圖像預處理之前,首先要了解什么是RGB圖像、灰度圖像和二值圖像[1]。在進行圖像處理時需要將圖像中每一個像素點數值化后存放在圖像矩陣中。RGB圖像是指彩色圖像,分別用R(紅色)、G(綠色)和B(藍色)三個通道的顏色值來表示每個像素的顏色,每一個像素點有3個顏色分量?;叶葓D像是指黑白圖像,每一個像素點由0~255的取值構成,其中0代表黑色,255代表白色。二值圖像僅由黑色和白色兩種顏色構成,取值僅有0或1,0代表黑色,1代表白色。如圖2所示:
圖2 RGB圖像、灰度圖像和二值圖像
首先需要把圖像灰度化后導入程序中,如圖3所示:
圖3 導入灰度化的圖像
import cv2
import numpy as np
img=cv2.imread('C://Users//MIF//Desktop/chedaoxian2.jp g',cv2.IMREAD_GRAYSCALE)
高斯濾波是一種常用的濾波算法,可以有效地抑制噪聲,平滑圖像,減少圖像中噪點對與分析的影響[2]。高斯濾波的原理如圖4所示,對于某一個圖像,要對紅色的這個像素點高斯濾波,首先要選定一個核大小,圖示為3×3(任意奇數都可以)。對于紅色像素點核內每一個像素點都有一個系數,距離紅色像素點越近的像素點系數越大,距離紅色像素點越遠的點系數越小。即讓臨近的像素點具有更高的重要度,對周圍像素計算加權平均值,較近的像素具有較大的權重值,從而得到新的像素點的值。
圖4 高斯濾波原理
高斯濾波在OpenCV中使用cv2.GaussianBlur()這個函數來完成,如下所示:a=cv2.GaussianBlur(img,(5,5),0)。
在將圖像高斯濾波后,對圖像進行邊緣檢測,找出圖像的邊界點[3]。在OpenCV中可以選擇sobel算子、scharr算子或者laplacian算子來得出圖像梯度,經過試驗發(fā)現使用canny邊緣檢測來計算圖像的邊緣效果更好,如圖5所示:b=cv2. Canny(a,100,200,apertureSize =3)。
圖5 canny邊緣檢測后的圖像
圖像處理中的ROI,即感興趣區(qū)域。在圖像中可以選擇一個區(qū)域作為圖像處理關注的重點,使用ROI可以減少圖像處理時間,減少資源占用。利用mask掩膜生成ROI時,首先生成一個和原圖像大小一致的黑底圖像,然后利用坐標畫出一個感興趣區(qū)域并且填充白色,利用函數cv2.bitwise_and()進行與操作,保留白色區(qū)域的圖像,剔除黑色區(qū)域的圖像,如圖6所示:
圖6 mask掩膜
mask=np.zeros_like(img)
triangle=np.array([[0,753],[500,376],[640,376],[1073,753]])
cv2.fillPoly(mask,[triangle],(255,255,255))
mask_img=cv2.bitwise_and(mask,b)
經過圖像的預處理,可以得到由多條線段構成的圖像?;舴蜃儞Q的原理就是在笛卡爾坐標系中,一條直線y=kx+b中由唯一的k、q相對應,我們把k、q分別作為x軸,y軸,可以得到霍夫空間。笛卡爾坐標系中的一條線對應霍夫空間中的一個點。反過來也同樣成立,霍夫空間中的一條線對應笛卡爾坐標系中的一個點,如圖7所示:
圖7 霍夫變換原理1
如果在笛卡爾坐標系中有幾個點共線,那么對應到霍夫空間中就有幾條直線通過同一個點,如圖8所示:
圖8 霍夫變換原理2
在笛卡爾坐標系中,由多個點共不同直線的情況如圖9所示。在笛卡爾坐標系中(1,1),(2,1),(4,1)這3個點共線,(3,2),(2,1),(1,0)這3個點共線。反映到霍夫空間中我們可以看出分別有A,B這兩個點由3條直線共點。而由于垂直于y軸的直線沒有k值,所以一般在處理的時候會采用極坐標。因此,霍夫變換的目的就是在霍夫空間內盡可能找到由更多條直線構成的點,可以設定閾值,篩選出不符合條件的點。
圖9 霍夫變換原理3
在OpenCV中,可以利用cv2.HoughLinesP()函數來完成霍夫變換,如下所示:
minLineLength = 100
maxLineGap = 10
lines = cv2.HoughLinesP(mask_img, 1, np.pi / 180,50, min LineLength, maxLineGap)
其中,mask_ing是我們預處理后的圖像;1是極坐標的ρ;np.pi/180是極坐標的θ;50是設定的閾值,超過50個像素點共線的直線才會被檢測出來,值越大,被檢測出來的線段個數越少,值越小,被檢測出來的線段個數越多;minLine Length代表線段的最小長度;maxLineGap代表同一方向上兩條線段判定為一條線段的最大允許間隔。經過cv2.Hough LinesP()函數處理后的返回值是(x1,y1,x2,y2)四個元素的列表,其中(x1,y1)和(x2,y2)分別代表線段的兩個端點。
完成霍夫變換后,需要對cv2.HoughLinesP()的返回值進行處理。處理的流程如圖10所示:
圖10 處理像素點集流程
(1)通過斜率正負判斷左右車道線,并分別添加入集合。
line_left=[]
line_right=[]
for line in lines:
for x1,y1,x2,y2 in line:
k=(y2-y1)/(x2-x1)
if k<0:
line_left.append(line)
else:
line_right.append(line)
(2)求出所有線段的斜率,并求平均值。分別計算出斜率列表中每一個元素與斜率平均值的差值,并且和平均值比較大小,超過設定閾值大小,刪除斜率不符合要求的點。
def clean_line(lines,threshould):
slope=[]
for line in lines:
for x1,y1,x2,y2 in line:
k=(y2-y1)/(x2-x1)
slope.append(k)
while len(lines)>0:
mean=np.mean(slope)
diff=[abs(s-mean) for s in slope]
idx=np.argmax(diff)
if diff[idx]>threshould:
slope.pop(idx)
lines.pop(idx)
else:
break
clean_line(line_left,0.5)
clean_line(line_right,0.5)
(3)分別提取左右兩側所有的點,并放入列表中。
points_left=[]
for points in line_left:
for x1,y1,x2,y2 in points:
left=(x1,y1)
points_left.append(left)
left=(x2,y2)
points_left.append(left)
points_right=[(x1,y1) for line in line_right for x1,y1,x2,y2 in line]
points_right=points_right+[(x2,y2) for line in line_right for x1,y1,x2,y2 in line]
(4)分別在左右點的列表中找出y值的最大值和最小值。
point_left_y=[]
for i in points_left:
point_left_y.append(i[1])
print(point_left_y)
point_left_y_max=np.max(point_left_y)
point_left_y_min=np.min(point_left_y)
point_right_y=[]
for i in points_right:
point_right_y.append(i[1])
point_right_y_max=np.max(point_right_y)
point_right_y_min=np.min(point_right_y)
(5)用一次多項式擬合所有的點,并且得到左右兩條線段的端點。
def fit_point_set(point_list,ymin,ymax):
x=[p[0] for p in point_list]
y=[p[1] for p in point_list]
fit=np.polyfit(y,x,1)
fit_set = np.poly1d(fit)
xmin=int(fit_set(ymin))
xmax=int(fit_set(ymax))
return [(xmin,ymin),(xmax,ymax)]
left_fps=fit_point_set(points_left,point_left_y_min,1073)
right_fps=fit_point_set(points_right,point_right_y_min,107 3)
以上程序就是對像素點集的處理,最終可以得到左右兩條擬合車道線的兩個端點,通過左右分別4個點,可以繪制出車道線。
繪制車道線時需要用OpenCV中的cv2.line()函數,如下所示:
cv2.line(img,left_fps[0],left_fps[1],(0,0,0),5)
cv2.line(img,right_fps[0],right_fps[1],(0,0,0),5)
最終得到的繪制車道線的效果如圖11所示:
圖11 車道線繪制效果
本文不僅僅可以用于檢測車道線的圖片,也可以在視頻 流中檢測車道線,使用cv.VideoCapture()函數就可以實現車道的實時檢測。本文以對車道線進行檢測這一簡單任務為例,說明openCV視覺庫的圖像處理技術優(yōu)勢,例如檢測來自前方車輛的干擾,車道線不清晰,識別障礙物,根據車道線的位置判斷車輛的偏移情況等,都可以通過此項技術進一步研究補充優(yōu)化。