
簡介基于YOLOv5與霍夫變換的車道線檢測Python項目將深度學習目標檢測與傳統圖像處理相結合YOLOv5負責車輛等目標識別霍夫變換完成車道線提取車道線部分無需額外的數據集訓練有效降低學習與使用門檻。面向計算機視覺方向的在校學生、教師及企業開發人員適用于課程設計、畢業設計、項目初期演示或自動駕駛感知相關入門實踐具備一定Python與OpenCV基礎即可上手。項目代碼已經過測試運行成功功能可用資源共125個文件壓縮包約710KB以Python源碼53個py、YOLOv5模型配置48個yaml、Shell運行腳本、Markdown說明文檔及Dockerfile為主另含Jupyter Notebook教程與測試圖片目錄結構清晰便于按模塊閱讀和二次開發。目前已有98人學習下載。除完整項目源碼外還提供容器環境配置與運行腳本下載后可按README指引快速復現若運行遇到問題可聯系作者遠程教學。讀者可重點關注霍夫變換檢測流程與YOLOv5目標檢測的協作方式。整體方案兼顧深度學習與傳統視覺適合作為車道線檢測入門、課設與畢設拓展的參考實現。1. 為什么 YOLOv5 和霍夫變換會同時出現在一個車道線檢測項目里拿到這個標題多數人的第一反應是YOLOv5 做目標檢測霍夫變換做直線檢測這兩者不是替代關系嗎實際上在這個項目里它們是串聯的——YOLOv5 負責在復雜道路環境中定位車道線的大致區域霍夫變換負責在該區域內精確提取車道線的直線輪廓。純霍夫方案在光照突變、路面陰影、車道線磨損時會大量誤檢純 YOLOv5 直接輸出線條又缺少像素級幾何信息無法為車輛橫向控制提供準確的曲率和截距。兩段式方案的好處在于檢測框縮小了霍夫的搜索空間霍夫的輸出又彌補了錨框沒有幾何精度的短板。這個組合適合做自動駕駛視覺感知的入門項目也常見于本科畢設——工程上夠完整又能在論文里分別講清楚深度學習和經典圖像處理兩條線的原理。這個項目實測跑通不難難的是把檢測精度調到能連續處理視頻而不抖動。2. 車道線檢測里的 YOLOv5網絡結構、數據標注與訓練調參2.1 為什么先要 YOLOv5 而不是直接用霍夫變換處理全圖車道線檢測任務里霍夫變換本身只需要一行cv2.HoughLinesP()就能跑起來但純圖像處理方案有一個致命短板它沒有「語義」概念。路邊沿、護欄接縫、橋面伸縮縫、前車陰影邊緣在邊緣圖里和車道線一樣都是直線段霍夫會一視同仁地輸出。YOLOv5 在這個項目里的角色不是「檢測車道線本身」而是先框出車道線可能存在的路面區域相當于給后續的霍夫變換加了一個語義門控。YOLOv5 在 COCO 上預訓練過的權重對車輛、行人、交通標志有很好的響應但車道線不在 COCO 的 80 個類別里所以要用自己的數據集做微調。標題里說「基于 YOLOv5」通常意味著兩件事第一用 YOLOv5 訓練一個只檢測車道線的模型第二拿這個模型跑出 anchor box在每個框的 ROI 內部再用霍夫變換提取直線。第二種方案的檢測目標不是「線」而是「線所在的條帶狀區域」標注框比逐像素分割省事得多。2.2 數據集準備標注車道線區域框而不是標注線如果用 YOLOv5 檢測車道線標注策略和普通目標檢測有區別。車道線是細長條目標標注框形狀接近 1:3 甚至 1:5 的長條矩形框不能只框住線的像素要把線周邊的路面紋理也包含進去。原因是后續的霍夫變換需要在框內做邊緣檢測如果框太緊Canny 會把車道線像素裁掉一半框略大一些反而降低了邊緣斷裂的概率。標注工具推薦 labelImg 或者 labelme導出為 YOLO 格式的 txt每行是class x_center y_center width height四個坐標值都歸一化到 0~1。類別按需要定義最常見的是兩類left_lane和right_lane。如果訓練數據里有摩托車道分隔線或者應急車道線可以加第三類extra_lane。標注時注意別把路面裂縫圈進來裂縫的像素特征和車道線在局部邊緣圖上非常相似類別混淆會讓訓練 loss 不降。數據集目錄結構一般這樣組織dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── lane.yamllane.yaml內容train: dataset/images/train val: dataset/images/val nc: 2 names: [left_lane, right_lane]2.3 訓練命令與超參數調整YOLOv5 官方倉庫的train.py可以直接用來訓練命令常見寫法是python train.py --data lane.yaml --cfg yolov5s.yaml --weights yolov5s.pt \ --batch-size 16 --epochs 100 --img-size 640 --device 0幾個參數的選擇邏輯--cfg yolov5s.yamls 是 small 版本depth_multiple0.33、width_multiple0.50參數量約 7.2M。車道線檢測任務簡單、目標形狀單一不需要上 yolov5l 或 yolov5xs 足夠而且推理速度快。如果要在 Jetson 這類邊緣設備上跑可以進一步用yolov5n。--img-size 640這個值影響小目標召回。車道線在畫面里是細長條如果輸入降到 320靠近消失點的車道線會縮小到 2~3 個像素寬特征基本丟失。640 是精度和速度的平衡點顯存夠且對遠距離檢測有要求可以試 960。--batch-size 16取決于顯存8G 顯存用 16 比較穩再大容易 OOM。--epochs 100遷移學習微調100 epoch 通常夠收斂配合早停機制看 val loss 不再下降就可以停。YOLOv5 的超參數在data/hyps/hyp.scratch.yaml里車道線檢測這種小而單一的目標有兩個值得動lr0: 0.01 # 初始學習率 anchor_t: 4.0 # 錨框長寬比閾值anchor_t默認 4.0 表示錨框的長寬比超過 4 倍就會被判定為不匹配。車道線的錨框長寬比經常到 5~8訓練時如果發現 loss 下降緩慢可以把這個值調到 5.0 或 6.0讓錨框在自動聚類時能容納更極端的長條形狀。lr0若是用yolov5s.pt做遷移0.01 是穩妥起點數據量小幾千張可以降到 0.005 防止震蕩。訓練結束后看runs/train/exp/weights/best.pt的 PR 曲線車道線檢測的 mAP0.5 一般能到 0.9 以上mAP0.5:0.95 會因為 IOU 閾值嚴格而偏低這是正常的不用強求。3. 霍夫變換在車道線檢測中的作用原理、參數與 ROI 設計3.1 從檢測框到候選直線圖像預處理YOLOv5 輸出的檢測框只是車道線的粗略位置框里的內容還是原始圖像塊。要在框內提取精確的直線走的是經典視覺管線將 ROI 區域從原圖中裁剪出來轉灰度圖高斯模糊去除路面紋理噪聲核大小(5, 5)即可太大會把車道線邊緣也抹平Canny 邊緣檢測提取邊緣像素對邊緣圖做霍夫變換累加投票將直線坐標映射回原圖坐標系這一步的關鍵在 Canny 的高斯模糊路面是紋理豐富的區域——瀝青顆粒、小石子、輪胎痕跡在邊緣檢測里都會產生響應。不對圖像做平滑霍夫變換會在投票階段被大量偽邊緣干擾threshold參數很難設置。平滑后 Canny 雙閾值推薦low50, high150這是一個對白天城市道路和高速公路都較穩的區間。3.2 HoughLines 與 HoughLinesP 的區別OpenCV 提供兩個霍夫變換接口車道線檢測基本只用概率版本import cv2 import numpy as np def detect_lane_lines(image): gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred cv2.GaussianBlur(gray, (5, 5), 0) edges cv2.Canny(blurred, 50, 150) lines cv2.HoughLinesP( edges, rho1, thetanp.pi / 180, threshold40, minLineLength60, maxLineGap80 ) return linesHoughLines返回的是極坐標參數(ρ, θ)需要自己換算成線段端點HoughLinesP直接輸出起點和終點坐標(x1, y1, x2, y2)省去反變換。兩者的核心數學原理相同圖像空間的一個點映射到參數空間是一條正弦曲線多個共線點在參數空間會相交于同一(ρ, θ)累加器統計交點峰值峰值超過閾值的(ρ, θ)就是一條直線。3.3 四個關鍵參數的設置依據參數作用車道線場景推薦值調試方向rho參數空間 ρ 的分辨率像素11 即可分辨率過粗會丟失斜率精度theta參數空間 θ 的分辨率弧度np.pi/1801 度精度要求高可設np.pi/360計算量翻倍threshold累加器投票閾值越小直線越容易被檢出30~50虛線車道線建議降低到 30實線可提高到 60minLineLength最小線段長度濾除碎短線60~100圖像尺寸 640 時 60 合適高清圖按比例放大maxLineGap同一直線上允許的最大斷裂間距40~80虛線車道線斷裂間距大建議不低于 60這里最值得強調的坑是threshold和maxLineGap的配合。虛線車道線每段長約 3 米在畫面中表現為 40~70 像素的短線段如果threshold設得過高短線段投票數不夠直接被濾掉如果設低路面紋理形成的雜線也會混進來。經驗做法是先從threshold30起步觀察誤檢多就調高漏檢多就調低不要同時改多個參數驗證一個現象。3.4 ROI 掩膜把搜索限制在合理路面區域霍夫變換里還有一個常被忽視的參數——ROI。直接對整幀圖像做霍夫地平線以上的天空、路邊的樹木、前方車輛的輪廓都會產生大量線段。最常用的做法是做一個三角形或梯形 mask覆蓋車輛前方路面的扇形區域def apply_roi_mask(edges, img_shape): mask np.zeros(img_shape[:2], dtypenp.uint8) h, w img_shape[:2] # 梯形頂點底邊占滿圖像寬度頂邊收窄到畫面中央區域 roi_vertices np.array([[ (0, h), (w, h), (int(w * 0.6), int(h * 0.5)), (int(w * 0.4), int(h * 0.5)) ]], dtypenp.int32) cv2.fillPoly(mask, roi_vertices, 255) masked_edges cv2.bitwise_and(edges, mask) return masked_edges梯形 mask 的四個頂點參數是車道線檢測里最需要標定的數值它取決于攝像頭的安裝角度和俯仰角。如果攝像頭裝在擋風玻璃中上部int(h * 0.5)這個頂邊高度基本適用如果裝在車頂消失點位置更低要把頂邊高度調到h * 0.6左右。有人推薦用動態計算消失點來替代固定梯形這個方向可以做但放在霍夫預處理階段有點過重——固定梯形在光線穩定場景下表現足夠好。4. 兩段式車線檢測 pipeline 的完整實現與驗證4.1 先跑通單幀檢測從 YOLOv5 框到霍夫直線的完整數據流前兩章分別講了兩條線的原理這一章把它們串成可運行的決策流。項目源碼里通常包含一個核心檢測腳本用 YOLOv5 的 PyTorch Hub 接口加載權重對每一幀圖像執行檢測、過濾、霍夫提取。下面的代碼展示了最小可運行的版本import cv2 import numpy as np import torch class LaneDetector: def __init__(self, weights_path, devicecpu): self.model torch.hub.load(ultralytics/yolov5, custom, pathweights_path, force_reloadFalse) self.model.conf 0.45 self.model.iou 0.5 self.device device def get_lane_regions(self, frame): results self.model(frame) boxes results.xyxy[0].cpu().numpy() # 過濾出類別為車道線的檢測框兼容不同訓練類別的 ID lane_boxes boxes[boxes[:, 5] 1] return lane_boxes def detect_lanes(self, frame): lane_boxes self.get_lane_regions(frame) h, w frame.shape[:2] all_lines [] for box in lane_boxes: x1, y1, x2, y2 [int(v) for v in box[:4]] roi frame[y1:y2, x1:x2] if roi.size 0: continue gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) blurred cv2.GaussianBlur(gray, (5, 5), 0) edges cv2.Canny(blurred, 50, 150) lines cv2.HoughLinesP(edges, rho1, thetanp.pi / 180, threshold30, minLineLengthmax(20, int(0.1 * (y2 - y1))), maxLineGap80) if lines is not None: for line in lines: lx1, ly1, lx2, ly2 line[0] all_lines.append(( lx1 x1, ly1 y1, lx2 x1, ly2 y1 )) return all_lines這段代碼的關鍵思路是霍夫變換的線段坐標用框內局部坐標系計算結果映射回原圖坐標時統一加上(x1, y1)相對偏移。minLineLength這里按框的高度動態計算這樣近處大的檢測框允許更長的線段遠處的小框不會被過長的閾值全部濾掉。4.2 引入幾何約束過濾誤檢斜率、截距與左右分類霍夫變換返回的直線里依然會混入護欄、路沿、前車輪廓等干擾線段。比調參更有效的一層過濾是幾何約束在圖像坐標系下車道線不會出現在某些斜率和位置組合上。def filter_and_classify(lines, width, height): left_lanes [] right_lanes [] for line in lines: x1, y1, x2, y2 line dx x2 - x1 dy y2 - y1 if dx 0: continue slope dy / dx # 圖像坐標系 y 軸向下車道線的斜率特征 # 左車道線為負斜率從左上到右下右車道線為正斜率 if abs(slope) 0.3: continue # 按線段中心點橫坐標除以圖像寬度簡單區分左右 center_x (x1 x2) / 2 if center_x width / 2 and slope 0: left_lanes.append(line) elif center_x width / 2 and slope 0: right_lanes.append(line) return left_lanes, right_lanesabs(slope) 0.3過濾掉接近水平的線段這類線段大概率來自路面接縫或車輛底盤陰影。斜率閾值的單位不是角度而是像素比值圖像寬高比不同這個值要微調:1920x1080 的畫面里 0.3 大致對應 16 度左右的傾角。濾完后按線段質心位置做左右分類分類后每側往往會留下多條重疊線段用均值合并成一條車道線比最小二乘擬合更穩。4.3 項目源碼結構參考與離線驗證方法一個完整的課設或畢設項目源碼目錄通常這樣組織project/ ├── weights/ │ └── best.pt # 訓練好的 YOLOv5 權重 ├── utils/ │ ├── preprocessing.py # 圖像預處理、ROI 掩膜 │ ├── lane_filter.py # 斜率過濾、左右分類 │ └── visualization.py # 畫線、畫框、輸出標注視頻 ├── detect.py # 單幀檢測入口 ├── detect_video.py # 視頻流檢測入口 ├── train_yolov5.sh # 訓練腳本 └── README.md驗證階段不要直接用車載視頻看效果先準備一段標注了車道線的測試視頻跑完檢測后輸出標注結果視頻逐幀檢查兩類典型問題。第一類問題是高頻閃爍某幾幀漏檢導致車道線消失。問題大概率出在 YOLOv5 的置信度閾值上把self.model.conf從 0.45 降到 0.3 可以緩解。第二類問題是線段跳動車道線在連續幀間左右橫跳問題出在霍夫參數太寬松、同一側車道線檢出了互相競爭的線段這時把threshold提高 10并把合并策略從簡單均值改成帶滑窗的指數平滑。帶時間維度的滑動平均是視頻穩定最有效的手段class LaneSmoother: def __init__(self, alpha0.6): self.alpha alpha self.left_avg None self.right_avg None def update(self, left_lines, right_lines): # 每個時刻的左右車道線取該側線段均值再套 EMA for side, lines in [(left, left_lines), (right, right_lines)]: if len(lines) 0: continue avg np.mean(lines, axis0) if getattr(self, side _avg) is None: setattr(self, side _avg, avg) else: prev getattr(self, side _avg) smoothed self.alpha * prev (1 - self.alpha) * avg setattr(self, side _avg, smoothed) return self.left_avg, self.right_avg平滑系數alpha0.6的意思是當前幀權重 40%、歷史權重 60%數字越大線越穩但延遲越高壓彎道時會出現線條跟不上路況的「滯后感」一般不超過 0.7。5. 彎道處理、曲線擬合與評估指標霍夫直線檢測的邊界和出路5.1 把直線換成二次曲線擬合霍夫變換輸出的是直線這一天然限制決定了它在彎道上的表現——曲率大的彎道會被擬合成折線視覺上像鋸齒。把霍夫輸出的線段端點收集起來換用最小二乘擬合二次多項式可以覆蓋緩彎場景。做法如下取霍夫線段的兩個端點作為采樣點以 y 為自變量、x 為因變量擬合x a * y^2 b * y c這樣擬合出來的曲線在圖像底部和中部與車道線貼合只在消失點附近偏差增大。P 階數提到 3 以上意義不大還會放大遠端抖動。def fit_curve(points, height): ys points[:, 1].astype(np.float64) xs points[:, 0].astype(np.float64) coeffs np.polyfit(ys, xs, 2) plot_y np.linspace(int(height * 0.5), height, 100) plot_x np.polyval(coeffs, plot_y) return np.column_stack((plot_x, plot_y))擬合前對霍夫線段做一次「遠端截斷」更穩。靠近消失點附近車道線變得非常細霍夫檢測到的邊緣位置誤差很大直接用這些采樣點擬合會明顯偏曲。常見做法是只取圖像下半部分兩側線段參與擬合上半部分用外推補全。5.2 在公開數據集上評估檢測效果項目源碼提供的檢測結果最終要變成量化指標。車道線檢測的評估指標和通用目標檢測不同主流數據集的評估方法如下指標計算方式本項目的參考基線Accuracy預測像素與真實標注像素的匹配度正常光照下 0.85 以上FP誤檢預測了不在標注內的車道線像素每幀越低越好FN漏檢標注中有但未預測出的車道線像素陰影、遠距離下會明顯升高IoU預測區域與真實區域的交并比0.5 左右即可認可TuSimple 數據集的評估腳本計算每個點附近的匹配情況對準確定義是「預測點與標注點距離小于某個閾值視為正確」。如果項目文檔里沒有附帶評估腳本可以用一段樸素實現跑個大概把預測的車道線畫成 2 像素寬的 mask標注也畫成 mask然后逐點比對除以標注像素總數就是近似 accuracy。注意要在光照條件和訓練集接近的幀上評估畢設里最常見的錯誤是拿夜間場景測試白天訓練的模型然后得出「識別率低」的結論——這不是模型問題是任務定義沒劃清楚。5.3 超越霍夫語義分割作為后續擴展方向霍夫變換在處理直線、虛線、簡單彎道時表現足夠但遇到實線虛線混合、道路施工改道、強逆光場景時參數怎么調都有瓶頸。后續如果要擴展項目深度思路是顯式引入反向透視變換把畫面投影到鳥瞰圖再做霍夫或者直接替換成語義分割網絡如 ENet、LaneNet逐像素分類出車道線。前者工程改動小只是多一步相機標定和單應性矩陣計算后者效果更好但數據標注成本翻倍。這個項目標題的價值在于用最少的算力把經典圖像處理和深度學習串成一條完整鏈路——霍夫變換的正確用法是解決局部幾何提取問題而不是和深度模型比拼語義理解能力。把這個定位想清楚后續每一層優化才不會被帶偏方向。本文還有配套的精品資源點擊獲取