当前位置: 首页 > news >正文

【项目实战】基于OpenCV和BDD100K数据集的辅助驾驶车道线检测与碰撞预警系统

🌟 项目简介

本文带你从零实现一个辅助驾驶视觉系统,核心功能包括:

  • 车道线分割:使用传统图像处理算法在道路图像上绘制绿色车道线。

  • 目标检测与距离估计:识别前方车辆和行人,估算距离并显示在画面中。

  • 碰撞预警:当距离过近时,在画面显示红色警告并在控制台模拟“嘀嘀嘀”报警声。

本项目的最大特点是完全离线运行,不需要深度学习框架(PyTorch/TensorFlow),也不需要联网下载模型,非常适合作为计算机视觉入门项目或课程作业。


🗂️ 数据集说明

我们使用的是Kaggle上的BDD100K YOLO格式数据集(由用户a7madmostafa上传)。

  • 数据内容:包含行车记录仪视角的道路图片。

  • 标注格式:YOLO格式(.txt文件),每行包含class_id x_center y_center width height(归一化坐标)。

  • 关注类别:车辆(car/truck/bus)、行人(person)等交通参与者。

数据集挂载路径(Kaggle环境)

text

/kaggle/input/datasets/a7madmostafa/bdd100k-yolo/test/images

📦 完整代码(可直接运行)

python

import cv2 import numpy as np import os import matplotlib.pyplot as plt # ============================================================ # 1. 路径配置(根据不同环境修改此处) # ============================================================ DATA_ROOT = "/kaggle/input/datasets/a7madmostafa/bdd100k-yolo" IMAGE_DIR = os.path.join(DATA_ROOT, "test/images") LABEL_DIR = os.path.join(DATA_ROOT, "test/labels") # 若不存在则尝试其他路径 # 类别映射表 (BDD100K标准) CLASS_NAMES = { 0: 'car', 1: 'truck', 2: 'bus', 3: 'traffic light', 4: 'traffic sign', 5: 'person', 6: 'bicycle', 7: 'motorcycle', 8: 'train', 9: 'rider' } # 只关心这些类别(车辆和行人) INTERESTED_CLASSES = [0, 1, 2, 3, 5, 6, 7, 9] # ============================================================ # 2. 车道线检测(前端展示:绿色线条) # ============================================================ def detect_lanes(image): """ 传统OpenCV车道线检测流程: 灰度化 -> 高斯模糊 -> Canny边缘检测 -> ROI掩码 -> 霍夫直线检测 """ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) edges = cv2.Canny(blur, 50, 150) height, width = edges.shape # 定义梯形/三角形感兴趣区域(只关注前方路面) mask = np.zeros_like(edges) polygon = np.array([[(0, height), (width, height), (width // 2, height // 2)]], np.int32) cv2.fillPoly(mask, polygon, 255) masked_edges = cv2.bitwise_and(edges, mask) # 霍夫变换检测直线 lines = cv2.HoughLinesP(masked_edges, rho=1, theta=np.pi/180, threshold=30, minLineLength=80, maxLineGap=30) lane_img = np.copy(image) if lines is not None: for line in lines: x1, y1, x2, y2 = line[0] # 【前端】绘制绿色粗线 cv2.line(lane_img, (x1, y1), (x2, y2), (0, 255, 0), 4) return lane_img # ============================================================ # 3. 距离估计(基于目标面积) # ============================================================ def estimate_distance(area, frame_area): """ 利用目标在画面中的面积占比粗略估算距离。 原理:面积越大 -> 距离越近 (反比关系)。 假设:面积占比0.01时距离为10米。 """ if area < 500: # 忽略噪声小点 return 99.0 ratio = area / frame_area if ratio < 0.001: # 距离太远忽略 return 99.0 return min(10.0 * (0.01 / ratio) ** 0.5, 80.0) # ============================================================ # 4. 解析YOLO标注文件(后端数据处理) # ============================================================ def get_boxes_from_label(label_path, img_shape): """ 读取txt标注文件,将归一化坐标转为像素坐标。 格式:class_id x_center y_center width height (归一化0~1) """ height, width = img_shape[:2] boxes = [] if not os.path.exists(label_path): return boxes with open(label_path, 'r') as f: lines = f.readlines() for line in lines: parts = line.strip().split() if len(parts) != 5: continue cls_id = int(parts[0]) if cls_id not in INTERESTED_CLASSES: continue # 解析归一化坐标 x_c, y_c, w_n, h_n = map(float, parts[1:]) # 还原为像素坐标 (左上角x, y, 宽度, 高度) x = int((x_c - w_n/2) * width) y = int((y_c - h_n/2) * height) w = int(w_n * width) h = int(h_n * height) # 边界裁剪,防止溢出画面 x = max(0, x) y = max(0, y) w = min(width - x, w) h = min(height - y, h) if w > 0 and h > 0: boxes.append((cls_id, x, y, w, h)) return boxes # ============================================================ # 5. 图像处理主流程(前端渲染 + 后端计算) # ============================================================ def process_image(img_path, label_path): # 读取图片并缩放到统一尺寸(加速处理) img = cv2.imread(img_path) if img is None: return None img = cv2.resize(img, (800, 450)) orig_h, orig_w = img.shape[:2] frame_area = orig_w * orig_h # 1. 车道线检测(前端绘制绿线) result = detect_lanes(img) # 2. 读取标注框(后端数据) boxes = get_boxes_from_label(label_path, img.shape) warning_triggered = False min_distance = 99.0 # 3. 遍历每个检测框 for cls_id, x, y, w, h in boxes: area = w * h dist = estimate_distance(area, frame_area) min_distance = min(min_distance, dist) # 根据距离着色:>8米绿色安全,<8米红色危险 color = (0, 255, 0) if dist > 8 else (0, 0, 255) # 【前端】绘制矩形框和距离文本 cv2.rectangle(result, (x, y), (x+w, y+h), color, 2) label = f"{CLASS_NAMES.get(cls_id, 'obj')} {dist:.1f}m" cv2.putText(result, label, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # 触发预警逻辑 if dist < 8.0: warning_triggered = True # 4. 【前端】显示预警信息 if warning_triggered: cv2.putText(result, "!!! WARNING: Collision Risk !!!", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 3) # 控制台模拟蜂鸣声(\a为ASCII响铃符) print("🔊 嘀嘀嘀!前方距离过近!", end=" ") if min_distance < 99: cv2.putText(result, f"Min Dist: {min_distance:.1f}m", (30, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 0), 2) # 转为RGB格式供matplotlib显示 return cv2.cvtColor(result, cv2.COLOR_BGR2RGB) # ============================================================ # 6. 主程序入口(模拟视频流播放) # ============================================================ if __name__ == "__main__": # 检查目录是否存在 if not os.path.exists(IMAGE_DIR): print(f"❌ 错误:图片目录不存在!请检查路径。") exit() # 获取前20张图片(数据量大,只取部分演示) image_files = sorted([f for f in os.listdir(IMAGE_DIR) if f.endswith(('.jpg', '.jpeg', '.png'))])[:20] print(f"✅ 共加载 {len(image_files)} 张图片,开始处理...") # 使用matplotlib交互模式动态显示 fig, ax = plt.subplots(figsize=(12, 6)) plt.ion() # 打开交互模式 for img_file in image_files: img_path = os.path.join(IMAGE_DIR, img_file) label_file = img_file.rsplit('.', 1)[0] + '.txt' label_path = os.path.join(LABEL_DIR, label_file) result_rgb = process_image(img_path, label_path) if result_rgb is None: continue # 【前端】刷新显示 ax.clear() ax.imshow(result_rgb) ax.set_title(f"Processing: {img_file}") ax.axis('off') plt.draw() plt.pause(0.3) # 每0.3秒切换一张,模拟视频帧率 plt.ioff() plt.show() print("\n✅ 全部处理完成!")

🔍 代码核心原理解析

1. 车道线检测(detect_lanes

  • Canny边缘检测:提取图像中梯度变化剧烈的像素点(即边缘)。

  • ROI掩码:由于车道线通常只出现在图像下方区域,我们用一个三角形掩码过滤掉天空、树木等无关区域,大幅减少误检。

  • 霍夫变换(HoughLinesP):在边缘图像中寻找直线段。参数threshold=30控制检测灵敏度,minLineLength=80过滤短线段。

2. YOLO标注解析(get_boxes_from_label

YOLO格式的坐标是归一化的(0~1),需要乘以图片的宽/高才能还原为像素值。注意这里的x_center, y_center是中心点,转换为左上角坐标时需减半宽高。

3. 距离估计算法(estimate_distance

这是一种单目视觉的简化近似,假设物体在图像中的面积占比与真实距离平方成反比。虽然不如双目视觉或激光雷达精准,但足以满足演示和预警逻辑的需求。实际项目中若需精准值,需要进行相机标定。

4. 预警逻辑

  • 距离阈值:设定为8米,当任意感兴趣目标小于该值时触发。

  • 前端反馈:画面顶部显示红色英文警告。

  • 后端反馈:控制台打印嘀嘀嘀(利用\a转义符可触发系统蜂鸣声)。


🧩 前端与后端的分工

模块类型具体代码作用
车道线绘制前端cv2.line(..., (0,255,0), 4)将检测到的直线渲染为绿色
目标框绘制前端cv2.rectangle()/cv2.putText()可视化检测结果和距离
预警文字前端cv2.putText(..., (0,0,255))显示红色预警提示
控制台输出前端print("嘀嘀嘀...")模拟蜂鸣报警声
图像显示前端plt.imshow()/plt.pause()在Notebook内展示图片序列
边缘检测后端cv2.Canny()/cv2.GaussianBlur()图像预处理和特征提取
直线拟合后端cv2.HoughLinesP()计算车道线几何参数
坐标转换后端归一化坐标转像素坐标解析标注数据
距离计算后端estimate_distance()根据面积计算距离数值

⚠️ 踩坑与排障指南

1. 路径找不到(FileNotFoundError)

  • 原因:Kaggle数据集的挂载路径因用户而异。

  • 解决:运行os.listdir('/kaggle/input')查看实际根目录名称,修改DATA_ROOT变量。

2. 找不到标签文件

  • 原因:部分数据集版本只有图片,没有labels文件夹。

  • 解决:若缺少标注,可去掉get_boxes_from_label调用,仅保留车道线检测功能。或者尝试遍历os.walk()查找.txt文件位置。

3. Matplotlib无法动态显示

  • 原因:plt.ion()在某些Jupyter内核中不稳定。

  • 解决:改用cv2.imshow()(需要GUI环境)或IPython.display.clear_output()刷新图片。

4. 内存溢出(Kaggle Kernel Died)

  • 原因:图片尺寸过大或一次性加载过多。

  • 解决:使用cv2.resize()统一缩小至800x450;仅读取前20张而非全量数据。


🚀 扩展与改进方向

  • 引入YOLOv8模型:替换标注文件读取,实现端到端的目标检测,提升准确率。

  • 车道偏离预警:计算车辆中心与车道线的横向偏移,当偏离时触发警报。

  • 测距标定:使用棋盘格标定相机内参,将像素面积映射为真实物理距离(米)。

  • 多线程加速:使用OpenMP或CUDA加速图像处理流水线。


💬 结语

本文完整实现了一个轻量级的辅助驾驶视觉系统,无需昂贵的GPU无需深度学习,仅靠OpenCV和简单的几何逻辑即可运行。适合作为计算机视觉课程的期末作业或入门项目。希望本文能帮助你理解图像处理的基础流程,并激发你对自动驾驶技术的兴趣!

如果你在运行过程中遇到任何问题,欢迎在评论区留言交流。

项目环境:Python 3.10 + OpenCV 4.8 + Matplotlib 3.7
数据集来源:Kaggle - BDD100K YOLO Format Dataset

http://www.jsqmd.com/news/1105992/

相关文章:

  • 卡梅德生物科普:CD48(SLAMF2)的免疫调控机制与研究工具选择
  • SQL 复杂查询优化:先减少扫描,再谈语法漂亮
  • Better BibTeX 终极指南:告别LaTeX文献管理的混乱时代
  • 6. 深入 Nginx 核心:HTTP 11 个处理阶段与模块开发实战
  • 轻量级AI模型实战:低配设备部署与优化指南
  • 【2026年华为暑期实习(AI)-7月1日-第三题- Certainty Forcing 训练损失计算】(题目+思路+JavaC++Python解析+在线测试)
  • 基于ICM-42605和GD32VF103的6DOF运动追踪系统设计
  • adb截图-------在小程序中实现纯 JS 驱动的 ADB 客户端
  • 输入输出流重载说明:std::ostream operator<<(std::ostream os, const Vector v)
  • AI 辅助:前端工程化效率:快不是少检查,而是少返工
  • Python在AI开发中的核心优势与实战技巧
  • 变分量子本征求解器(VQE)原理与NISQ设备应用
  • 深度学习Pipeline与Baseline构建指南
  • 【6.20】射频\+FPGA\+Verilog\+仪器自动化 完整知识链路复盘
  • 智能体时代,软件工程的本质
  • 现在系统运行基本上正常,较少遇到问题了
  • 采齿背后的能量闭包原理
  • 截屏、OCR、翻译、录屏全打包?这款开源软件,一个快捷键搞定所有!
  • OpenHarmony 英语学习 App 实战:从 0 到 1 搭建中小学生英语学习应用
  • 工程化赋能传统业务工作流:先找重复劳动,不要先找服务
  • 大模型评测与AI产品质量保障:第7篇 机器学习的三种学习范式
  • SQL实战:测试必会的增删改查,从入门到熟练
  • SpringBoot 自动配置原理
  • 记忆排列题目分析
  • 第93题 IGBT模块陶瓷基板(AlN/Al₂O₃/Si₃N₄)金属化
  • C++ PDF解析渲染库Poppler全方位实战:场景、库对比、CMake集成、可运行代码
  • 死磕信号量实现读者-写者:我被自己写的代码坑惨了
  • 市县级全域旅游智慧导览电子地图制作实操(三)AI+人工生成全域手绘地图
  • Xinference开源大模型本地部署实战指南
  • 工业级条码扫描模块与PIC32MZ嵌入式方案解析