简介:这份资源是面向高校学生与Python开发者的毕业设计/课程设计完整项目包,主题为基于深度学习的交通标志识别系统。项目采用YOLOv5完成模型训练,前端界面使用Python编写,后端基于Django框架,数据库为MySQL 5.7,整体代码结构简洁、界面美观,适合作为毕业设计或课程设计参考。功能上支持实景图片检测、笔记本摄像头实时检测,并能在小窗口显示交通标志类型,同时针对低像素模糊、远距离、雾霾、雨天、黑夜等特殊场景做了检测适配,检测结果可保存另存。资源包共570个文件,包含47个py源码、62个js脚本、31个css样式、151个gif与107个png界面素材、2个pth模型权重及sql数据库文件等,压缩包约578.97MB,目录清晰便于二次开发。目前已有66人学习下载,可帮助读者快速获取完整前后端方案、模型训练思路与界面实现参考。
1. 从一张雾天路牌说起:这套 Django + YOLOv5 交通标志识别源码到底能跑出什么
去年帮一个学弟看毕设,他发来一张自己拍的图:傍晚六点半,路灯还没完全亮,前车尾灯糊成一片,路边一块限速 40 的圆牌在画面里只占几十个像素,边缘还带着雨滴反光。他问我,这种图丢进模型里能不能认出来。这个问题其实正好戳中交通标志识别最真实的痛点——不是实验室里那种裁剪得干干净净、正对镜头的标准图,而是实景、低像素、远距离、雾霾、雨天、黑夜这些"非理想条件"。这套基于深度学习的交通标志识别系统源码,就是围绕这个场景做的:前端 Django 页面负责上传图片、调摄像头、展示分类结果,后端用 YOLOv5 做检测和分类,MySQL 存检测记录,检测后的图像支持另存。它适合正在做 python 毕业设计、课程设计的人,也适合想找一个"前后端 + 模型 + 数据库"完整闭环练手的开发者。整套东西不追求算法上的花活,追求的是能跑通、能演示、能讲清楚。
2. 环境与依赖:Python 3.6.8 + MySQL 5.7 这套老组合怎么装才不翻车
这套源码的环境说明写得很明确:Python 3.6.8、MySQL 5.7、Navicat11、PyCharm。看到 Python 3.6.8 这个版本号,熟手心里应该有数了——这是个偏老的版本,很多新库的默认安装会直接报错。所以第一步不是急着 pip install,而是先把版本对齐。
2.1 为什么锁定 Python 3.6.8 而不是直接上 3.10
YOLOv5 早期版本对 Python 版本比较敏感,torch 的某些版本在 3.6 上编译好的 wheel 更全,尤其在 Windows 上。如果你用 3.10 去装老版本 torch,很可能遇到找不到对应 wheel、只能源码编译的情况,编译又要 Visual Studio Build Tools,一圈下来半天没了。常见做法是直接用 conda 建一个 3.6.8 的独立环境,和系统 Python 隔离,避免污染。
# 创建独立环境,避免和系统 Python 冲突 conda create -n traffic_sign python=3.6.8 conda activate traffic_sign # 验证版本,必须是 3.6.8 python --version逻辑说明:conda create -n的-n后面跟环境名,这里叫 traffic_sign,方便识别。参数python=3.6.8强制指定小版本,conda 会去拉对应的解释器。激活后python --version是唯一可信的验证方式,别信 IDE 里显示的,IDE 经常记着旧配置。
2.2 依赖安装顺序:先 torch 再 requirements
很多人拿到源码直接pip install -r requirements.txt,然后卡在 torch 上。正确顺序是先单独装 torch 和 torchvision,再装其余依赖。因为 requirements 里如果写了 torch 但没锁版本,pip 会去拉最新版,最新版早就不支持 3.6 了。
# 先装 torch,指定支持 py3.6 的版本 pip install torch==1.7.1 torchvision==0.8.2 -f https://download.pytorch.org/whl/torch_stable.html # 再装项目其余依赖 pip install -r requirements.txt # Django 单独确认一下版本 pip show django逻辑说明:-f参数指定 wheel 的下载源,torch_stable.html是官方维护的历史版本索引页,能保证 1.7.1 这个版本在 py3.6 下有对应 wheel。pip show django用来确认 Django 版本,这套源码一般配 Django 2.x 或 3.0,如果装成了 4.x,很多老写法会直接报ImportError。
2.3 MySQL 5.7 建库与 Django 连接配置
数据库这块,MySQL 5.7 的安装本身不难,坑在于字符集和 Django 的连接驱动。建库时字符集一定要用 utf8mb4,否则存中文路径或备注会乱码。
-- 建库,字符集用 utf8mb4 CREATE DATABASE traffic_sign_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 建一个专用账号,别用 root 跑应用 CREATE USER 'ts_user'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON traffic_sign_db.* TO 'ts_user'@'localhost'; FLUSH PRIVILEGES;逻辑说明:utf8mb4比utf8多支持 emoji 和部分生僻字,虽然交通标志场景用不上 emoji,但养成习惯没坏处。单独建账号是为了权限隔离,GRANT只给这一个库的权限,FLUSH PRIVILEGES让权限立即生效。
Django 的settings.py里数据库配置对应改成:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'traffic_sign_db', 'USER': 'ts_user', 'PASSWORD': 'your_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': {'charset': 'utf8mb4'}, } }参数说明:ENGINE固定用 mysql 后端,OPTIONS里的 charset 要和建库时一致,否则连接层和存储层字符集不匹配,中文会变成问号。改完执行python manage.py migrate生成表结构,这一步报错八成是驱动没装,pip install mysqlclient即可,Windows 上如果编译失败,换pymysql并在__init__.py里打补丁。
3. YOLOv5 训练与推理:从数据集标注到小窗口显示分类结果
模型这块是整套系统的核心。摘要里说得很清楚,模型只需要用 YOLOv5 训练,系统界面用 Python 写。所以这一章的重点是:数据怎么准备、模型怎么训、训完怎么在 Django 里调起来并把结果画到小窗口上。
3.1 数据集准备与 YOLO 格式标注
交通标志的公开数据集常见的有 GTSRB、TT100K 等,但毕设场景下,很多人会自己拍一批图补充特殊天气样本。标注工具常见用 labelImg,导出 YOLO 格式,每张图对应一个 txt,每行是类别 中心x 中心y 宽 高,坐标都是归一化到 0~1 的值。
# 目录结构建议这样组织 datasets/ images/ train/ val/ labels/ train/ val/ data.yamldata.yaml内容:
train: datasets/images/train val: datasets/images/val nc: 4 names: ['speed_limit', 'no_entry', 'warning', 'direction']参数说明:nc是类别数,这里举例 4 类,实际按你的标注来。names的顺序必须和标注时类别 id 对应,id 0 对应 names 里第一个,错一个位置整个训练就废了。这是血泪经验,标注时类别顺序一旦定了就别改。
3.2 训练命令与关键参数怎么调
YOLOv5 的训练入口是train.py,核心参数不多,但每个都影响结果。
python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data datasets/data.yaml \ --weights yolov5s.pt \ --project runs/train \ --name traffic_sign_v1逻辑说明:--img 640是输入分辨率,交通标志在画面里往往很小,分辨率太低会直接丢特征,640 是平衡速度和精度的常见选择。--batch 16是批大小,显存不够就降到 8 或 4。--epochs 100是训练轮数,小数据集 100 轮通常够收敛,看results.png里的 mAP 曲线,如果还在涨就加。--weights yolov5s.pt用预训练权重做迁移学习,比从零训快得多,小数据集上效果也更稳。--name给这次训练起个名,方便在 runs 目录里区分多次实验。
训练过程中重点看三个指标:mAP@0.5、precision、recall。交通标志场景下 recall 比 precision 更值得关注,因为漏检一个标志比误检更危险。如果 recall 偏低,优先加数据、加分辨率,而不是盲目调学习率。
3.3 在 Django 里加载模型并推理
训练完得到best.pt,把它放到 Django 项目的某个目录下,比如models/best.pt。推理代码封装成一个函数,视图里调用。
import torch import cv2 import numpy as np # 全局加载一次,别每次请求都加载 model = torch.hub.load('ultralytics/yolov5', 'custom', path='models/best.pt', force_reload=False) def detect_image(img_path): # 读取图片,cv2 默认 BGR img = cv2.imread(img_path) # 推理 results = model(img) # 拿到检测框、类别、置信度 detections = results.pandas().xyxy[0] boxes = [] for _, row in detections.iterrows(): if row['confidence'] < 0.4: continue boxes.append({ 'label': row['name'], 'conf': round(float(row['confidence']), 2), 'bbox': [int(row['xmin']), int(row['ymin']), int(row['xmax']), int(row['ymax'])] }) return boxes逻辑说明:torch.hub.load第一次会去拉 yolov5 仓库代码,force_reload=False表示缓存后不再重复拉。模型对象放在模块级全局,Django 启动时加载一次,别写在视图函数里,否则每个请求都重新加载,慢到怀疑人生。results.pandas().xyxy[0]把结果转成 DataFrame,xyxy表示坐标格式是左上右下。置信度阈值 0.4 是经验值,低于这个数的框大多是噪声,可以按实际效果调。
3.4 小窗口显示分类结果与图像另存
摘要里特别提到"能够在一个小窗口显示是什么交通标志",这个用 OpenCV 的imshow或者在前端页面上叠加一个 div 都能做。如果走 Django 前端,常见做法是把检测结果画到图上再返回给页面。
def draw_and_save(img_path, boxes, save_path): img = cv2.imread(img_path) for b in boxes: x1, y1, x2, y2 = b['bbox'] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 在框上方写类别和置信度 text = f"{b['label']} {b['conf']}" cv2.putText(img, text, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(save_path, img) return save_path参数说明:cv2.rectangle的线宽 2 和颜色(0,255,0)是 BGR 顺序的绿色。cv2.putText的(x1, y1-8)让文字在框上方,0.6是字体缩放,2是线宽。cv2.imwrite保存到指定路径,这个路径可以存到 MySQL 的记录表里,方便历史查询。注意 OpenCV 的imwrite对中文路径支持不好,保存路径尽量用英文或数字,否则会静默失败——这是典型的玄学问题,图没存上还不报错。
4. 摄像头实时检测与特殊天气场景:从笔记本摄像头到雾天黑夜的应对
摘要里第二条和第三条分别提到摄像头检测和特殊天气检测。这两块其实是同一个技术链路的不同输入源,摄像头是实时流,特殊天气是图像质量退化。分开讲清楚。
4.1 笔记本摄像头实时检测链路
摄像头检测用 OpenCV 的VideoCapture,逐帧读、逐帧推理。但这里有个性能问题:YOLOv5 在 CPU 上跑一帧可能要几百毫秒,直接逐帧推理会卡成幻灯片。常见做法是抽帧,比如每 5 帧推理一次,中间帧复用上一次的结果。
import cv2 import torch model = torch.hub.load('ultralytics/yolov5', 'custom', path='models/best.pt', force_reload=False) cap = cv2.VideoCapture(0) # 0 表示默认摄像头 frame_count = 0 last_boxes = [] while True: ret, frame = cap.read() if not ret: break frame_count += 1 # 每 5 帧推理一次 if frame_count % 5 == 0: results = model(frame) last_boxes = results.pandas().xyxy[0] # 用上一次结果画框 for _, row in last_boxes.iterrows(): if row['confidence'] < 0.4: continue cv2.rectangle(frame, (int(row['xmin']), int(row['ymin'])), (int(row['xmax']), int(row['ymax'])), (0, 255, 0), 2) cv2.imshow('Traffic Sign Detection', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()逻辑说明:VideoCapture(0)的 0 是摄像头索引,外接摄像头可能是 1。frame_count % 5控制推理频率,5 是经验值,机器好可以降到 3,机器差就升到 10。last_boxes缓存上一次结果,保证画面上的框不会闪。cv2.waitKey(1)里的 1 是等待毫秒数,配合ord('q')实现按 q 退出。这套逻辑在 Django 里要改成把帧编码成字节流返回给前端,或者用 WebSocket 推,但核心抽帧思路一样。
4.2 特殊天气与低质量图像的应对策略
雾霾、雨天、黑夜、远距离、低像素,这些场景的共同点是图像对比度低、细节丢失。模型层面能做的有限,更多要在预处理和数据增强上下功夫。
常见做法有三条:一是训练时做数据增强,YOLOv5 自带 HSV 增强、马赛克增强,可以额外加雾化、加噪声、调亮度;二是推理前做图像增强,比如 CLAHE 自适应直方图均衡化,能明显改善雾天和黑夜的对比度;三是针对远距离小目标,提高输入分辨率或改用更大的模型(yolov5m/l)。
import cv2 def enhance_low_quality(img): # 转 LAB 空间,只对 L 通道做 CLAHE lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) l = clahe.apply(l) lab = cv2.merge((l, a, b)) return cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)参数说明:clipLimit=2.0控制对比度增强的强度,太大噪声会被放大,太小没效果,2.0 是常用起点。tileGridSize=(8,8)是分块大小,块越小局部增强越明显,但计算量也越大。转 LAB 空间是因为 L 通道是亮度,单独增强亮度不会影响颜色,比直接在 BGR 上做均衡化更自然。
提示:CLAHE 对雾天和黑夜有效,但对运动模糊基本没用。运动模糊要靠提高快门速度或后期去模糊,模型层面解决不了。
4.3 检测结果入库与历史查询
每次检测完,把图片路径、检测到的类别、置信度、时间戳存进 MySQL,前端就能做一个历史记录页。Django 的 ORM 写起来很直接。
from django.db import models class DetectionRecord(models.Model): image_path = models.CharField(max_length=255) label = models.CharField(max_length=64) confidence = models.FloatField() created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'detection_record'参数说明:auto_now_add=True让创建时间自动写入,不用手动传。db_table指定表名,不写的话 Django 会用app名_模型名的默认格式。存的时候一条检测记录一行,如果一张图有多个标志就存多行,查询时按image_path聚合即可。
5. 避坑与排查:这套源码跑不起来时先看这几条
这套源码的坑,大多集中在环境版本、路径、模型加载和中文处理上。下面几条是我实际遇到过、也见过别人反复踩的。
5.1 现象:pip install -r requirements.txt卡在 torch 下载
原因:requirements 里没锁 torch 版本,pip 去拉最新版,最新版不支持 Python 3.6,于是反复重试或直接超时。
解决:先单独装 torch 1.7.1 和 torchvision 0.8.2,用官方 wheel 索引,再装其余依赖。命令见 2.2 节。
5.2 现象:Django 启动报No module named 'MySQLdb'
原因:Django 的 mysql 后端默认用 MySQLdb,也就是 mysqlclient,Windows 上编译这个包需要 C 编译器,很多人装不上。
解决:两条路。一是装 mysqlclient 的预编译 wheel,pip install mysqlclient在部分版本上能直接成功;二是改用 pymysql,在项目__init__.py里加:
import pymysql pymysql.install_as_MySQLdb()这样 Django 会把 pymysql 当成 MySQLdb 用,纯 Python 实现,不需要编译。
5.3 现象:检测结果的中文类别名显示成方块
原因:OpenCV 的putText不支持中文,字体里没有中文字形,画出来就是方块或问号。
解决:要么类别名用英文,要么用 PIL 的ImageDraw配合中文字体文件来画。毕设演示场景下,类别名用英文最省事,前端页面上再用中文映射表显示。
5.4 现象:摄像头能打开但画面全黑或卡死
原因:摄像头被其他程序占用,或者VideoCapture的索引不对,或者驱动问题。
解决:先确认没有其他程序(比如微信、钉钉、Zoom)占着摄像头。然后换索引试,0 不行试 1、2。Windows 上如果还不行,在VideoCapture后加cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)和cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)强制设分辨率,有些摄像头默认分辨率不被支持就会黑屏。
5.5 现象:模型推理报CUDA out of memory
原因:显存不够,batch 太大或图片分辨率太高。
解决:推理时把--img降到 416 或 320,或者强制用 CPU:model.to('cpu')。毕设演示对速度要求不高的话,CPU 推理完全够用,只是慢一点。
6. 进阶技巧:把检测结果做成可复现的验证流程
到这一步,系统基本能跑了。但毕设答辩或者课程设计验收时,光"能跑"不够,还得能证明"跑得对"。我一般会做一套固定的验证流程,把结果量化出来,这样答辩时心里有底。
第一步是准备一个固定的小测试集,从验证集里抽 50 到 100 张,覆盖正常、雾天、黑夜、远距离几类场景,每类至少 10 张。这个测试集不要参与训练,专门用来验证。
第二步是写一个批量推理脚本,把测试集跑一遍,输出每张图的检测结果和真实标注的对比。
import os import cv2 import torch model = torch.hub.load('ultralytics/yolov5', 'custom', path='models/best.pt', force_reload=False) test_dir = 'test_images' result_lines = [] for fname in os.listdir(test_dir): if not fname.lower().endswith(('.jpg', '.png')): continue path = os.path.join(test_dir, fname) img = cv2.imread(path) results = model(img) dets = results.pandas().xyxy[0] # 只保留置信度大于 0.4 的 dets = dets[dets['confidence'] > 0.4] labels = dets['name'].tolist() result_lines.append(f"{fname}: {labels}") with open('test_result.txt', 'w', encoding='utf-8') as f: f.write('\n'.join(result_lines))逻辑说明:这段脚本遍历测试目录,逐张推理,把文件名和检测到的类别写进文本文件。dets['confidence'] > 0.4是过滤低置信度框,和推理时保持一致。输出用 utf-8 编码,避免中文类别名写文件时报错。
第三步是人工核对test_result.txt,统计漏检和误检。漏检就是真实有标志但没检测出来,误检就是检测出了不存在的标志。交通标志场景下,漏检的代价更大,所以如果漏检多,优先加数据、加分辨率;如果误检多,提高置信度阈值。
第四步是把这套流程固化成一个脚本,每次改了模型或参数就重跑一遍,对比前后结果。这样你能清楚知道每次改动是变好了还是变差了,而不是凭感觉。
注意:验证集和测试集一定要分开。我见过有人拿训练集当测试集,mAP 高得离谱,一上真实场景就原形毕露。这是最典型的自欺欺人。
从那以后我每次改完模型参数,都强制走一遍这套批量验证流程,哪怕只是调了个置信度阈值。因为模型这东西,你不量化它,它就跟你玩玄学。希望这套源码和上面的流程,能帮你把毕设稳稳当当地做完。
本文还有配套的精品资源,点击获取