ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Python卷积神经网络的驾驶员疲劳检测与预警系统

基于Python卷积神经网络的驾驶员疲劳检测与预警系统 简介面向毕业设计、课程设计与项目开发场景这套基于Python卷积神经网络的人脸识别驾驶员疲劳检测与预警系统利用打哈欠、眨眼、点头三类面部特征综合人脸朝向、瞳孔朝向、眼睛开合度、眨眼频率及瞳孔收缩率等数据实时估算驾驶员注意力集中程度并提出安全预警。压缩包共20个文件以11个Python脚本为核心涵盖数据预处理、模型训练、模型评估与可视化检测等环节同时配有XML人脸检测模型、HDF5训练权重文件、Tkinter界面及可直接运行的exe程序整体大小约78.33MB。项目内另含系统说明、运行说明与README文档便于快速掌握环境配置与调用流程。目前已有723人学习下载源码经过严格测试可放心参考并在此基础上扩展。这套资源尤其适合需要完整实操参考的深度学习初学者和毕业设计学生能够帮助理解从数据准备到疲劳状态识别、再到预警交互的工程化实现思路。1. 疲劳检测项目为什么会把“人脸识别”变成“人脸状态分类”夜间跑国道网约车司机连续驾驶3小时后眨眼间隔明显拉长偶尔出现一次超过一秒的闭眼。如果系统能提前两秒预警情况可能完全不同。标题里的“基于Python卷积神经网络人脸识别驾驶员疲劳检测与预警系统”表面看像普通的人脸识别产品实际上要解决的是“人脸状态分类”先用检测框把人脸找出来再裁剪出眼睛和嘴巴区域让卷积神经网络判断此刻是清醒、犯困还是打哈欠最后按PERCLOS或闭眼时长触发声光预警。它适合作为毕业设计、课程设计或车载疲劳预警的原型实现交付物是源码、训练好的模型和一份能解释清楚的文档。这套系统真正的难点不在“识别”而在数据类别平衡、实时帧率控制和预警阈值怎么定这三处恰好也是答辩时最容易暴露问题的地方。2. 选型先想清楚CNN主干、疲劳判定规则和预警链路2.1 为什么这个项目不用普通的人脸识别而是用卷积神经网络做状态分类普通的人脸识别回答的是“这张脸是谁”对同一个人的脸在不同光照、角度下要输出稳定的特征向量。疲劳检测回答的是“眼睛闭没闭、嘴张没张”更关注局部纹理。如果直接把FaceNet或ArcFace套过来输入整张脸模型会学偏到人脸ID特征上眨眼状态这种细节根本不是FaceNet的优化目标。卷积神经网络在这里的定位是图像分类器输入裁剪出的眼部小图输出各类别的概率分布。这也是为什么标题里“人脸识别”四个字要打折扣理解——它指的是人脸检测加局部区域状态分类而不是1比N的检索。传统做法用Dlib的68点关键点计算眼睛纵横比EAR优点是轻量、不用训练缺点是侧脸、墨镜、头发遮挡时关键点会漂移。在真实驾驶场景下摄像头装在挡风玻璃角落司机转头看后视镜的瞬间关键点就丢了。CNN分类器对遮挡和光照的鲁棒性更强而且训练数据可控往数据集里加入戴墨镜或低光照样本模型就能学会在这些条件下工作。那为什么不让YOLO直接检测闭眼YOLO的定位能力没问题但“闭眼”这种状态类别需要逐帧画框标注成本高框的质量稍微差一点训练出来的状态分类器就不稳。常见做法是让OpenCV DNN或MTCNN只负责定位人脸把状态识别交给一个小型CNN两边职责分离出了问题也容易排查。2.2 PERCLOS和眨眼频率两种疲劳判定规则怎么选PERCLOS是道路交通安全领域最常引用的指标定义是单位时间内眼睛闭合程度超过80%的时间占比。课程设计做工程简化时可以直接用“闭眼帧数除以窗口总帧数”来近似论文里写清楚是简化版即可属于可接受的学术处理。判定阈值一般取0.4也就是一个60秒的滑动窗口里闭眼总时长超过24秒就触发一级预警。比单纯数眨眼次数更稳因为眨眼次数在轻度疲劳时会下降深度疲劳时反而会出现短时间高频眨眼单看次数非常容易误判。也有系统用每分钟眨眼次数做辅助正常范围15到20次每分钟。我的建议是把眨眼次数作为辅助特征、PERCLOS作为主判定同时加一条硬规则单次闭眼超过2秒直接判定深度疲劳不再等窗口累计。这个规则在工程现场很实用因为持续闭眼本身就是最危险的信号等一个60秒窗口凑满24秒再报警就来不及了。嘴巴维度的检测不能只看开口面积说话也会张大嘴必须要求“打哈欠概率高于阈值且持续时间超过1秒”才记一次哈欠用CNN分类器做这件事比用嘴角距离判断更可靠至少在口罩和遮挡场景下不会直接失效。2.3 系统整体链路从摄像头画面到预警触发需要走几步整个系统的数据流是单向的每一步的输入输出在动手写代码之前就应该定死摄像头读入一帧BGR图像人脸检测器输出人脸框按比例从人脸框里切出眼睛区域和嘴巴区域两个CNN分类器分别输出闭眼概率和打哈欠概率状态机根据连续帧计数产生闭眼事件和哈欠事件滑动窗口计算PERCLOS后映射到预警等级最后在界面上显示并写入日志。写代码时不要把这个链路拆得太散用一个FatigueEngine类把帧处理、状态更新和预警逻辑包在一起外部UI只负责取画面和显示结果。人脸检测器的选型直接决定演示流畅度可以提前做一个对比大概能预估帧耗时检测器CPU单帧耗时侧脸/遮挡表现适用场景OpenCV DNN SSD约15ms中等毕设演示首选MTCNN40ms以上较好离线视频分析RetinaFace80ms以上最好有GPU的开发机对多数课程设计场景OpenCV DNN的SSD模型在帧率和精度之间最平衡而且不依赖GPU答辩现场用一台普通笔记本就能撑住。MTCNN虽然精度高但在CPU上连续跑容易发热掉帧。检测到多个人脸时取面积最大的那个作为驾驶员避免副驾或后排乘客干扰判定结果。3. 从数据集到训练脚本把CNN分类器跑完整3.1 数据集准备闭眼/睁眼/打哈欠三类样本怎么凑、怎么划分公开数据集推荐NTHU Drowsy Driver Detection、CEW闭眼数据集、YawDD打哈欠数据集前两者偏眼睛状态后者偏嘴巴动作组合起来基本够用。如果学校要求数据版权或想体现自主性可以用手机或笔记本摄像头自己采分别录睁眼正常驾驶、闭眼、打哈欠三段视频每段5到10分钟按每3到5帧抽一帧保存去掉模糊帧后挑出清晰的写标签。自采数据的好处是背景、人脸角度、光线和设备都和你最终的演示环境一致模型在现场跑得更稳。目录结构建议直接按训练集和验证集分开摆后面写Dataset类不需要再拆一次data/ train/ close_eye/ open_eye/ yawn/ val/ close_eye/ open_eye/ yawn/类别数量上每类训练图800到1500张、验证图200到300张是一个不会出大错的区间。太少会欠拟合太多对ResNet18来说容易过拟合。关键是切分方式必须按人来切不能把同一个视频的帧同时放进train和val。我曾经见过有人直接把视频帧随机分成8比2验证精度虚高到97%一换实时摄像头画面就降到50%原因就是训练集和验证集里有大量同一场景的相似帧模型把背景记住了而不是学会判断闭眼。切分时先按视频片段分组再按组划分这是花几分钟就能避开的坑。3.2 数据预处理与增强如何把图片统一成224×224为什么用224×224ResNet类网络的默认输入就是224而且眼睛区域的辨识特征没有精细到需要更大分辨率。小尺寸训练更快CPU推理时也更宽松。另一个问题是灰度还是彩色如果只做睁眼闭眼二分类灰度图完全够用只要检测目标里有打哈欠建议保留RGB因为嘴部张开的边缘和口腔颜色在灰度图里区分度会下降。增强策略里有一条硬性规则不要做垂直翻转。垂直翻转会得到倒立的人脸物理上不存在喂进模型只会把特征学乱。下面的Dataset类直接读取data目录下的图片在取样本时才真正加载图像几十万张图也不会把内存挤爆import os from PIL import Image from torch.utils.data import Dataset from torchvision import transforms class DriverStateDataset(Dataset): def __init__(self, root_dir, trainTrue): self.samples [] # 标签映射闭眼0睁眼1打哈欠2 self.class_map {close_eye: 0, open_eye: 1, yawn: 2} phase train if train else val for cls_name, label in self.class_map.items(): cls_dir os.path.join(root_dir, phase, cls_name) for fname in os.listdir(cls_dir): if fname.lower().endswith((.jpg, .jpeg, .png)): self.samples.append((os.path.join(cls_dir, fname), label)) if train: self.transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.3), transforms.ColorJitter(brightness0.15, contrast0.1), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) else: self.transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] img Image.open(path).convert(RGB) img self.transform(img) return img, torch.tensor(label, dtypetorch.long)参数说明RandomHorizontalFlip概率设为0.3而不是0.5因为驾驶员基本正对摄像头过度翻转会影响左右特征的稳定性ColorJitter的brightness和contrast分别给0.15、0.1用来模拟车内光照变化。后半段的Normalize用的是ImageNet预训练权重要求的均值和标准差如果后面加载ResNet18的ImageNet预训练权重这一步必须保持一致否则迁移效果会明显变差。不用预训练模型的话这套归一化参数仍然可用不影响收敛。3.3 训练CNN分类器基于PyTorch的完整代码、参数说明和训练日志怎么看主干网络建议直接上ResNet18而不是ResNet50。ResNet18参数量约11MCPU推理勉强能跑ResNet50在同等条件下帧率几乎腰斩对小数据集也更容易过拟合。如果你做的是只用眼睛的二分类可以自己写一个三层卷积的小网络答辩时解释“为什么不用预训练模型”反而更有底气。下面的训练代码以三分类为例用的是ResNet18加ImageNet预训练权重import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import models device torch.device(cuda if torch.cuda.is_available() else cpu) train_dataset DriverStateDataset(data, trainTrue) val_dataset DriverStateDataset(data, trainFalse) model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) # 替换最后一层全连接输出类别数为3 model.fc nn.Linear(model.fc.in_features, 3) model model.to(device) # 类别不平衡时给少数类更高权重先从[2.0, 1.0, 1.5]起步 criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size8, gamma0.1) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers0) val_loader DataLoader(val_dataset, batch_size64, shuffleFalse, num_workers0) for epoch in range(25): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() loss criterion(model(images), labels) loss.backward() optimizer.step() running_loss loss.item() * images.size(0) train_loss running_loss / len(train_dataset) model.eval() correct 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) preds model(images).argmax(dim1) correct (preds labels).sum().item() val_acc correct / len(val_dataset) scheduler.step() print(fEpoch {epoch1:02d}/25 loss{train_loss:.4f} val_acc{val_acc:.3f}) torch.save(model.state_dict(), driver_state_cnn.pth)参数说明batch_size在6GB显存内用32没问题如果CPU训练建议降到16AdamW配上1e-4的学习率和1e-4的weight_decay是中小数据集上比较稳定的组合比裸的Adam不容易过拟合StepLR每8个epoch把学习率乘以0.125个epoch里经历两次衰减适合这种数据量不大的任务。训练日志主要看两个信号train_loss稳步下降、val_acc逐步上升属于正常train_loss降但val_acc卡住不动说明开始过拟合优先把数据增强调强或减小模型容量loss从一开始就在一个平台来回震荡多半是学习率偏大。存模型用state_dict而不是整model save方便后续换网络结构重新加载。需要说明一点如果你的torchvision版本比较旧weightsmodels.ResNet18_Weights.IMAGENET1K_V1这个写法可能不识别退回经典写法pretrainedTrue就行。3.4 导出与测试用混淆矩阵检查模型是不是“偏科”准确率只能告诉你整体水平无法暴露“闭眼类被大量误判成睁眼”这类问题。训练完一定要输出混淆矩阵和分类报告from sklearn.metrics import classification_report, confusion_matrix all_preds [] all_labels [] model.eval() with torch.no_grad(): for images, labels in val_loader: images images.to(device) preds model(images).argmax(dim1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) print(confusion_matrix(all_labels, all_preds)) print(classification_report(all_labels, all_preds, target_names[close_eye, open_eye, yawn]))如果close_eye这一类recall明显偏低说明模型倾向于把所有样本判成睁眼常见原因就是睁眼样本数量碾压闭眼。解决办法有两类一是做数据增广对闭眼样本做更强的复制和扰动二是给CrossEntropyLoss加class weight权重按“多数类样本数除以少数类样本数”来粗略估计或者从我前面注释里的[2.0, 1.0, 1.5]起步再调。还有一个更实战的建议把三分类改成两个独立的二分类器即eye_model只输出睁眼/闭眼mouth_model只输出打哈欠/不打哈欠。原因是“闭眼同时打哈欠”这类组合样本很难标三分类会把模型绕晕拆开后每个分类器面对的问题都更纯粹调阈值也直观许多。4. 把模型接进视频流疲劳判定与预警的工程实现4.1 视频帧处理与人脸检测先找到脸再送进CNN实时视频流的第一步是人脸检测。OpenCV DNN的SSD人脸检测器用起来很简单模型文件是deploy.prototxt加res10_300x300_ssd_iter_140000.caffemodel网上很容易找到很多OpenCV发行版都带了这两个文件。检测时要把图像减均值并缩放到300×300这是模型的固定输入设置不要为了“更快”随意改分辨率。一个关键优化是跳帧人脸框在视频流里短时间内不会有大的位移每3帧跑一次人脸检测中间两帧直接复用上一次的框裁剪区域和CNN分类仍然是逐帧执行的这样CPU占用能降下来不少。import cv2 class FatigueEngine: def __init__(self): self.face_net cv2.dnn.readNetFromCaffe( deploy.prototxt, res10_300x300_ssd_iter_140000.caffemodel ) self.face_boxes [] self.frame_count 0 self.skip 2 # 每3帧检测一次人脸 def detect_face(self, frame): self.frame_count 1 if self.frame_count % self.skip ! 0: return self.face_boxes h, w frame.shape[:2] blob cv2.dnn.blobFromImage( frame, 1.0, (300, 300), (104.0, 177.0, 123.0) ) self.face_net.setInput(blob) detections self.face_net.forward() self.face_boxes [] for i in range(detections.shape[2]): confidence detections[0, 0, i, 2] if confidence 0.7: continue x1, y1, x2, y2 (detections[0, 0, i, 3:7] * [w, h, w, h]).astype(int) self.face_boxes.append((x1, y1, x2, y2)) return self.face_boxes置信度0.7是个折中点调到0.5会引入大量误检调到0.9会把侧脸和小脸漏掉。检测到多张人脸时取面积最大的作为驾驶员目标同时记录下这张脸的宽高用来按比例裁剪眼睛和嘴巴区域。裁剪比例我一般这样取眼睛区域从人脸框高的15%到45%、宽的20%到80%嘴巴区域从人脸框高的55%到90%、宽的15%到85%。这个比例来自人脸标准五官分布如果你的摄像头装得偏高或偏低需要现场微调10分钟内能完成。4.2 EAR阈值与连续帧计数怎么判断一次“闭眼”而不是“眯了一下”CNN输出的闭眼概率不能直接用因为单帧预测会有抖动正常眨眼持续大约100到150毫秒按25fps算就是3到4帧。如果每一帧概率大于0.8都算闭眼正常眨眼会被累计成大量闭眼时间误报率完全不可接受。惯用做法是加连续帧计数闭眼概率连续超过阈值达到N帧才计一次闭眼事件。N设成3帧在25fps下约120毫秒既能覆盖真实闭眼又不会把正常眨眼算进去。class FatigueState: def __init__(self, fps25): self.fps fps self.eye_closed_frames 0 self.eye_close_duration 0.0 self.yawn_frames 0 self.yawn_count 0 def update(self, eye_closed_prob, yawn_prob, dt0.04): # 闭眼需要连续3帧才计一次事件 if eye_closed_prob 0.8: self.eye_closed_frames 1 else: self.eye_closed_frames 0 if self.eye_closed_frames 3: self.eye_close_duration 0.12 # 打哈欠需要高概率保持约1秒 if yawn_prob 0.7: self.yawn_frames 1 else: if self.yawn_frames int(self.fps * 1.0): self.yawn_count 1 self.yawn_frames 0 property def perclos(self): # 用最近60秒做窗口超过0.4触发一级预警 window_seconds 60 return min(self.eye_close_duration / window_seconds, 1.0)闭眼概率阈值0.8偏向“宁可漏报、不要误报”因为疲劳预警系统误报一次的体验比漏报更差驾驶员很快就不信任它了。如果你发现检测灵敏度不够可以把0.8降一点但每次下降0.05之后都要重新跑一段模拟视频看误报率。dt参数代表一帧耗时默认0.04秒对应25fps。如果实际帧率只有15fps连续帧阈值也要跟着调整3帧在15fps下相当于200毫秒仍然可用但再低就容易被噪声触发。这里的简化PERCLOS用的是“发生闭眼事件时每次记0.12秒闭眼时长”的近似累计实际项目中可以更精确地统计每一帧的闭眼状态。对课程设计而言这个近似量级是安全的只要在论文里写明计算公式即可。如果导师较真可以把闭眼状态逐帧写入一个长度为60秒的环形队列计算队列里闭眼帧的比例同样是60秒窗口的闭眼占比。4.3 预警触发与疲劳状态记录声音、界面和日志三层输出预警分成两级一级用界面红色警示加语音提醒二级用更急促的声音并记录事件详情两级还不足以唤醒深度疲劳的司机那就不是预警能解决的问题了。一级预警触发条件是PERCLOS超过0.4或者连续闭眼超过2秒二级预警触发条件是PERCLOS超过0.6或者在一级预警后60秒内又发生两次长闭眼。这个规则不需要复杂状态机一个FatigueState类加上几个累计值就够了。UI层面不建议用OpenCV的cv2.imshow直接怼完所有功能答辩时需要的是带检测画框、疲劳曲线、预警列表的界面。PyQt5配合QTimer是常见组合QTimer每40毫秒触发一次槽函数读取一帧、送进FatigueEngine、把检测结果画好再以QPixmap显示。核心回调只保留这么一段逻辑def on_frame(self): ok, frame cap.read() if not ok: return state fatigue_engine.process(frame) # 返回状态文本、PERCLOS、预警等级 pixmap convert_cv_qt(frame) # 将BGR帧转成QImage并缩放到QLabel self.label_video.setPixmap(pixmap) self.label_state.setText(state.text)convert_cv_qt需要自己写核心是cv2.cvtColor转成RGB再转QImage这个函数网上到处都是避免用低效的像素级赋值方式。声音预警建议用pygame.mixer加载一段短音频循环播放比winsound跨平台性好也比在PyQt里嵌入QMediaPlayer省事。所有报警和状态数据都要落盘方便复盘和论文数据截图import csv import time with open(fatigue_log.csv, a, newline) as f: writer csv.writer(f) writer.writerow([ time.strftime(%Y-%m-%d %H:%M:%S), round(state.perclos, 3), state.yawn_count, state.alert_level ])日志里至少保留时间戳、PERCLOS、哈欠次数、预警等级这四列。做答辩图表时直接把这个CSV读进来画疲劳趋势曲线比现场重新跑视频再录屏省力得多。5. 疲劳检测系统常见问题排查与避坑记录现象1训练loss卡在0.6左右不再下降val_acc一直停在60%到70%。原因类别严重不均衡模型把所有样本都判成了多数类“睁眼”。解决先打印验证集里的预测分布确认是不是全在某一类然后给CrossEntropyLoss加class weight或者干脆改成两个独立的二分类器。如果改完loss还是不动检查标签顺序和数据集目录是否对得上我曾见过close_eye目录里混入大量睁眼图标签完全标反了。现象2摄像头画面里人脸正常但CNN预测概率来回跳明明睁着眼闭眼概率却忽高忽低。原因OpenCV读取的帧是BGR格式训练时用的图片是RGB模型从来没有见过BGR颜色分布。解决在送入模型前统一做cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)如果用的是PIL读图则不存在这个问题。这种问题最隐蔽因为画面肉眼看不出区别但模型输出完全乱掉。现象3系统每几秒就报一次闭眼人明明很清醒。原因连续闭眼帧阈值设成了1正常眨眼被当成闭眼事件累计。解决把闭眼概率阈值从0.8往上调到0.85同时把连续帧阈值N设到3或4。正常眨眼时长大约100到150毫秒大幅超过这个时间的闭眼才算异常。每次调整后都要用自己的脸实测一遍在摄像头前做10次正常眨眼和5次刻意闭眼看日志里的闭眼事件数量是否接近真实值。现象4白天演示正常晚上或阴天就频繁漏检。原因光线不足时人脸检测器先找不到脸或者裁剪出的眼睛区域暗成一团CNN在低光照样本上没有见过类似分布。解决在数据增强里加入亮度下降的扰动或者对送入检测器的帧先做CLAHE直方图均衡化如果晚上是刚需最可靠的办法是用带补光灯的USB摄像头。单纯把画面调亮会让噪声被放大反而影响状态判断。现象5答辩现场帧率只有8fps画面一顿一顿。原因人脸检测和CNN分类全在UI主线程里同步执行每帧都做完整推理。解决把视频帧读取和检测放进工作线程用queue传帧或者延续跳帧思路每3帧检测一次人脸、复用检测框分类器只吃裁剪后的小图。可以实时打印FPS来验证优化效果按下不表至少要在画面上把当前FPS显示出来这是答辩时很加分的细节。6. 从“能跑”到“能交差”进阶验证与答辩技巧6.1 端到端验证5分钟清醒5分钟疲劳模拟模型和预警逻辑都跑通之后别急着打包交差先做一个端到端验证录制5分钟正常驾驶视频和5分钟疲劳模拟视频疲劳模拟里做刻意闭眼、打哈欠、低头等动作。然后让系统回放这段视频统计误报率和漏报率。误报是指清醒段里触发了预警漏报是指疲劳段里应该预警却没反应。一个能交差的基线大致是清醒段误报0次或1次疲劳段漏报不超过1次。如果误报太多优先调高闭眼概率阈值和连续帧阈值如果漏报太多先看是不是人脸检测把脸漏了再调整裁剪比例。把这两段视频的测试结果整理成表格写进论文的“实验与结果分析”比任何文字描述都直观。6.2 参数校准与交付习惯少翻车的两个细节我通常会加一个启动校准功能系统启动后让司机正常睁眼10秒统计这段时间里闭眼概率分布的均值和标准差然后把闭眼阈值设为均值加三倍标准差。这个做法能解决不同人眼形差异带来的误判——有人天生眼睛小闭眼概率基线就是比常人高。校准完成后把本次阈值打印在界面角落答辩时老师问“阈值为什么设成这个数”你就能给出一个基于数据的回答。交付前的代码习惯同样重要。写一个check_env.py依次检查摄像头索引是否存在、模型文件路径是否正确、日志目录是否可写、torch和cv2版本是否满足要求一键运行并给出明确提示。我自己做这套系统时最后贴心的习惯是把所有参数集中放到config.py而不是散落在各个文件里这样老师复现时只需要改一个文件。如果有打包演示的需要可以尝试用PyInstaller打包成exe注意在spec文件里把训练好的pth模型和prototxt、caffemodel一起加入资源目录打包后如果提示缺动态库常见做法是把torch和cv2的依赖dll一并带上。这套项目做完你会对“训练模型和部署模型完全是两回事”有很深的体会因为训练时只要精度部署时还要问帧率、温度和用户受不受得了误报。希望帮到你。本文还有配套的精品资源点击获取
返回列表