OpenCV實(shí)戰(zhàn)案例之車(chē)道線識(shí)別詳解
一、首先進(jìn)行canny邊緣檢測(cè),為獲取車(chē)道線邊緣做準(zhǔn)備
import cv2 gray_img = cv2.imread('img.jpg',cv2.IMREAD_GRAYSCALE) canny_img = cv2.Canny(gray_img,50,100) cv2.imwrite('canny_img.jpg',canny_img) cv2.imshow('canny',canny_img) cv2.waitKey(0)
二、進(jìn)行ROI提取獲取確切的車(chē)道線邊緣(紅色線內(nèi)部)
方法:在圖像中,黑色表示0,白色為1,那么要保留矩形內(nèi)的白色線,就使用邏輯與,當(dāng)然前提是圖像矩形外也是0,那么就采用創(chuàng)建一個(gè)全0圖像,然后在矩形內(nèi)全1,之后與之前的canny圖像進(jìn)行與操作,即可得到需要的車(chē)道線邊緣。
import cv2 import numpy as np canny_img = cv2.imread('canny_img.jpg',cv2.IMREAD_GRAYSCALE) roi = np.zeros_like(canny_img) roi = cv2.fillPoly(roi,np.array([[[0, 368],[300, 210], [340, 210], [640, 368]]]),color=255) roi_img = cv2.bitwise_and(canny_img, roi) cv2.imwrite('roi_img.jpg',roi_img) cv2.imshow('roi_img',roi_img) cv2.waitKey(0)
三、利用概率霍夫變換獲取直線,并將斜率正數(shù)和復(fù)數(shù)的線段給分割開(kāi)來(lái)
TIPs:使用霍夫變換需要將圖像先二值化
概率霍夫變換函數(shù):
- lines=cv2.HoughLinesP(image, rho,theta,threshold,minLineLength, maxLineGap)
- image:圖像,必須是8位單通道二值圖像
- rho:以像素為單位的距離r的精度,一般情況下是使用1
- theta:表示搜索可能的角度,使用的精度是np.pi/180
- threshold:閾值,該值越小,判定的直線越多,相反則直線越少
- minLineLength:默認(rèn)為0,控制接受直線的最小長(zhǎng)度
- maxLineGap:控制接受共線線段的最小間隔,如果兩點(diǎn)間隔超過(guò)了參數(shù),就認(rèn)為兩點(diǎn)不在同一直線上,默認(rèn)為0
- lines:返回值由numpy.ndarray構(gòu)成,每一對(duì)都是一對(duì)浮點(diǎn)數(shù),表示線段的兩個(gè)端點(diǎn)
import cv2 import numpy as np #計(jì)算斜率 def calculate_slope(line): x_1, y_1, x_2, y_2 = line[0] return (y_2 - y_1) / (x_2 - x_1) edge_img = cv2.imread('masked_edge_img.jpg', cv2.IMREAD_GRAYSCALE) #霍夫變換獲取所有線段 lines = cv2.HoughLinesP(edge_img, 1, np.pi / 180, 15, minLineLength=40, maxLineGap=20) #利用斜率劃分線段 left_lines = [line for line in lines if calculate_slope(line) < 0] right_lines = [line for line in lines if calculate_slope(line) > 0]
四、離群值過(guò)濾,剔除斜率相差過(guò)大的線段
流程:
- 獲取所有的線段的斜率,然后計(jì)算斜率的平均值
- 遍歷所有斜率,計(jì)算和平均斜率的差值,尋找最大的那個(gè)斜率對(duì)應(yīng)的直線,如果差值大于閾值,那么就從列表中剔除對(duì)應(yīng)的線段和斜率
- 循環(huán)執(zhí)行操作,直到剩下的全部都是小于閾值的線段
def reject_abnormal_lines(lines, threshold): slopes = [calculate_slope(line) for line in lines] while len(lines) > 0: mean = np.mean(slopes) diff = [abs(s - mean) for s in slopes] idx = np.argmax(diff) if diff[idx] > threshold: slopes.pop(idx) lines.pop(idx) else: break return lines reject_abnormal_lines(left_lines, threshold=0.2) reject_abnormal_lines(right_lines, threshold=0.2)
五、最小二乘擬合,實(shí)現(xiàn)將左邊和右邊的線段互相擬合成一條直線,形成車(chē)道線
流程:
- 取出所有的直線的x和y坐標(biāo),組成列表,利用np.ravel進(jìn)行將高維轉(zhuǎn)一維數(shù)組
- 利用np.polyfit進(jìn)行直線的擬合,最終得到擬合后的直線的斜率和截距,類(lèi)似y=kx+b的(k,b)
- 最終要返回(x_min,y_min,x_max,y_max)的一個(gè)np.array的數(shù)據(jù),那么就是用np.polyval求多項(xiàng)式的值,舉個(gè)example,np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1,即可以獲得對(duì)應(yīng)x坐標(biāo)的y坐標(biāo)。
def least_squares_fit(lines): # 1. 取出所有坐標(biāo)點(diǎn) x_coords = np.ravel([[line[0][0], line[0][2]] for line in lines]) y_coords = np.ravel([[line[0][1], line[0][3]] for line in lines]) # 2. 進(jìn)行直線擬合.得到多項(xiàng)式系數(shù) poly = np.polyfit(x_coords, y_coords, deg=1) print(poly) # 3. 根據(jù)多項(xiàng)式系數(shù),計(jì)算兩個(gè)直線上的點(diǎn),用于唯一確定這條直線 point_min = (np.min(x_coords), np.polyval(poly, np.min(x_coords))) point_max = (np.max(x_coords), np.polyval(poly, np.max(x_coords))) return np.array([point_min, point_max], dtype=np.int) print("left lane") print(least_squares_fit(left_lines)) print("right lane") print(least_squares_fit(right_lines))
六、繪制線段
cv2.line(img, tuple(left_line[0]), tuple(left_line[1]), color=(0, 255, 255), thickness=5) cv2.line(img, tuple(right_line[0]), tuple(right_line[1]), color=(0, 255, 255), thickness=5)
全部代碼(視頻顯示)
import cv2 import numpy as np def get_edge_img(color_img, gaussian_ksize=5, gaussian_sigmax=1, canny_threshold1=50, canny_threshold2=100): """ 灰度化,模糊,canny變換,提取邊緣 :param color_img: 彩色圖,channels=3 """ gaussian = cv2.GaussianBlur(color_img, (gaussian_ksize, gaussian_ksize), gaussian_sigmax) gray_img = cv2.cvtColor(gaussian, cv2.COLOR_BGR2GRAY) edges_img = cv2.Canny(gray_img, canny_threshold1, canny_threshold2) return edges_img def roi_mask(gray_img): """ 對(duì)gray_img進(jìn)行掩膜 :param gray_img: 灰度圖,channels=1 """ poly_pts = np.array([[[0, 368], [300, 210], [340, 210], [640, 368]]]) mask = np.zeros_like(gray_img) mask = cv2.fillPoly(mask, pts=poly_pts, color=255) img_mask = cv2.bitwise_and(gray_img, mask) return img_mask def get_lines(edge_img): """ 獲取edge_img中的所有線段 :param edge_img: 標(biāo)記邊緣的灰度圖 """ def calculate_slope(line): """ 計(jì)算線段line的斜率 :param line: np.array([[x_1, y_1, x_2, y_2]]) :return: """ x_1, y_1, x_2, y_2 = line[0] return (y_2 - y_1) / (x_2 - x_1) def reject_abnormal_lines(lines, threshold=0.2): """ 剔除斜率不一致的線段 :param lines: 線段集合, [np.array([[x_1, y_1, x_2, y_2]]),np.array([[x_1, y_1, x_2, y_2]]),...,np.array([[x_1, y_1, x_2, y_2]])] """ slopes = [calculate_slope(line) for line in lines] while len(lines) > 0: mean = np.mean(slopes) diff = [abs(s - mean) for s in slopes] idx = np.argmax(diff) if diff[idx] > threshold: slopes.pop(idx) lines.pop(idx) else: break return lines def least_squares_fit(lines): """ 將lines中的線段擬合成一條線段 :param lines: 線段集合, [np.array([[x_1, y_1, x_2, y_2]]),np.array([[x_1, y_1, x_2, y_2]]),...,np.array([[x_1, y_1, x_2, y_2]])] :return: 線段上的兩點(diǎn),np.array([[xmin, ymin], [xmax, ymax]]) """ x_coords = np.ravel([[line[0][0], line[0][2]] for line in lines]) y_coords = np.ravel([[line[0][1], line[0][3]] for line in lines]) poly = np.polyfit(x_coords, y_coords, deg=1) point_min = (np.min(x_coords), np.polyval(poly, np.min(x_coords))) point_max = (np.max(x_coords), np.polyval(poly, np.max(x_coords))) return np.array([point_min, point_max], dtype=np.int) # 獲取所有線段 lines = cv2.HoughLinesP(edge_img, 1, np.pi / 180, 15, minLineLength=40, maxLineGap=20) # 按照斜率分成車(chē)道線 left_lines = [line for line in lines if calculate_slope(line) > 0] right_lines = [line for line in lines if calculate_slope(line) < 0] # 剔除離群線段 left_lines = reject_abnormal_lines(left_lines) right_lines = reject_abnormal_lines(right_lines) return least_squares_fit(left_lines), least_squares_fit(right_lines) def draw_lines(img, lines): left_line, right_line = lines cv2.line(img, tuple(left_line[0]), tuple(left_line[1]), color=(0, 255, 255), thickness=5) cv2.line(img, tuple(right_line[0]), tuple(right_line[1]), color=(0, 255, 255), thickness=5) def show_lane(color_img): edge_img = get_edge_img(color_img) mask_gray_img = roi_mask(edge_img) lines = get_lines(mask_gray_img) draw_lines(color_img, lines) return color_img capture = cv2.VideoCapture('video.mp4') while True: ret, frame = capture.read() if not ret: break frame = show_lane(frame) cv2.imshow('frame', frame) cv2.waitKey(10)
總結(jié)
到此這篇關(guān)于OpenCV實(shí)戰(zhàn)案例之車(chē)道線識(shí)別詳解的文章就介紹到這了,更多相關(guān)OpenCV車(chē)道線識(shí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- opencv車(chē)道線檢測(cè)的實(shí)現(xiàn)方法
- 基于OpenCV實(shí)現(xiàn)車(chē)道線檢測(cè)(自動(dòng)駕駛 機(jī)器視覺(jué))
- python+opencv實(shí)現(xiàn)車(chē)道線檢測(cè)
- 基于opencv實(shí)現(xiàn)車(chē)道線檢測(cè)
- C++ opencv實(shí)現(xiàn)車(chē)道線識(shí)別
- 使用opencv實(shí)現(xiàn)車(chē)道線檢測(cè)實(shí)戰(zhàn)代碼
- OpenCV利用霍夫變換實(shí)現(xiàn)交通車(chē)道線檢測(cè)
相關(guān)文章
python中內(nèi)置庫(kù)os與sys模塊的詳細(xì)介紹
這篇文章主要介紹了python中內(nèi)置庫(kù)os與sys模塊的詳細(xì)介紹,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08python?sklearn數(shù)據(jù)預(yù)處理之?dāng)?shù)據(jù)縮放詳解
數(shù)據(jù)的預(yù)處理是數(shù)據(jù)分析,或者機(jī)器學(xué)習(xí)訓(xùn)練前的重要步驟,這篇文章主要為大家詳細(xì)介紹了sklearn數(shù)據(jù)預(yù)處理中數(shù)據(jù)縮放的相關(guān)知識(shí),感興趣的小伙伴可以學(xué)習(xí)一下2023-10-10python如何統(tǒng)計(jì)代碼運(yùn)行的時(shí)長(zhǎng)
這篇文章主要介紹了python如何統(tǒng)計(jì)代碼運(yùn)行的時(shí)長(zhǎng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07淺談numpy中l(wèi)inspace的用法 (等差數(shù)列創(chuàng)建函數(shù))
下面小編就為大家?guī)?lái)一篇淺談numpy中l(wèi)inspace的用法 (等差數(shù)列創(chuàng)建函數(shù))。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06django的autoreload機(jī)制實(shí)現(xiàn)
這篇文章主要介紹了django的autoreload機(jī)制實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06PyQt5實(shí)現(xiàn)從主窗口打開(kāi)子窗口的方法
今天小編就為大家分享一篇PyQt5實(shí)現(xiàn)從主窗口打開(kāi)子窗口的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06numpy.ndarray 實(shí)現(xiàn)對(duì)特定行或列取值
今天小編就為大家分享一篇numpy.ndarray 實(shí)現(xiàn)對(duì)特定行或列取值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12淺談Python由__dict__和dir()引發(fā)的一些思考
這篇文章主要介紹了淺談Python由__dict__和dir()引發(fā)的一些思考,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10