基于深度学习的交通标志与行人车辆检测系统 —— 功能与技术点详解
一个把「YOLOv8 目标检测」和「PyQt5 桌面界面」完整打通的实战项目:既能检测交通标志,也能识别行人与车辆,支持图片、文件夹批量、视频、摄像头四种输入方式。
通过网盘分享的文件:交通标志与行人车辆检测系统8.zip
链接: https://pan.baidu.com/s/1SzfiXqEh0k8bRzFXYU0OyQ?pwd=ek4j 提取码: ek4j
–来自百度网盘超级会员v7的分享
一、

项目背景
交通场景下的感知任务是计算机视觉最经典的应用之一:交通信号灯、限速标志、人行横道,以及行人、公交车、汽车、卡车等目标,都是自动驾驶与智能交通的基础感知对象。
本项目实现了一套开箱即用的桌面级检测系统,把训练好的 YOLOv8 模型封装进图形界面,让不懂编程的使用者也能直接拖入图片或视频完成检测,同时保留完整的参数调节与结果导出能力。
二、系统功能一览
2.1 检测类别(共 9 类)
| 0 | Trafic Light Signal | 交通信号灯 |
| 1 | Stop Signal | 停止信号 |
| 2 | Speedlimit Signal | 限速信号 |
| 3 | Crosswalk Signal | 人行横道信号 |
| 4 | Crosswalk | 人行横道 |
| 5 | Pedestrian | 行人 |
| 6 | Bus | 公交车 |
| 7 | Car | 汽车 |
| 8 | Truck | 卡车 |
2.2 核心功能
| 单张图片检测 | 支持 jpg / jpeg / png / bmp,检测完成后在界面展示带框结果 |
| 文件夹批量检测 | 选中整个文件夹,自动遍历所有图片逐张检测,结果追加进结果表格 |
| 视频文件检测 | 支持 avi / mp4 / wmv / mkv,逐帧检测并实时刷新预览画面 |
| 摄像头实时检测 | 调用本机摄像头(默认设备 0),实时画面 + 实时检测 |
| 检测参数可调 | 置信度阈值、交并比阈值(IOU)界面实时调节,即时生效 |
| 标签显示开关 | 可一键切换「显示类别与置信度」或「只画检测框」 |
| 目标逐个查看 | 下拉框选择「全部」或某个具体目标,单独查看该目标的框与坐标 |
| 结果信息表格 | 序号、文件路径、类别、置信度、坐标位置,逐条列出 |
| 结果导出 | 图片结果写入 save_data,视频结果导出为带检测框的 avi 文件 |
| 训练脚本 | 提供 train.py,可基于自有数据集重新训练模型 |
三、技术架构
整个系统可以划分为三层,层次清晰、职责单一:
┌─────────────────────────────────────────────┐
│ 界面层(PyQt5) │
│ UiMain.py / style.css / 各类控件与信号槽 │
├─────────────────────────────────────────────┤
│ 逻辑层(MainProgram.py) │
│ 业务调度:读图/读视频/摄像头、参数管理、 │
│ 结果展示、结果保存、多线程 │
├─────────────────────────────────────────────┤
│ 推理层(ultralytics + PyTorch) │
│ YOLOv8 模型加载与推理,输出 boxes 结果 │
├─────────────────────────────────────────────┤
│ 工具层(detect_tools.py / Config.py) │
│ 图像格式转换、绘制、路径读取、全局配置 │
└─────────────────────────────────────────────┘
- 界面层:由 Qt Designer 设计 .ui 文件并生成 UiMain.py,通过 style.css 做 QSS 样式美化。
- 逻辑层:MainProgram.py 中的 MainWindow 类负责把界面事件与推理逻辑串起来。
- 推理层:使用 ultralytics 的 YOLO 接口加载 models/best.pt 完成推理。
- 工具层:Config.py 集中管理模型路径、类别名称、保存路径;detect_tools.py 提供绘图与格式转换等通用函数。
四、核心技术点拆解
4.1 目标检测:YOLOv8 单阶段检测
项目使用 YOLOv8(ultralytics 8.0.199)作为检测模型,任务类型为 detect:
# MainProgram.py
from ultralytics import YOLO
self.model = YOLO(Config.model_path, task='detect')
加载模型后,一次推理即可拿到所有目标框。结果从 results 对象中按字段取出,结构非常清晰:
results = self.model(img_path, conf=self.conf_thres, iou=self.iou_thres)[0]
location_list = results.boxes.xyxy.tolist() # 每个目标的 [x1, y1, x2, y2]
cls_list = results.boxes.cls.tolist() # 每个目标的类别 ID
conf_list = results.boxes.conf.tolist() # 每个目标的置信度
这种「一次前向 + 三个并行列表」的组织方式,为后续界面展示提供了极大便利。
4.2 模型训练策略
模型基于 YOLOv8 架构训练,关键超参数如下(来自训练结果目录中的配置记录):
| task | detect | 目标检测任务 |
| epochs | 50 | 训练轮数 |
| batch | 4 | 批大小(显存/内存友好) |
| imgsz | 640 | 输入分辨率 |
| optimizer | SGD | 优化器 |
| lr0 / lrf | 0.01 / 0.01 | 初始学习率 / 最终学习率系数 |
| momentum | 0.937 | 动量 |
| weight_decay | 0.0005 | 权重衰减 |
| warmup_epochs | 3.0 | 预热轮数 |
| close_mosaic | 10 | 最后 10 轮关闭 Mosaic 增强 |
| fliplr | 0.5 | 随机水平翻转增强 |
| mosaic | 1.0 | Mosaic 数据增强 |
| erasing | 0.4 | 随机擦除增强 |
| degrees / shear / perspective | 0 / 0 / 0 | 未启用旋转、错切、透视增强 |
几个值得注意的工程取向:
- 数据增强适中:启用了 Mosaic、水平翻转、HSV 色彩扰动,但关闭了旋转、透视等强几何变换——交通场景的目标朝向相对固定,过强的几何增强反而会引入噪声。
- 最后阶段关闭 Mosaic:close_mosaic=10 让模型在训练末期回归真实分布,提升最终精度。
- batch=4 与 workers=0:典型的「消费级显卡 / CPU 环境也能跑」的保守配置。
训练结果(损失曲线、PR 曲线、混淆矩阵、验证集预测可视化等)全部保存在 models/exp27 目录中,可直接用于分析模型表现。
4.3 PyQt5 界面:Qt Designer + QSS
界面采用「可视化设计 + 代码生成」的方式:
- .ui 文件由 Qt Designer 设计,UiMain.py 由它生成;
- 业务代码通过多重继承方式复用生成的界面类:
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self) # 装载设计好的界面
self.initMain() # 业务初始化
self.signalconnect() # 绑定信号与槽
- 外观通过外部 QSS 文件统一管理,样式与代码解耦:
style_file = 'UIProgram/style.css'
self.setStyleSheet(QSSLoader.read_qss_file(style_file))
这种写法的好处是:改界面不用动业务逻辑,改样式不用重新生成界面代码。
4.4 信号与槽:界面响应的事件驱动模型
所有交互都通过 Qt 的信号槽机制绑定,一处集中注册,结构一目了然:
def signalconnect(self):
self.PicBtn.clicked.connect(self.open_img) # 打开图片
self.FilesBtn.clicked.connect(self.detact_batch_imgs) # 批量检测
self.VideoBtn.clicked.connect(self.vedio_show) # 打开视频
self.CapBtn.clicked.connect(self.camera_show) # 打开摄像头
self.SaveBtn.clicked.connect(self.save_detect_video) # 保存结果
self.ExitBtn.clicked.connect(QCoreApplication.quit) # 退出
self.comboBox.activated.connect(self.combox_change) # 目标切换
4.5 多线程:让视频保存不再卡死界面
视频逐帧推理是一个耗时操作,如果直接放在主线程里,界面会「未响应」。项目用 QThread 把它挪到后台线程,并通过信号回传进度:
class btn2Thread(QThread):
update_ui_signal = pyqtSignal(int, int) # 当前帧数, 总帧数
def run(self):
while cap.isOpened() and self.is_running:
ret, frame = cap.read()
results = self.model(frame, conf=self.conf, iou=self.iou)[0]
out.write(results.plot())
self.update_ui_signal.emit(cur_num, total) # 上报进度
主线程收到信号后更新进度条:
self.btn2Thread_object.update_ui_signal.connect(self.update_process_bar)
同时还设置了 is_running 标志位,支持用户中途取消保存(关闭进度条窗口即终止线程),这是一个很实用的健壮性设计。
4.6 图像渲染:从 OpenCV 到 Qt 的无缝转换
OpenCV 使用 BGR 通道顺序、numpy 数组存储;Qt 需要 QImage 才能显示。项目封装了标准转换函数:
def cvimg_to_qpiximg(cvimg):
height, width, depth = cvimg.shape
cvimg = cv2.cvtColor(cvimg, cv2.COLOR_BGR2RGB) # BGR → RGB
qimg = QImage(cvimg.data, width, height, width * depth, QImage.Format_RGB888)
return QPixmap(qimg)
同时,为了让不同比例的图片都完整显示在固定大小的显示区内,实现了等比自适应缩放:
def get_resize_size(self, img):
img_height, img_width, _ = img.shape
ratio = img_width / img_height
if ratio >= self.show_width / self.show_height:
self.img_width = self.show_width
self.img_height = int(self.img_width / ratio)
else:
self.img_height = self.show_height
self.img_width = int(self.img_height * ratio)
return self.img_width, self.img_height
4.7 中文标签绘制:绕开 OpenCV 的短板
OpenCV 的 cv2.putText 不支持中文,直接写会显示成「???」。项目的做法是交给 PIL 绘制:
def drawRectBox(image, rect, addText, fontC, color):
cv2.rectangle(image, (rect[0], rect[1]), (rect[2], rect[3]), color, 2)
cv2.rectangle(image, (rect[0] – 1, rect[1] – 25), (rect[0] + 60, rect[1]), color, –1)
img = Image.fromarray(image)
draw = ImageDraw.Draw(img)
draw.text((rect[0] + 2, rect[1] – 27), addText, (255, 255, 255), font=fontC)
return np.array(img)
字体在程序启动时加载一次并复用,避免每帧都读字体文件:
from PIL import ImageFont
self.fontC = ImageFont.truetype("Font/platech.ttf", 25, 0)
4.8 参数动态调节:改阈值即时重检
置信度和 IOU 阈值不是「设完重启才生效」,而是改动即触发重检,交互体验更好:
def update_conf_thres(self, value):
self.conf_thres = value
if hasattr(self, 'model'):
self.model.conf = value
if hasattr(self, 'org_img'):
self.detect_current_image() # 立即用新阈值重新检测当前图片
滑块滚动过程中就能直观看到「哪些目标被过滤掉了」,非常适合做阈值调优演示。
4.9 中文路径读取:cv2.imread 的隐藏坑
cv2.imread 在中文路径下会静默失败(返回 None)。项目改用 imdecode 方案规避:
def img_cvread(path):
# 读取含中文名的图片文件
img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR)
return img
在本机实测中,项目位于含中文的目录下依然可以正常运行。
4.10 模型预热与设备自动选择
两个容易被忽略但很影响体验的细节:
设备自动选择——有 GPU 用 GPU,没有就退回 CPU:
self.device = 0 if torch.cuda.is_available() else 'cpu'
模型预热——启动时先用一张小图跑一次推理,把算子/runtime 初始化提前完成,避免用户点第一张图时卡顿:
self.model = YOLO(Config.model_path, task='detect')
self.model(np.zeros((48, 48, 3)), device=self.device) # 预先加载推理模型
五、界面交互设计
界面整体分为左右两大区域,符合「左边看结果、右边看数据」的使用习惯:
- 左侧检测显示区:展示原图 / 检测结果图 / 视频帧,中央自适应缩放。
- 右侧检测结果区:
- 顶部为检测参数设置(标签开关、置信度阈值、交并比阈值);
- 中部为检测信息面板(目标数目、目标选择下拉框、类型、置信度、xmin/ymin/xmax/ymax、用时);
- 底部为结果表格(序号 / 文件路径 / 类别 / 置信度 / 坐标位置)。
- 底部操作栏:打开图片、打开文件夹、打开视频、打开摄像头、保存、退出。
批量检测时表格会自动滚动到最新一行,并通过 QApplication.processEvents() 实时刷新界面,让用户看到「逐张处理」的过程而不是假死。
六、性能实测
在纯 CPU 环境下(不依赖显卡)实测:
| 单张图片推理耗时 | 约 58 ~ 71 ms(640×640 输入) |
| 前处理 | 约 1 ~ 12 ms |
| 后处理 | 约 1 ms |
| 运行设备 | CPU(torch.cuda.is_available() 为 False 时自动回退) |
也就是说,CPU 下单张图片约 0.06 ~ 0.07 秒即可完成检测,实时视频与摄像头场景也完全可用(30 FPS 视频需约 33 ms/帧,实测接近该量级)。若配备 GPU 并安装 CUDA 版 PyTorch,速度还可进一步提升。
七、工程化细节与踩坑记录
| 模型加载失败 | ModuleNotFoundError: No module named 'dill' | best.pt 使用 dill 序列化,需在虚拟环境中 pip install dill |
| 中文路径读图失败 | cv2.imread 返回 None | 改用 cv2.imdecode + np.fromfile |
| 中文标签乱码 | cv2.putText 显示问号 | 改用 PIL ImageDraw + TrueType 字体绘制 |
| 视频保存时界面卡死 | 主线程被推理阻塞 | 用 QThread 后台推理 + 信号回传进度 |
| 换机器后训练报错 | dataset.yaml 里是绝对路径 | 训练前改为自己的数据集路径 |
| ultralytics 自动装依赖装错环境 | dill 装到系统 Python 而非虚拟环境 | 手动在虚拟环境中 pip install dill |
八、可扩展方向
九、总结
这个项目最大的价值在于闭环完整:从数据准备、模型训练(train.py + dataset.yaml),到推理封装(ultralytics + best.pt),再到界面交互(PyQt5)、多线程优化、结果导出,形成了一条可以直接交付给使用者的完整链路。
技术上它覆盖了几个非常实用的工程要点:
- 单阶段检测器的一次前向多字段解析;
- Qt 界面与业务逻辑的解耦(Qt Designer + QSS + 信号槽);
- 耗时任务的后台线程化与进度反馈;
- 跨库协作时的格式转换(OpenCV ↔ Qt ↔ PIL);
- 中文路径、中文标签这类「国产环境下的经典坑」的规避方案。
对于想入门「深度学习 + 桌面应用」落地的开发者来说,这是一个很好的参考样本。
网硕互联帮助中心





评论前必须登录!
注册