OpenCV+Django疲劳检测实战:EAR阈值与摄像头实时监测

发布时间:2026/10/10 11:13:52
OpenCV+Django疲劳检测实战:EAR阈值与摄像头实时监测
简介针对疲劳驾驶及长时间作业引发的安全隐患基于PythonDjangoOpenCV的疲劳检测系统毕业设计资料可作为完整参照适合计算机相关专业学生开展人脸识别、图像处理类课题。资源共1个文件为docx格式论文文档压缩包仅1.01MB内容包含中英文摘要、目录、绪论、国内外研究现状、系统设计等章节结构清晰完整。论文围绕基于眼动信号与人脸判断的疲劳检测方法展开重点讲解借助OpenCV图像处理库检测眼睛闭合程度利用面部表情呈现与眨眼频次表征疲劳状态并结合Python编程语言、Mysql数据库实现图像识别、图片分析及照片管理等系统功能模块。还介绍了疲劳检测在交通安全、医疗保健等领域的应用前景以及我国连续驾驶4小时强制休息等背景知识。目前已有365人学习浏览适合需要系统梳理疲劳检测课题技术路线、快速搭建论文框架的读者借鉴。1. 拿到一套疲劳检测项目先看懂它的三块结构第一次在本地打开“基于pythonDjangoopencv的疲劳检测系统源码数据库论文.docx”这类项目包时我的第一反应是去代码里找神经网络模型找了两小时才明白这套东西的真正地基不是算法而是“源码、数据库、论文”三个结构各自承担的任务。OpenCV 负责看得见——从摄像头帧里判断眼睛开合Django 负责把判断结果变成页面和持久化记录数据库和论文文档负责解释结果凭什么可信。它能解决的实际问题是驾驶疲劳监测、实验室专注度统计、线上考试异常行为分析这一类需要“实时感知 事后回查”的场景。适合读它的人是正在做 Django 项目实战的新手以及想把 OpenCV 检测接进 Web 工程的视觉工程师。我的建议是先拆结构再逐层跑通。2. OpenCV 疲劳检测核心链路从 EAR 指标到摄像头逐帧判断2.1 眨眼检测不是玄学EAR 指标的数学定义与阈值疲劳检测最粗糙的做法是看“眼睛是否被遮挡”比如用 Haar 检测不到眼睛就判定闭眼。这种办法白天还行稍微偏一点头就翻车实际项目中我一般用把眼睛开合程度建模成连续指标的做法最常听到、也最好复现的是 EAREye Aspect Ratio眼睛纵横比。EAR 把单只眼睛的 6 个关键点拉成一组欧氏距离。假设 p1 到 p6 按 68 点人脸标注顺序排列那么垂直方向取两组距离的平均再除以水平方向距离。写成 Python 就是这个样子import numpy as np def eye_aspect_ratio(eye): # eye 是按固定顺序排列的 6 个坐标点 # 垂直方向两个距离取平均 v1 np.linalg.norm(eye[1] - eye[5]) v2 np.linalg.norm(eye[2] - eye[4]) # 水平方向一个距离 h np.linalg.norm(eye[0] - eye[3]) ear (v1 v2) / (2.0 * h) return ear这段代码的逻辑很简单睁眼时垂直距离和水平距离的比值较高闭眼时三个距离同时变短分子下降得比分母更明显EAR 就掉下来。实际标定中睁眼 EAR 一般在 0.250.35闭眼能到 0.1 左右中间值 0.2 是比较常见的初始阈值。注意 EAR 对不同人脸有差异单帧低于阈值不能直接判闭眼必须连续几帧都低才触发一次眨眼。纯 OpenCV 路线下如果没有 dlib 或人脸关键点模型可以用 Haar 眼睛检测框的高度除以宽度做近似。效果不如 EAR但胜在少一个模型依赖eye_cascade cv2.CascadeClassifier(haarcascade_eye.xml) def eye_openness(gray, face_region): eyes eye_cascade.detectMultiScale(face_region, 1.1, 5) if not eyes: return 0.0 # 检测不到就当闭眼处理 x, y, w, h sorted(eyes, keylambda r: r[2] * r[3])[-1] return h / w这里 detectMultiScale 的 scaleFactor1.1 和 minNeighbors5 是常见值scaleFactor 越接近 1 越慢但越准minNeighbors 太小会出一堆假框。它只是一个兜底方案后面所有参数讨论我还是按 EAR 来展开。2.2 用 OpenCV 的 dnn 模块加载人脸检测器模型选型与最小检测代码有了 EAR 指标还要先找到脸和眼睛区域。单独用 Haar 做人脸检测在侧脸、低头和暗光下抖动非常明显摄像头稍微偏置人脸框就来回跳EAR 也跟着抖。稳定一点的做法是走 OpenCV 的 dnn 模块加载预训练 SSD 人脸检测模型。这个模型在很多 OpenCV 教程里都能找到配套的是 deploy.prototxt 和 res10_300x300_ssd_iter_140000.caffemodel 两个文件。import cv2 import numpy as np proto_file models/deploy.prototxt model_file models/res10_300x300_ssd_iter_140000.caffemodel net cv2.dnn.readNetFromCaffe(proto_file, model_file) def detect_faces(frame, conf_threshold0.5): h, w frame.shape[:2] blob cv2.dnn.blobFromImage( frame, 1.0, (300, 300), (104.0, 177.0, 123.0) ) net.setInput(blob) detections net.forward() faces [] for i in range(detections.shape[2]): confidence detections[0, 0, i, 2] if confidence conf_threshold: box detections[0, 0, i, 3:7] * np.array([w, h, w, h]) x1, y1, x2, y2 box.astype(int) faces.append((x1, y1, x2, y2)) return facesblobFromImage 的参数值得解释scale1.0 表示不做像素缩放300×300 是模型输入尺寸太小丢细节太大浪费推理时间mean 减 (104, 177, 123) 是这组预训练模型自带的 BGR 均值不能随便改成 (0,0,0)。conf_threshold 用 0.5 时小脸和侧脸偶尔会漏我一般会在测试集上试 0.40.6 再定。检测结果框是 01 的归一化坐标必须乘回原始宽高才能画框和裁切眼睛区域。2.3 参数怎么设EAR 阈值、连续帧数与摄像头帧率的换算关系EAR 阈值、连续低帧数、判定窗口、PERCLOS 占比这四个参数决定系统到底是“灵敏”还是“稳健”。它们不是独立变量必须和摄像头帧率换算。假设帧率是 30fpsEAR 连续 4 帧低于阈值意味着眼睛闭合了约 4/300.13 秒。人正常眨眼一次闭合约 0.10.4 秒0.13 秒能抓到眨眼又不会把快速闪烁误判成疲劳。low_frame_num 4 ear_low_frames 0 total_frames 0 closed_frames 0 window_seconds 60 while True: ear compute_ear(current_frame) if ear 0.2: ear_low_frames 1 else: ear_low_frames 0 if ear_low_frames low_frame_num: closed_frames 1 ear_low_frames 0 total_frames 1 if total_frames 30 * window_seconds: perclos closed_frames / total_frames if perclos 0.4: mark_fatigue() total_frames 0 closed_frames 0参数按表格里这套来调就够起步参数典型取值调整方向与后果EAR 阈值0.180.25提高则灵敏但容易误报降低则抗干扰但漏报连续低帧数2530fps 用 415fps 用 2过低会闪断判定窗口3060 秒越长越平滑但报警延迟越高PERCLOS 阈值0.4论文里常见的疲劳判断线越高越保守这里 PERCLOS 的含义是“在窗口期内眼睛闭合时间占总时长的比例”。驾驶疲劳研究里 40% 是一条被广泛沿用的警戒线你也可以按自己的数据改。帧率变化时务必重新换算连续帧数这是最容易出玄学问题的地方同样 0.2 秒闭合时间15fps 下只有 3 帧你设置 5 帧就永远检测不到。3. Django 如何接住 OpenCV 的检测结果项目结构、ORM 与视频推流3.1 项目初始化django-admin startproject 与多 app 划分Django 创建 app 是这套系统里最简单也最容易走错的一步。所有逻辑堆在一个 app 里到后面论文要加统计页、管理员要改阈值、摄像头要独立进程代码就会拧成一团。我一般的划分是 detection 放检测算法和数据模型dashboard 放页面和统计接口分别启动一个 app。django-admin startproject fatigue_site cd fatigue_site python manage.py startapp detection python manage.py startapp dashboard然后在 fatigue_site/settings.py 的 INSTALLED_APPS 里把两个 app 注册进去INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, detection, dashboard, ]对 Django 项目实战新手来说另一个高频坑是在 PyCharm 里导入已经建好的 Django 项目后迁移命令在终端能跑IDE 里却一直提示找不到模块。原因通常是 PyCharm 的 Python 解释器没指向虚拟环境里的 python手工改一下 Project Interpreter 就好不是代码问题。我在每次开新工程时都会先做一遍省得后面背一堆环境错误。3.2 用 Django 执行查询与删除对象ORM 如何管理疲劳记录检测线程把每一帧的 EAR 和是否疲劳写进数据库前先定义一个足够简单的模型。疲劳记录表不需要存图片存下时间点、用户、EAR 值和状态就够了# detection/models.py from django.db import models class User(models.Model): name models.CharField(max_length50) class FatigueRecord(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_namerecords) ear models.FloatField() is_fatigue models.BooleanField(defaultFalse) created_at models.DateTimeField(auto_now_addTrue)运营一段时间后数据库会积累大量记录。用 Django 执行查询和删除对象都有固定套路。查最近一小时的疲劳数据再清理 7 天前的历史记录from django.utils import timezone from datetime import timedelta from detection.models import FatigueRecord # 执行查询最近一小时疲劳记录 recent FatigueRecord.objects.filter( is_fatigueTrue, created_at__gtetimezone.now() - timedelta(hours1), ).order_by(-created_at) # 执行删除清理 7 天前记录 cutoff timezone.now() - timedelta(days7) deleted_count, detail FatigueRecord.objects.filter(created_at__ltcutoff).delete()delete() 的返回值第一个是总共删除的行数第二个是每个模型删了多少行的明细。如果发现明细里只有 FatigueRecord 而没有关联表说明外键关系没建好级联删除没有生效这一点我在第 5 章还会展开。外键查询还有一个 N1 问题。像网上的 ORM 例子里写 select_related(personinfo__vocation) 那样这里跨表取 user 时也需要用 select_related 一次 JOIN 回来records FatigueRecord.objects.select_related(user).filter(is_fatigueTrue) for r in records: print(r.user.name, r.ear, r.created_at)加上 select_related 之后循环里访问 r.user 不会触发逐条查询。这个优化在检测入库频率高的时候特别明显是疲劳记录落库和查询里最值得先做的改进。3.3 把摄像头画面变成 HTTP 视频流StreamingHttpResponse 的封装Django 视图默认返回完整响应体不适合持续输出视频。要把 OpenCV 的帧送到浏览器常见做法是 MJPEG 流也就是用 StreamingHttpResponse 不断吐出 multipart 的 JPEG 帧。先封装一个 VideoCameraimport cv2 from django.http import StreamingHttpResponse class VideoCamera: def __init__(self): self.cap cv2.VideoCapture(0) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) def get_frame(self): ok, frame self.cap.read() if not ok: return b # 在 frame 上画人脸框和 EAR 后再编码 ret, jpeg cv2.imencode(.jpg, frame, [cv2.IMWRITE_JPEG_QUALITY, 70]) return jpeg.tobytes() def gen(camera): while True: frame camera.get_frame() if frame: yield b--frame\r\nContent-Type: image/jpeg\r\n\r\n frame b\r\n def video_feed(request): return StreamingHttpResponse( gen(VideoCamera()), content_typemultipart/x-mixed-replace; boundaryframe, )在 urls.py 里加一条path(video_feed/, views.video_feed)前端用img src/video_feed/就能看到实时画面。参数上两个点值得注意一是 JPEG 质量压到 70 以后流量下降非常明显肉眼几乎看不出检测框有损失二是每次请求都会创建新的 VideoCamera如果同时两个浏览器页面打开第二个会抢不到摄像头。生产环境应把 VideoCamera 做成全局单例我在第 5 章的断流问题里给出解决办法。注意开发环境用 runserver 调试视频流没有问题但正式交付时不要用 runserver 扛视频流。用 uWSGI/nginx 时记得把响应缓冲和 timeout 调大否则 30 秒后画面会被中间层切断。4. 数据库设计与论文闭环三张表、聚合统计与模型文件落位4.1 疲劳记录、用户与参数三张表的字段设计与迁移论文里的实验需要说清楚数据从哪来、参数是多少、检测对象是谁。数据库只放一张记录表会让这类问题没法回答所以我至少会拆成三张表用户表、疲劳记录表、参数表。这也是“源码数据库论文”里数据库部分最常见的落地方式。表名关键字段用途userid, name, role, created_at被检测人身份fatigue_recordid, user_id, ear, is_fatigue, created_at逐次状态采样fatigue_paramid, ear_threshold, low_frame_num, perclos_threshold, updated_at每次实验参数留痕对应模型# detection/models.py from django.db import models class User(models.Model): name models.CharField(max_length50) role models.CharField(max_length20, defaultdriver) class FatigueRecord(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_namerecords) ear models.FloatField() is_fatigue models.BooleanField(defaultFalse) created_at models.DateTimeField(auto_now_addTrue) class FatigueParam(models.Model): ear_threshold models.FloatField(default0.2) low_frame_num models.IntegerField(default4) perclos_threshold models.FloatField(default0.4) updated_at models.DateTimeField(auto_nowTrue)建好后执行数据库迁移python manage.py makemigrations detection python manage.py migrate参数为什么单独一张表因为论文评审最常问的“阈值为什么是 0.2、为什么连续 4 帧”必须有一个可查对象。每跑一轮实验把参数写一条记录后面复现结果时直接读这张表就不用手写“实验记录.txt”。4.2 论文图表数据怎么来ORM 聚合查询与分组统计论文里需要 PERCLOS 变化曲线、不同时间段疲劳次数统计。这些图不要导出 Excel 再手工画直接在 Django 里用 ORM 聚合喂给前端图表库或者 Pyecharts 都能用。按小时聚合最近 7 天的记录from django.db.models import Count, Avg, Q from django.db.models.functions import TruncHour from detection.models import FatigueRecord hourly ( FatigueRecord.objects .filter(created_at__gtetimezone.now() - timedelta(days7)) .annotate(hourTruncHour(created_at)) .values(hour) .annotate( totalCount(id), fatigue_countCount(id, filterQ(is_fatigueTrue)), avg_earAvg(ear), ) .order_by(hour) )TruncHour 会把时间截断到小时values(hour) 相当于 GROUP BY。fatigue_count 用了带 filter 的 Count统计的是 is_fatigueTrue 的条数avg_ear 是整个小时窗口平均的 EAR。这组数据再套个 json.dumps 就能输出成[{ hour: 2025-01-01 08:00, fatigue_count: 3 }]。论文里我一般还会把同样逻辑封装成一个统计接口这样前端页面和后端脚本共用一份代码避免两处口径对不上。关键窗口、阈值、过滤条件都从函数参数传写进论文的方法部分时可以直接引用这个接口评审要看数据时也能现场重新跑。4.3 模型文件与数据集管理OpenCV 特征文件放哪里、怎么被 Django 找到源码目录里最容易乱的就是模型文件。我见过有人把 caffemodel 放在 app 的 views.py 同级目录过几天换电脑运行路径一改就 FileNotFoundError。项目根目录建一个 models/把 deploy.prototxt、caffemodel、haarcascade 都放进去再在 settings.py 里统一指向它# settings.py import os BASE_DIR os.path.dirname(os.path.dirname(os.path.abspath(__file__))) MODEL_DIR os.path.join(BASE_DIR, models)使用模型的地方不要写死路径import os import cv2 from django.conf import settings face_detector cv2.dnn.readNetFromCaffe( os.path.join(settings.MODEL_DIR, deploy.prototxt), os.path.join(settings.MODEL_DIR, res10_300x300_ssd_iter_140000.caffemodel), )数据集也建议按模型文件的方式统一管理。常见做法是建一个 dataset/ 目录下面分 normal 和 fatigue 两个子集再放一段录好的测试视频。Python 脚本读取 dataset 里的视频做离线回放和实时检测共用同一个 detect_faces 函数这样算法验证和论文实验可以互相印证。第一次跑通时记得检查 MODEL_DIR 下的文件存在在 Django shell 里打印 os.path.exists(...)通过后再进入视频流调试。5. 疲劳检测系统避坑指南OpenCV 安装、摄像头占用与模型路径的 5 个常见问题5.1 现象一pip install opencv-python 之后 import cv2 仍然报错现象按网上教程在终端里执行pip install opencv-python显示安装成功进入 Python 环境import cv2却抛出 ModuleNotFoundError或者直接提示 libGL.so.1: cannot open shared object file。原因最常见的是当前 shell 的 pip 和 python 不属于同一个环境。很多人直接在系统 Python 里装又开了虚拟环境跑项目两边路径不互通另一个高频原因是 Ubuntu 服务器缺 OpenCV 的底层运行依赖 libgl1 和 libglib2.0包虽然装了图形库却没到位。解决把解释器和 pip 统一再补依赖。我在 Ubuntu 上一般这样处理python3 -m venv venv source venv/bin/activate pip install --upgrade pip pip install opencv-python opencv-contrib-python sudo apt-get install -y libgl1 libglib2.0-0 python -c import cv2; print(cv2.__version__)opencv-python 是核心包opencv-contrib-python 额外带 face、tracking 等扩展模块只做人脸检测和 dnn 推理只装前者也可以。python 安装 numpy 库的方法不用单独折腾cv2 会自动拉 numpy 依赖手动装错版本反而可能让 cv2 和 numpy 产生 ABI 冲突。如果你是在用 IDE导入不了 cv2 时先检查解释器是不是 venv 里的 python命令行里的 pip 和 IDE 解释器经常不是一个。5.2 现象二摄像头打开失败代码返回 None 或黑屏现象cv2.VideoCapture(0)执行后cap.isOpened()为 False或者能打开但read()一直返回黑帧。原因摄像头索引不对。笔记本自带摄像头不一定是 0外接摄像头可能是 1、2Linux 下还要确认设备节点存在且当前用户有读权限。另一个常见问题是程序退出前没有cap.release()或者浏览器标签页正占着摄像头。解决写一个专门的打开函数而不是假设索引固定是 0import cv2 def open_camera(index0): cap cv2.VideoCapture(index, cv2.CAP_V4L2) if not cap.isOpened(): cap cv2.VideoCapture(index, cv2.CAP_DSHOW) if not cap.isOpened(): raise RuntimeError(fcamera {index} cant open) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) return cap第二个参数是后端标志Ubuntu 用 V4L2Windows 用 DSHOW选错会导致读取不到画面。分辨率固定 640x480 是为了让后续 dnn 检测保持稳定尺度4K 输入不会提高 EAR 精度反而让 CPU 占用飙升。如果还黑屏在 Ubuntu 上执行ls -l /dev/video*看权限把自己加入 video 组或临时用 sudo 验证是不是设备问题。摄像头这种设备我调多了多数“玄学”其实是索引和占用。5.3 现象三FileNotFoundError模型文件在 Django 里找不到路径现象本地python manage.py runserver一切正常换 gunicorn/uwsgi 部署或者在 PyCharm 里换了工作目录readNetFromCaffe 突然报模型文件不存在。原因代码里写了“models/xxx.caffemodel”这样的相对路径。相对路径是按当前进程工作目录解析的Django 在不同启动方式下工作目录会变路径自然失效。解决所有模型路径以 settings.BASE_DIR 为基准。settings.py 里加一个 MODEL_DIR读取时统一拼接from django.conf import settings import os import cv2 face_detector cv2.dnn.readNetFromCaffe( os.path.join(settings.MODEL_DIR, deploy.prototxt), os.path.join(settings.MODEL_DIR, res10_300x300_ssd_iter_140000.caffemodel), )如果项目包里没有附带预训练权重需要自己下载后放进 BASE_DIR/models。放好后先在 Django shell 里打印 os.path.exists确认再走业务代码。这个问题简单到不值得怀疑但它就是疲劳检测系统里被问得最多的一类原因在于“本地能跑”和“部署能跑”用的是两个工作目录。5.4 现象四删除疲劳记录没有级联关联删除现象删除 User 后FatigueRecord 里的记录还留在数据库导致统计结果对不上或者删除记录后参数表、统计表里出现了悬空的 user 引用。原因Django 的级联行为由外键 on_delete 决定。模型里没有写on_deletemodels.CASCADE时框架不会自动清子表如果建表是从旧项目直接拷的 SQLORM 模型和实际表结构不一致delete() 也会出现“删了主表但子表还在”的情况。解决模型外键显式声明级联删除统一走 ORMclass FatigueRecord(models.Model): user models.ForeignKey( User, on_deletemodels.CASCADE, related_namerecords, ) # 删除用户时级联删除记录 User.objects.filter(nametest_user).delete() # 只清空某用户的记录但保留用户 user.records.all().delete()第一次执行删除后打印一下返回值deleted, detail FatigueRecord.objects.all().delete() print(deleted, detail)detail 里会列出每个模型的删除条数。看到 FatigueRecord 数量变化了才能确定级联真的生效。跨表查询时再配 select_related(user)避免每条记录都单独查一次用户表记录量上来之后这个差异会非常明显。还要注意如果有代码通过 raw SQL 直接删主表Django 的 on_delete 是管不到的。5.5 现象五浏览器视频流卡死几秒后断流现象video_feed 在本地能打开过几秒画面停住刷新后连接重置或者同事的浏览器同时打开页面第二个直接黑屏。原因Django 开发服务器是单进程多线程VideoCamera 里只有一个 VideoCapture 实例。第一个请求断开后生成器没有退出摄像头还被僵尸请求占着第二个请求进来时和残留线程抢同一把摄像头流就断了。另外无限循环的生成器如果不控制资源CPU 会一直满载。解决给摄像头访问加互斥锁生成器退出时释放import threading import cv2 from django.http import StreamingHttpResponse camera_lock threading.Lock() camera cv2.VideoCapture(0) def gen(): while True: with camera_lock: ok, frame camera.read() if not ok: continue ret, jpeg cv2.imencode(.jpg, frame, [cv2.IMWRITE_JPEG_QUALITY, 70]) yield b--frame\r\nContent-Type: image/jpeg\r\n\r\n jpeg.tobytes() b\r\n def video_feed(request): return StreamingHttpResponse( gen(), content_typemultipart/x-mixed-replace; boundaryframe, )互斥锁把 read 和 imencode 放在同一个临界区避免两个线程同时取帧JPEG 质量 70 也能降低带宽压力。要支持真正的多人同时查看更彻底的做法是把摄像头采集放到独立线程不断往 Redis 写最新帧每个视频流请求只读 Redis 而不碰摄像头。这个改造不复杂但能让系统从“能演示”变成“能多人同时用”。6. 进阶验证把检测结果跑成论文里的性能曲线疲劳检测系统能不能交付我从来不以“弹窗有没有出现”为标准而是看离线回放时的性能曲线。这里有一个可复用的验证方法先把摄像头录成标准测试视频再让检测代码按帧回放把每一帧的 EAR 值导出成 CSV和人工标注结果对比。import csv import cv2 cap cv2.VideoCapture(dataset/fatigue_test.mp4) csv_file open(ear_log.csv, w, newline) writer csv.writer(csv_file) writer.writerow([frame, ear, is_closed]) frame_no 0 while True: ok, frame cap.read() if not ok: break ear compute_ear_on_frame(frame) is_closed 1 if ear 0.2 else 0 writer.writerow([frame_no, round(ear, 4), is_closed]) frame_no 1 cap.release() csv_file.close()跑完后用 Python 或 Excel 画 EAR 曲线立刻能看到两件事曲线平不平滑检测的“闭眼”段和人工标注的闭眼段是否对齐。我第一次做这套系统时就吃过亏只盯着实时画面把阈值调到 0.18觉得闭眼检测很准后来离线回放才发现测试视频里那个人眼睛本身比较小睁眼 EAR 也只有 0.2 左右0.18 的阈值漏掉了近一半的疲劳事件。改成 0.15 并加上“连续 4 帧低 EAR”的条件之后才在保持不误报的情况下把召回率提上来。这条经验我现在仍然在沿用每换一次摄像头就把同一段测试视频重跑一遍三组阈值画在同一张图里再决定参数。阈值调参从一个“试到不报错”的经验活变成了有标定依据的流程。希望这个验证方法也能帮到你。本文还有配套的精品资源点击获取