使用霍夫變換檢測(cè)車道線
點(diǎn)擊上方“小白學(xué)視覺”,選擇加"星標(biāo)"或“置頂”
重磅干貨,第一時(shí)間送達(dá)

第一部分:高斯模糊+ Canny邊緣檢測(cè)
第二部分:霍夫變換
第三部分:優(yōu)化+顯示線條
import numpy as npimport cv2import matplotlib.pyplot as plt
第1行:Numpy用于執(zhí)行數(shù)學(xué)計(jì)算,我們要用它來創(chuàng)建和操作數(shù)組。
第3行:使用Matplotlib可視化圖像。
image_path = r"D:\users\new owner\Desktop\TKS\Article LaneDetection\udacity\solidWhiteCurve.jpg"image1 = cv2.imread(image_path)plt.imshow(image1)
def grey(image):return cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)def gauss(image):return cv2.GaussianBlur(image, (5, 5), 0)def canny(image):edges = cv2.Canny(image,50,150)return edges
img參數(shù)定義了我們要進(jìn)行歸一化(減少噪聲)的圖像。這個(gè)函數(shù)使用一個(gè)稱為高斯核的核函數(shù),用于對(duì)圖像進(jìn)行歸一化。
sigma參數(shù)定義沿x軸的標(biāo)準(zhǔn)偏差。標(biāo)準(zhǔn)偏差衡量圖像中像素的分布,我們希望像素?cái)U(kuò)散是一致的,因此標(biāo)準(zhǔn)偏差為0。
img參數(shù)定義了我們要檢測(cè)邊緣的圖像。
threshold-1參數(shù)過濾所有低于這個(gè)數(shù)字的梯度(它們不被認(rèn)為是邊緣)。
threshold-2參數(shù)決定了邊緣的有效值。
如果兩個(gè)閾值之間的任何梯度連接到另一個(gè)高于閾值2的梯度,則將考慮該梯度。

def region(image):height, width = image.shapetriangle = np.array([[(100, height), (475, 325), (width, height)]])mask = np.zeros_like(image)mask = cv2.fillPoly(mask, triangle, 255)mask = cv2.bitwise_and(image, mask)return mask

左:Img1,右圖:Img2。(實(shí)際上,它是白色的,但我們把它改成了黃色)

其他一切都被忽略了,僅輸出隔離區(qū)域中的邊。
lines = cv2.HoughLinesP(isolated, rho=2, theta=np.pi/180,threshold=100, np.array([]), minLineLength=40, maxLineGap=5)
參數(shù)1:孤立梯度
參數(shù)5:占位符數(shù)組
參數(shù)6:最小行長
參數(shù)7:最大行間距

笛卡爾坐標(biāo)空間中的直線
笛卡爾平面上的點(diǎn)在霍夫空間中變成直線
笛卡爾平面上的直線在霍夫空間上變成點(diǎn)


每個(gè)點(diǎn)代表前面顯示的線(匹配顏色)
笛卡爾平面上的直線在霍夫空間中表示為點(diǎn)
笛卡兒平面上的點(diǎn)在霍夫空間中表示為直線
通過求霍夫空間中與這兩個(gè)點(diǎn)對(duì)應(yīng)的兩條直線的POI的m和b坐標(biāo),可以找到笛卡爾空間中兩點(diǎn)的最佳擬合直線,然后根據(jù)這些m和b的值組成一條直線。
P表示從原點(diǎn)垂直于直線的距離。
θ表示從正x軸到直線的俯角。
xcosθ表示x方向上的距離。
ysinθ表示y方向上的距離。

這是對(duì)極坐標(biāo)含義的直觀解釋
θ是一條垂直線的90度,因?yàn)樗鼜恼?/span>x軸到直線本身的俯角是90度。θ的另一種表示方法是π/2(弧度)。如果你們想了解更多關(guān)于弧度的知識(shí),以及我們?yōu)槭裁匆褂盟鼈儯@里有一個(gè)很好的視頻。然而,沒有必要知道弧度是什么。
X和Y取點(diǎn)(6,4)的值因?yàn)檫@是我們?cè)谶@個(gè)例子中使用的點(diǎn)。
P = 6cos(90) + 4sin(90)
P = 6(1) + 4(0)
P = 6

我們想解釋的東西的圖像。

def average(image, lines):left = []right = []for line in lines:slope = parameters[0]y_int = parameters[1]if slope < 0:left.append((slope, y_int))else:right.append((slope, y_int))
第4行:通過直線數(shù)組進(jìn)行循環(huán)。
第5行:從每個(gè)線段中提取兩個(gè)點(diǎn)的(x, y)值。
第6-9行:確定每個(gè)線段的斜率和y軸截距。
第10-13行:將負(fù)斜率添加到左行列表中,將正斜率添加到右行列表中。
right_avg = np.average(right, axis=0)left_avg = np.average(left, axis=0)left_line = make_points(image, left_avg)right_line = make_points(image, right_avg)return np.array([left_line, right_line])
第1-2行:對(duì)兩個(gè)列表(左邊和右邊)的所有線段取平均值。
第3-4行:計(jì)算每一行的起始點(diǎn)和端點(diǎn)。(我們將在下一節(jié)定義make_points函數(shù))
第5行:輸出每一行的2個(gè)坐標(biāo)。
def make_points(image, average):slope, y_int = averagey1 = image.shape[0]y2 = int(y1 * (3/5))x1 = int((y1 — y_int) // slope)x2 = int((y2 — y_int) // slope)return np.array([x1, y1, x2, y2])
第1行:定義函數(shù)
第2行:得到平均斜率和y截距
第3 - 4行:定義的高度線(左右兩邊都一樣)
第5 - 6行:通過重新排列一條線的方程計(jì)算x坐標(biāo),從y=mx+b to x = (y-b) / m
第7行:輸出坐標(biāo)集

應(yīng)用于左線的make_points函數(shù)的可視化示例
但是,這個(gè)函數(shù)并不顯示這些線,它只計(jì)算顯示這些線所需的點(diǎn)。接下來,我們要?jiǎng)?chuàng)建一個(gè)函數(shù),它取這些點(diǎn),并用它們來畫線。
def display_lines(image, lines):lines_image = np.zeros_like(image)if lines is not None:for line in lines:x1, y1, x2, y2 = linecv2.line(lines_image, (x1, y1), (x2, y2), (255, 0, 0), 10)return lines_image
第2行:創(chuàng)建一個(gè)與原始圖像相同尺寸的黑色圖像
第3行:確保包含線點(diǎn)的列表不是空的
第4-5行:循環(huán)遍歷列表,并提取兩對(duì)(x, y)坐標(biāo)

左:直接添加線條到圖像。右:使用cv2.addddled函數(shù)
copy = np.copy(image1)grey = grey(copy)gaus = gauss(grey)edges = canny(gaus,50,150)isolated = region(edges)lines = cv2.HoughLinesP(isolated, 2, np.pi/180, 100, np.array([]),minLineLength=40, maxLineGap=5)averaged_lines = average(copy, lines)black_lines = display_lines(copy, averaged_lines)

video = r”D:\users\new owner\Desktop\TKS\Article LaneDetection\test2_v2_Trim.mp4"cap = cv2.VideoCapture(video)while(cap.isOpened()):ret, frame = cap.read()if ret == True:#----THE PREVIOUS ALGORITHM----#gaus = gauss(frame)edges = cv2.Canny(gaus,50,150)isolated = region(edges)lines = cv2.HoughLinesP(isolated, 2, np.pi/180, 50,)lanes = cv2.ad1dWeighted(frame, 0.8, black_lines, 1, 1)cv2.imshow(“frame”, lanes)#----THE PREVIOUS ALGORITHM----#if cv2.waitKey(10) & 0xFF == ord(‘q’):breakelse:breakcap.release()cv2.destroyAllWindows()
第1-2行:定義視頻的路徑。
第3-4行:捕獲視頻(使用cv2. videcapture),并循環(huán)遍歷所有幀。
第5-6行:讀取幀,如果有幀,繼續(xù)。
第10-18行:從前面的算法復(fù)制代碼,并將所有使用Copy的地方替換為frame,因?yàn)槲覀兿氪_保我們操作的是視頻的幀,而不是前面函數(shù)中的圖像。
第22-23行:顯示每一幀10秒,如果按下“q”按鈕,退出循環(huán)。
第24-25行:它是第5-6行if語句的延續(xù),但它所做的只是如果沒有任何幀,就退出循環(huán)。
第26-27行:關(guān)閉視頻
關(guān)鍵點(diǎn):
使用高斯模糊去除圖像中的所有噪聲
使用canny邊緣檢測(cè)來分離圖像中的邊緣
關(guān)鍵字:
高斯模糊
位和二進(jìn)制
精明的邊緣檢測(cè)
霍夫變換
梯度
極坐標(biāo)
OpenCV車道線檢測(cè)
其他需要考慮的資源:
youtube視頻。
好消息,小白學(xué)視覺團(tuán)隊(duì)的知識(shí)星球開通啦,為了感謝大家的支持與厚愛,團(tuán)隊(duì)決定將價(jià)值149元的知識(shí)星球現(xiàn)時(shí)免費(fèi)加入。各位小伙伴們要抓住機(jī)會(huì)哦!

交流群
歡迎加入公眾號(hào)讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動(dòng)駕駛、計(jì)算攝影、檢測(cè)、分割、識(shí)別、醫(yī)學(xué)影像、GAN、算法競賽等微信群(以后會(huì)逐漸細(xì)分),請(qǐng)掃描下面微信號(hào)加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請(qǐng)按照格式備注,否則不予通過。添加成功后會(huì)根據(jù)研究方向邀請(qǐng)進(jìn)入相關(guān)微信群。請(qǐng)勿在群內(nèi)發(fā)送廣告,否則會(huì)請(qǐng)出群,謝謝理解~

