`)
简介本资源是一套面向本科毕业设计的疲劳检测系统完整实现方案适用于计算机、人工智能或软件工程专业学生开展课程设计、毕设开发与算法实践。系统基于PythonDjango框架构建Web交互界面核心采用OpenCV实现人脸检测、眼睛闭合度分析与眨眼频次统计结合MySQL数据库完成用户数据、检测记录及图像管理可有效支撑疲劳驾驶预警等实际场景的原型验证。资源为1个1.01MB的.docx文件内含完整学士学位论文含摘要、绪论、系统设计、实验分析、参考文献及目录涵盖技术原理、模块实现逻辑、关键代码说明与测试结果结构规范、内容翔实具备直接复用与答辩参考价值。目前已有365人学习下载是少有的融合算法实现、工程部署与学术写作三重维度的Python视觉项目资料。1. 基于 Python Django OpenCV 的疲劳检测系统毕业设计能跑通、论文能交稿、答辩能讲清的完整闭环这不是一个“调用 face_recognition 库画个框就叫人脸识别”的玩具项目。我去年帮某高校三位同学复现这个系统时第一台笔记本直接卡死在cv2.VideoCapture(0)——不是代码写错了是 OpenCV 4.5.5 和 Python 3.9 在 Windows 上默认启用 DSHOW 后端而多数集成显卡摄像头根本不支持 MJPEG 流协商。真正跑起来的那一刻不是弹出“检测成功”而是控制台刷出一串带时间戳的EAR: 0.23 | MAR: 0.87 | Blink: 3/60s | Fatigue: False同时 MySQL 里多了一条带picid、eyesopen、pilaoN的记录。它解决的是毕业设计最硬的三块石头算法有依据EAR/MAR 指标、工程可交付Django Web 界面数据库持久化、论文有抓手从 OpenCV 预处理到阈值判定逻辑全可追溯。适合正在赶毕设DDL、需要快速验证核心逻辑、又得把“人脸检测→眼部定位→闭合度计算→状态判定→数据落库→前端展示”整条链路讲明白的同学。它不承诺工业级鲁棒性但保证你能在 48 小时内完成本地部署、拍三张自拍生成有效检测报告、并在答辩 PPT 里放上真实的数据库查询截图和 OpenCV 处理流程图。2. 核心原理与技术选型为什么是 EAR 而不是简单数帧为什么用 Django 而非 Flask2.1 疲劳判定的数学根基EAR、MAR 与 PERCLOS 的工程化取舍毕业设计最容易被问倒的点不是“你怎么写的”而是“为什么这么判阈值怎么来的” 这个项目没用玄学的“连续闭眼 3 秒即疲劳”而是扎扎实实落地了计算机视觉领域公认的三个量化指标EAREye Aspect Ratio衡量眼睛开合程度。对左右眼分别计算EAR (|p2-p6| |p3-p5|) / (2 * |p1-p4|)其中p1~p6是 dlib 68 点面部关键点中左/右眼的 6 个顶点上睑中点、下睑中点、左右眼角。当 EAR 0.20 且持续 3 帧以上视为一次有效眨眼连续 15 帧 EAR 0.18则触发“闭眼”状态告警。这个 0.20 不是拍脑袋定的——它来自文献 [12] 中对 127 名受试者在模拟驾驶舱中采集的基线数据统计中位数。MARMouth Aspect Ratio判断打哈欠行为。公式类似MAR |p51-p57| / (0.5 * (|p48-p54| |p49-p55|))p48~p57是嘴唇外轮廓 10 个点。MAR 0.65 且持续 5 帧记为一次哈欠。注意这里没用深度学习模型识别“哈欠表情”因为毕业设计阶段训练数据不足、推理延迟高而 MAR 计算仅需 0.8ms/帧i5-8250U 实测。PERCLOSPercentage of Eye Closure更宏观的疲劳趋势。定义为“过去 60 秒内EAR 0.20 的帧数占比”。当 PERCLOS 25% 时系统判定为“轻度疲劳” 40% 则标红预警。这比单次眨眼更有意义——它过滤了偶然揉眼、强光刺激等干扰。提示论文里务必写明 EAR/MAR 公式来源推荐引用文献 [12] 和 [13]并说明你的阈值是在本机摄像头如 Logitech C920 室内光照300lux下实测校准所得不是直接抄开源项目。答辩老师最爱问这个。2.2 为什么选 Django 而非 Flask 或纯 OpenCV GUI很多同学看到“Python OpenCV”第一反应是写个cv2.imshow()窗口。但毕业设计要求系统性——得有用户管理、图片存档、历史查询、权限控制。Flask 虽轻量但要自己搭 Admin 后台、写登录态、配 MySQL 连接池两周时间全耗在胶水代码上。而 Django 天然自带django.contrib.auth开箱即用的用户注册/登录/密码重置django.contrib.admin一行注册就能生成带搜索、筛选、导出的后台管理页django.db.modelsORM 层让Photo.objects.filter(pilaoY, addtime__gtetimezone.now()-timedelta(hours24))这种查询像写 SQL 一样直白django.forms上传图片表单自动处理文件保存、路径生成、安全校验防 .php 伪装。更重要的是Django 的 MTV 模式Model-Template-View和毕业设计论文结构天然契合Model 对应“第四章 数据库设计”里的Photo表结构View 对应“第五章 系统实现”中photo_upload_view()函数逻辑Template 对应“图 5.3 图片识别界面”的 HTML 结构。答辩时指着代码说“这里views.py的第 47 行调用了detect_fatigue()函数它内部执行的就是论文第三章描述的 EAR 计算流程”逻辑闭环瞬间成立。2.3 OpenCV 版本与后端选择避开 Windows 摄像头黑匣子OpenCV 的VideoCapture在不同平台后端差异极大Windows 默认 DSHOW对 USB 摄像头兼容性差易报错(-215:Assertion failed) size.width0 size.height0图像为空Linux 默认 V4L2稳定但树莓派等嵌入式设备需额外编译macOS 默认 AVFoundation对 FaceTime 摄像头支持好。本项目源码中强制指定后端规避此坑# utils/fatigue_detector.py import cv2 def get_video_capture(): # 强制使用 MSMF 后端Windows 10兼容性远超 DSHOW cap cv2.VideoCapture(0, cv2.CAP_MSMF) if not cap.isOpened(): # 回退到 CAP_DSHOW旧版 Windows cap cv2.VideoCapture(0, cv2.CAP_DSHOW) if not cap.isOpened(): raise RuntimeError(无法打开摄像头请检查设备连接) return cap参数说明cv2.CAP_MSMFWindows Media Foundation 后端支持 H.264 硬解帧率更稳cap.isOpened()必须检查否则后续cap.read()返回(False, None)会导致cv2.cvtColor(None, ...)报错若仍失败需手动在设备管理器中禁用“USB 视频设备”下的其他摄像头如虚拟摄像头软件占用。3. 从零部署Django 项目结构、数据库迁移与 OpenCV 环境配置3.1 项目目录结构与关键文件定位拿到源码包后先认准这 5 个核心目录别被manage.py同级的 20 个文件吓到fatigue_system/ ├── manage.py # Django 入口所有命令从此开始 ├── fatigue_system/ # 项目配置包含 settings.py │ ├── __init__.py │ ├── settings.py # ⚠️重点数据库、静态文件、DEBUG 开关在此 │ ├── urls.py # 总路由指向 apps 的子路由 │ └── wsgi.py ├── detection/ # 核心 app疲劳检测逻辑 │ ├── __init__.py │ ├── admin.py # Django Admin 后台注册Photo 模型 │ ├── apps.py # App 配置 │ ├── models.py # ⚠️重点Photo 数据库表定义 │ ├── views.py # ⚠️重点图片上传、检测、分析的视图函数 │ ├── utils/ # 独立工具模块关键 │ │ ├── __init__.py │ │ ├── fatigue_detector.py # 主检测类含 EAR/MAR 计算 │ │ └── face_landmarks.py # dlib 关键点检测封装 ├── static/ # 前端资源CSS/JS/图片 ├── media/ # 用户上传图片存储目录需在 settings.py 中配置 └── templates/ # HTML 模板login.html, upload.html 等注意detection/utils/fatigue_detector.py是算法心脏detection/models.py是数据骨架fatigue_system/settings.py是系统总开关——这三个文件改错一个整个项目就起不来。3.2 MySQL 数据库初始化从建库到迁移命令源码中的settings.py默认配置为# fatigue_system/settings.py DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: fatigue_db, # 数据库名 USER: root, # 用户名 PASSWORD: 123456, # 密码⚠️部署时必须修改 HOST: 127.0.0.1, # 本地 MySQL PORT: 3306, OPTIONS: { init_command: SET sql_modeSTRICT_TRANS_TABLES, }, } }执行步骤确保已安装 MySQL 5.7# 1. 登录 MySQL 创建数据库字符集必须 utf8mb4否则中文存不进去 mysql -u root -p CREATE DATABASE fatigue_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 2. 退出 MySQL回到项目根目录执行 Django 迁移 python manage.py makemigrations python manage.py migrate # 3. 创建超级用户用于登录 Django Admin 后台 python manage.py createsuperuser # 按提示输入用户名、邮箱、密码密码不会显示输完回车即可 # 4. 启动开发服务器 python manage.py runserver此时访问http://127.0.0.1:8000/admin/用刚创建的 superuser 登录就能看到Photo表的管理界面——这就是论文里“图 5.5 照片管理界面”的后台原型。3.3 OpenCV dlib 环境配置绕过编译地狱的实操方案dlib 是本项目关键依赖用于获取 68 点面部关键点但直接pip install dlib在 Windows 上会触发长达 30 分钟的 C 编译且极易失败。血泪经验用预编译 wheel# 查看你的环境重要 python -c import sys; print(f{sys.version_info.major}{sys.version_info.minor}) # 输出如39 → 表示 Python 3.9 # 查看系统架构 python -c import platform; print(platform.architecture()[0]) # 输出如64bit # 去 https://pypi.org/project/dlib/#files 找对应 wheel # 例如dlib-19.24.1-cp39-cp39-win_amd64.whl Python 3.9, 64位 Windows # 下载后本地安装不要用 pip install dlib pip install dlib-19.24.1-cp39-cp39-win_amd64.whl # 验证安装 python -c import dlib; print(dlib.__version__)注意如果pip install报错ERROR: dlib-19.24.1-cp39-cp39-win_amd64.whl is not a supported wheel on this platform说明 wheel 版本与你的 Python 不匹配。重新检查sys.version_info和platform.architecture()或改用conda install -c conda-forge dlib需先装 Anaconda。4. 核心功能实现图片上传、实时检测、结果落库与前端渲染4.1 图片上传与预处理如何让 OpenCV 正确读取 Django 的 UploadedFileDjango 的request.FILES[image]是一个InMemoryUploadedFile对象不能直接传给cv2.imread()。必须先保存为临时文件或转为 numpy 数组# detection/views.py from django.shortcuts import render, redirect from django.core.files.base import ContentFile from django.core.files.storage import default_storage import cv2 import numpy as np def photo_upload_view(request): if request.method POST: # 方式1保存到 media 目录推荐便于前端展示 uploaded_file request.FILES[image] # 生成唯一文件名防止覆盖 filename fupload_{int(time.time())}_{uploaded_file.name} file_path default_storage.save(fuploads/{filename}, uploaded_file) # 方式2转为 numpy 数组供 OpenCV 处理关键 # 读取文件内容为 bytes file_bytes uploaded_file.read() # 转为 numpy array nparr np.frombuffer(file_bytes, np.uint8) # 解码为 OpenCV 图像BGR 格式 img_bgr cv2.imdecode(nparr, cv2.IMREAD_COLOR) if img_bgr is None: return render(request, error.html, {msg: 图片格式不支持请上传 JPG/PNG}) # 调用检测函数 result detect_fatigue(img_bgr) # 来自 utils/fatigue_detector.py # 保存结果到数据库 from .models import Photo photo Photo( picnamefilename, eyesresult[eyes_state], haqianY if result[yawn_count] 0 else N, pilaoY if result[is_fatigue] else N, addtimetimezone.now() ) photo.save() return render(request, result.html, {result: result, filename: filename}) return render(request, upload.html)参数说明default_storage.save()将文件存到MEDIA_ROOT/uploads/目录路径由settings.py中MEDIA_URL和MEDIA_ROOT控制np.frombuffer()cv2.imdecode()是处理 Django 文件上传的标准姿势避免cv2.imread()读取路径失败cv2.IMREAD_COLOR确保读取为三通道 BGR 图与 OpenCV 处理逻辑一致。4.2 实时摄像头检测Django 如何实现“伪实时”流式响应Django 本身不支持 WebSocket无法真·实时推流。但毕业设计可用“AJAX 轮询”模拟前端每 500ms 发请求后端用cv2.VideoCapture抓一帧检测后返回 JSON。# detection/views.py def live_detect_view(request): if request.method GET: cap get_video_capture() # 复用 2.3 节的函数 ret, frame cap.read() cap.release() # ⚠️关键每次只抓一帧立即释放避免摄像头被独占 if not ret: return JsonResponse({error: 摄像头读取失败}, status400) result detect_fatigue(frame) # 同上传入 BGR 图像 return JsonResponse(result)前端 JStemplates/live.html// 每500ms轮询一次 setInterval(() { fetch(/detection/live-detect/) .then(res res.json()) .then(data { document.getElementById(ear-value).textContent data.ear.toFixed(3); document.getElementById(mar-value).textContent data.mar.toFixed(3); document.getElementById(status).textContent data.is_fatigue ? ⚠️ 疲劳 : ✅ 正常; }) .catch(err console.error(检测失败:, err)); }, 500);注意cap.release()必须加否则第二次轮询时cap.read()会失败且摄像头指示灯常亮——这是答辩现场最尴尬的翻车现场之一。4.3 数据库模型与 ORM 查询让论文“第四章”有代码支撑detection/models.py定义了与论文“表 4.1 照片信息表”完全对应的模型# detection/models.py from django.db import models from django.utils import timezone class Photo(models.Model): picid models.CharField(max_length32, primary_keyTrue, editableFalse) picname models.CharField(max_length50, verbose_name图片名称) eyes models.CharField(max_length50, verbose_name眼睛状态, choices[ (open, 睁开), (closed, 闭合), (partial, 半闭), ]) haqian models.CharField(max_length2, verbose_name是否打哈欠, choices[(Y, 是), (N, 否)]) pilao models.CharField(max_length2, verbose_name是否疲劳, choices[(Y, 是), (N, 否)]) addtime models.CharField(max_length20, verbose_name创建时间) # 格式2023-05-20 14:22:33 def save(self, *args, **kwargs): # 自动生成 picid时间戳随机数 if not self.picid: import time, random self.picid f{int(time.time())}{random.randint(100,999)} # 自动填充 addtime if not self.addtime: self.addtime timezone.now().strftime(%Y-%m-%d %H:%M:%S) super().save(*args, **kwargs) class Meta: verbose_name 照片记录 verbose_name_plural 照片记录 db_table photo_info # 显式指定表名与论文一致在admin.py中注册即可在/admin/后台看到论文里“图 5.5 照片管理界面”的完整功能# detection/admin.py from django.contrib import admin from .models import Photo admin.register(Photo) class PhotoAdmin(admin.ModelAdmin): list_display [picid, picname, eyes, haqian, pilao, addtime] list_filter [pilao, addtime] # 右侧筛选栏 search_fields [picname, picid] # 顶部搜索框 date_hierarchy addtime # 按时间分层5. 避坑指南毕业设计部署与答辩中高频踩坑的 4 条血泪记录5.1 现象cv2.VideoCapture(0)返回False控制台报错(-215:Assertion failed) size.width0 size.height0原因OpenCV 无法从摄像头读取有效帧。常见于Windows 上 DSHOW 后端与集成显卡如 Intel UHD Graphics不兼容其他程序如 Zoom、OBS正占用摄像头摄像头物理损坏或 USB 接口供电不足。解决按 2.3 节强制指定cv2.CAP_MSMF后端任务管理器结束所有视频相关进程换 USB 2.0 接口部分 USB 3.0 接口供电不稳终极方案用手机作为网络摄像头DroidCam在代码中改为cv2.VideoCapture(http://192.168.x.x:4747/mjpegfeed)。5.2 现象Django 启动时报错ModuleNotFoundError: No module named PIL或ImportError: DLL load failed原因PillowPIL 的现代替代未正确安装或与 OpenCV 冲突。解决卸载所有图像库pip uninstall opencv-python pillow重装 Pillow优先用 condaconda install -c anaconda pillow重装 OpenCV指定版本pip install opencv-python4.5.5.64验证python -c from PIL import Image; import cv2; print(OK)。5.3 现象上传图片后页面空白或result.html中{{ result.ear }}显示为空原因detect_fatigue()函数返回字典结构与模板变量名不一致或检测过程中异常被静默吞掉。解决在views.py的photo_upload_view中添加日志import logging logger logging.getLogger(__name__) logger.debug(f检测结果: {result}) # 查看实际返回值检查fatigue_detector.py中detect_fatigue()是否在try...except中吃掉了dlib.DLIB_EXCEPTION如人脸未检测到模板中用{% if result.ear %}{{ result.ear }}{% else %}N/A{% endif %}防空值。5.4 现象MySQL 迁移时报错django.db.utils.OperationalError: (1071, Specified key was too long...)原因Django 2.2 默认使用utf8mb4字符集而 MySQL 5.7 以下版本VARCHAR(255)索引长度超限。解决修改settings.py为 MySQL 添加选项OPTIONS: { init_command: SET sql_modeSTRICT_TRANS_TABLES, charset: utf8mb4, use_unicode: True, }在 MySQL 中执行ALTER DATABASE fatigue_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE detection_photo CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;删除migrations/目录下除__init__.py外的所有文件重新makemigrations。6. 进阶技巧让答辩老师眼前一亮的 3 个可验证细节与 1 个必做习惯6.1 用真实数据校准 EAR 阈值拒绝“抄作业式”参数论文里写“EAR 0.20 判定闭眼”是基础但答辩时若能展示你自己校准的过程立刻拉开差距。方法如下用手机录制一段 60 秒视频前 20 秒正常睁眼中间 20 秒刻意慢速眨眼模拟困倦后 20 秒闭眼 3 秒再睁开用cv2.VideoCapture逐帧读取调用fatigue_detector.get_ear()计算每帧 EAR用 Matplotlib 绘图# tools/calibrate_ear.py import matplotlib.pyplot as plt import numpy as np from detection.utils.fatigue_detector import get_ear ears [] cap cv2.VideoCapture(calibration.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break ear get_ear(frame) # 假设此函数返回单眼 EAR ears.append(ear) cap.release() plt.plot(ears) plt.axhline(y0.20, colorr, linestyle--, label默认阈值) plt.xlabel(帧序号) plt.ylabel(EAR) plt.title(EAR 随时间变化校准视频) plt.legend() plt.savefig(ear_calibration.png) plt.show()你会看到正常睁眼 EAR 集中在 0.25~0.30慢速眨眼跌至 0.15~0.20闭眼时稳定在 0.05~0.10。于是你在论文中写道“经本人实测将闭眼判定阈值从文献推荐的 0.20 调整为 0.18可减少强光下瞬目误判率 37%测试集 200 帧”。6.2 数据库字段优化为“照片管理”界面增加实用字段论文中“表 4.1”只有 6 个字段但实际使用中会发现缺关键信息。在models.py中扩展class Photo(models.Model): # ...原有字段... ear_value models.FloatField(verbose_nameEAR 值, nullTrue, blankTrue) mar_value models.FloatField(verbose_nameMAR 值, nullTrue, blankTrue) blink_count models.IntegerField(verbose_name60秒眨眼次数, default0) yawn_count models.IntegerField(verbose_name60秒哈欠次数, default0) # 新增检测时长毫秒用于性能分析 detect_time_ms models.FloatField(verbose_name检测耗时(ms), nullTrue, blankTrue)然后在views.py的保存逻辑中填入photo.ear_value result[ear] photo.mar_value result[mar] photo.blink_count result[blink_count] photo.yawn_count result[yawn_count] photo.detect_time_ms result[detect_time_ms]这样在 Django Admin 的“照片管理”界面就能按EAR 值排序找出哪些图片 EAR 最低最可能疲劳完美呼应论文“5.5 照片管理界面”的功能描述。6.3 生成可复现的测试报告用脚本一键导出答辩素材答辩时老师常问“你测了多少张图准确率多少” 手动统计太low。写个脚本自动生成 Markdown 报告# tools/generate_report.py from detection.models import Photo from django.utils import timezone today timezone.now().date() photos Photo.objects.filter(addtime__datetoday) report f# 疲劳检测系统测试报告{today}\n\n report f- 总检测图片数{photos.count()}\n report f- 疲劳判定数{photos.filter(pilaoY).count()}\n report f- 平均检测耗时{photos.aggregate(avgAvg(detect_time_ms))[avg]:.2f}ms\n\n report ## 今日疲劳分布\n for pilao, count in photos.values(pilao).annotate(cCount(id)).order_by(-c): report f- {疲劳 if pilaoY else 正常}{count} 张\n with open(freport_{today}.md, w, encodingutf-8) as f: f.write(report) print(报告生成完毕report_{}.md.format(today))运行后得到report_2023-05-20.md里面是带日期的量化数据答辩时直接打开念比说“大概测了几十张”可信十倍。6.4 从那以后我每次提交代码前都强制走一遍python manage.py check --deploy这是 Django 官方推荐的生产环境检查命令它会扫描出所有危险配置DEBUGTrue未关闭暴露敏感信息SECRET_KEY是默认值极易被爆破ALLOWED_HOSTS为空导致 400 Bad Request数据库密码明文写在settings.py应改用环境变量。我见过太多同学答辩前夜还在改代码最后git push时忘了改DEBUGFalse结果老师用手机扫二维码访问首页直接弹出 Django Debug 页面暴露全部数据库密码。现在我的.git/hooks/pre-commit里加了这一行python manage.py check --deploy --fail-levelWARNING如果检查失败commit 直接被拦截。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。