ARTICLE DETAIL

资讯详情

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

Python违规驾驶行为识别源码:检测+分类两级架构落地与调参指南

Python违规驾驶行为识别源码:检测+分类两级架构落地与调参指南 简介面向毕业设计的Python违规驾驶行为识别系统源码融合计算机视觉与深度学习技术可对驾驶过程中的常见违规行为进行检测分析适合人工智能、软件工程等专业学生完成毕业设计或课程实践。压缩包共128个文件大小约64.93MB以Python源码为主包含81个py脚本覆盖数据处理、模型训练、推理检测等核心环节另有8个shell辅助脚本用于环境配置或自动运行模型权重pth/pkl/npy可供直接加载使用图片样本和Markdown/TXT说明文档则辅助理解项目结构与操作方式。已有1061人学习下载。读者可获得一整套可运行的违规驾驶行为识别项目既能参照源码和文档掌握算法设计、模型部署的思路也能基于现有模块二次开发快速完成实验或毕设功能演示实用性和学习价值都比较高。1. 违规驾驶行为识别源码Python 工程怎么落地而不是只交一份 Demo交通事故统计里分心驾驶造成的伤亡往往被酒驾、超速掩盖但对车队管理来说它才是最难抓的。靠人盯视频监控100 辆车一天产生的画面就足以让安全员看吐。这套 Python 违规驾驶行为识别系统源码的定位不是算法比赛的玩具而是一条能跑通「摄像画面输入 → 行为检测 → 分类判定 → 结果输出」的工程链路。它用开源视觉方案处理逐帧图像识别驾驶员是否手持接打电话、吸烟等违规动作并输出可视化标注和日志记录毕设、课程设计、算法预研都能直接用。新手可以拿它当模板照着跑熟手能直接替换模型和参数继续迭代。下面我按拆过的工程实操流程来写尽量把每个环节的取舍和坑都说清楚。2. 架构选型与数据准备两级识别方案背后的三个理由2.1 为什么不用端到端单模型而是“检测 分类”两级串联我拆过不少驾驶行为识别的毕设代码最常见的翻车点不是模型不收敛而是架构选错了。有人拿视频分类网络直接硬切整段视频输入是连续几帧输出直接是「打电话/抽烟/正常」——这种端到端方案看起来简单实际上有三个问题第一它需要海量视频级标注公开的驾驶行为数据集基本没有足够量第二它把「人在哪」和「人在干什么」绑死在同一个特征空间里换一个车内摄像头角度就废第三推理时要攒够一个 clip 的帧数延迟高没法做实时的违规告警。这套源码的选型是两级架构先用目标检测模型定位驾驶员的手部区域和关键物体比如手机再把裁剪后的区域送进行为分类网络判断动作类别。检测管「在哪里」分类管「是什么」各干各的哪个环节效果不好就单独替换。我一般会把这个结构理解成两个接口清晰的模块后面不管是换 YOLO 版本还是换更轻量的分类网络都不需要动整条链路。对于毕设场景这个架构还额外有个好处检测和分类的错误能分开归因答辩时讲技术方案也容易说清楚。源码根目录里的grid.npy就属于这类辅助模块的产物它通常保存了预计算好的网格划分信息。这类文件在运行时用np.load()直接读进来就行训练阶段不需要重新生成。如果你换摄像头安装位置才需要重新跑一遍网格生成脚本——这一点不少初学者会忽略后文避坑章节我会单独展开。2.2 视频样本怎么切帧采样频率与标注策略数据集环节决定了这个系统的上限。驾驶行为识别难在两类样本太少一是违规动作的时长往往只有 3 到 5 秒二是不同司机的手部姿态差异极大。如果直接把原始视频逐帧存下来一张 1080p 图片约 1.5 MB10 分钟视频就是近万帧硬盘和标注时间都吃不消。常见做法是按时间步长抽帧我用的是每 0.2 秒取一帧的节奏import cv2 import os video_path data/train_video.mp4 output_dir data/frames os.makedirs(output_dir, exist_okTrue) cap cv2.VideoCapture(video_path) fps cap.get(cv2.CAP_PROP_FPS) frame_id 0 step int(fps / 5) # 固定每 0.2 秒取一帧 while True: ret, frame cap.read() if not ret: break if frame_id % step 0: cv2.imwrite(os.path.join(output_dir, f{frame_id:06d}.jpg), frame) frame_id 1 cap.release()步长为什么要取fps / 5而不是逐帧两个原因。第一邻近帧之间画面差异很小连续帧只会让训练集里充满高度相似的样本模型容易过拟合到重复模式上第二驾驶行为的动作周期通常以秒计0.2 秒一帧足够捕获到接电话、抽烟这类动作的关键姿态还能把数据量压缩到原来的二十分之一。如果你想抓更快的动作比如擦眼睛、低头看手机可以把分母加大到 10也就是每 0.1 秒一帧但要注意后期标注成本会成倍增长。标注策略上这个场景不建议做像素级分割用检测框加行为标签就够了。对每一帧框出驾驶员手部区域标签写死成三类normal、phone、smoke。如果某个动作发生在模糊或遮挡严重的帧里直接跳过这一帧而不是硬标一个框这样能避免给模型喂脏数据。另有一点训练集里「正常驾驶」帧不要占比超过 70%否则模型会学成偏科生——上来不管看到什么都倾向输出normal。2.3 配置文件与参数体系先读懂再动手拿到源码后先别急着跑训练把配置文件读一遍能省下半天排错时间。这套系统的配置集中在config.py里核心参数如下# config.py # 输入来源与运行设备 INPUT_VIDEO data/sample.mp4 SOURCE_TYPE video # video / camera DEVICE cpu # cpu / cuda # 模型权重路径 DET_MODEL weights/yolo_det.pt CLS_MODEL weights/behavior_cls.onnx # 推理阈值 CONF_THRESH 0.45 # 检测框置信度阈值低于它的框直接丢弃 IOU_THRESH 0.5 # NMS 交并比阈值控制重叠框的合并 # 性能控制 FRAME_SKIP 2 # 每隔 2 帧做一次推理其余帧直接复用上一次结果 # 驾驶位裁剪区域格式为 [x, y, w, h] ROI [560, 40, 720, 720]这套配置里最容易被忽略的是ROI。车内摄像头角度固定时驾驶员基本只出现在画面的某个子区域副驾和后座会白白消耗检测算力还会引入误检。把ROI划出来只在驾驶位区域做检测精度和速度都能提升。FRAME_SKIP则是帧率换精度的开关设成 2 表示每 3 帧只推理一次中间两帧直接沿用上一次的框位置后面第五章我会专门把这几组数的组合规律讲透。3. 环境搭建与工程入口从裸机到跑通识别只需要四步3.1 Python 环境与 CUDA 检查这套源码对硬件的要求比想象中低CPU 机器也能跑只是推理速度在每秒几帧左右。有独立显卡的话先确认驱动和 CUDA 状态避免后面 PyTorch 装错版本导致torch.cuda.is_available()始终返回Falsepython --version nvidia-sminvidia-smi正常输出会显示驱动版本和 CUDA 版本比如 CUDA 11.8。建议用 conda 新建一个独立环境不要直接装在系统 Python 里因为后续装 PyTorch、OpenCV 时经常出现版本回退或冲突独立环境能让你随时删掉重来。我一般习惯命名为driving_behaviorPython 版本固定 3.8 或 3.9 都可以太新的 Python 版本部分预编译 wheel 可能还没跟上。3.2 依赖清单与安装命令工程依赖以视觉和深度学习库为主典型的requirements.txt大概长这样opencv-python4.8.0.74 numpy1.24.3 torch2.0.0 torchvision0.15.0 onnxruntime1.15.1 PyYAML6.0 tqdm4.65.0 Pillow10.0.0安装时有个值得注意的细节先把torch和torchvision通过官方源装好再装opencv-python最后装小依赖。如果直接把requirements.txt一把梭有可能会因为依赖解析顺序问题装出一个 OpenCV 找不到libGL.so.1的环境。装完跑一句验证python -c import cv2, numpy, torch; print(cv2.__version__, numpy.__version__, torch.__version__)能正常打印版本号就说明环境就绪。如果在这一步报libGL.so.1缺失在 Ubuntu 上执行apt install libgl1 -y就能解决这个坑几乎每个做视觉的同学都踩过。3.3 源码目录结构与启动入口拆完已知源码文件目录结构大致如下├── src/ # 核心代码 │ ├── detect.py # 主入口视频读取、推理、可视化 │ ├── config.py # 全局配置参数 │ ├── models/ │ │ ├── det_model.py # 目标检测模型封装 │ │ └── cls_model.py # 行为分类模型封装 │ ├── utils/ │ │ ├── nms.py # 非极大值抑制后处理 │ │ └── logger.py # 日志与告警记录 │ └── weights/ # 训练好的权重文件 ├── data/ # 视频样本与帧数据 ├── grid.npy # 预计算网格数据runtime 加载 ├── requirements.txt └── README.md启动入口是detect.py支持从视频文件和摄像头两种来源取流。视频文件模式适合验证效果摄像头模式适合现场演示。命令行参数设计得比较直接python src/detect.py --source data/sample.mp4 --weights weights/best.pt --conf 0.45--source指定输入路径--weights指定权重文件--conf覆盖配置文件里的置信度阈值。加--camera 0则换成调用本机默认摄像头。第一次跑建议用视频文件模式一是方便观察每帧输出二是避免摄像头初始化失败干扰判断。4. 源码核心逻辑拆解检测、分类、可视化怎么串成一条流水线4.1 检测模型推理把画面里的人和手持物品先找出来主流程是一个标准的视频循环读帧 → 裁剪 ROI → 检测 → 分类 → 画框 → 输出。第一级检测模型负责任务是把手部区域和手机、烟这类手持物品框出来。这里有一段典型的封装代码import torch def detect_objects(model, image, conf_thresh): 对一帧图像做目标检测返回过滤后的检测框列表。 返回值的每个框是 (x1, y1, x2, y2, score, class_id) results model(image) boxes [] for pred in results.xyxy[0]: x1, y1, x2, y2, score, cls pred.tolist() if score conf_thresh: continue # 低置信度框直接丢弃减少分类阶段压力 boxes.append((x1, y1, x2, y2, score, int(cls))) return boxesresults.xyxy[0]是检测模型输出的所有候选框每一项是[左上角x, 左上角y, 右下角x, 右下角y, 置信度, 类别ID]。这里的过滤逻辑是纯粹基于置信度的粗筛精确的框合并交给utils/nms.py里的 NMS。注意一点不要把conf_thresh调得太低否则后续分类网络要处理的裁剪区域会暴增。做过真机测试的人都清楚每帧多十几个候选框累积下来 fps 掉得非常明显。4.2 行为分类推理从手部区域判定违规动作检测模型输出的是带坐标的框但它只告诉你「这里有东西」不告诉你「这个动作违规没有」。第二级分类网络接手做这件事对每个检测框裁剪出对应区域缩放到分类网络要求的输入尺寸推理得到行为类别。代码结构大致是这样import cv2 import torch CLASS_NAMES [normal, phone, smoke] def classify_behavior(cls_model, frame, box): 把一个检测框对应的图像区域送进分类网络。 x1, y1, x2, y2, score, cls_id box crop frame[int(y1):int(y2), int(x1):int(x2)] # 分类模型输入尺寸固定为 224x224缩放会损失细节尽量保持长宽比 crop_resized cv2.resize(crop, (224, 224)) tensor transform(crop_resized).unsqueeze(0) with torch.no_grad(): output cls_model(tensor) label_id int(output.argmax(dim1)) return label_id, CLASS_NAMES[label_id]两个细节值得注意。第一transform里必须包含归一化且均值和标准差要与训练时一致不然分类精度会突然崩掉这也是很多复制来的源码换了机器就不好使的隐性原因。第二argmax是最简单的判定方式工程上更稳妥的做法是再加一个阈值判断——如果最高的类别概率都不到 0.6直接归类为「不确定」而不是硬判一个结果。实时监控系统里「不确定」比「错判」要好处理得多后者会引发大量无效告警。4.3 可视化与日志输出让每一次告警可追责最后一步是把结果画到画面上同时写入本地日志。这一环看似简单但在毕设答辩或真实部署里很关键因为它是唯一能被外界感知的输出物import cv2 import time log_file open(alarm_log.txt, a, encodingutf-8) for box in current_boxes: x1, y1, x2, y2, score, cls_id box label CLASS_NAMES[cls_id] # 违规行为用红色框正常行为用绿色框 color (0, 0, 255) if label ! normal else (0, 255, 0) cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), color, 2) cv2.putText(frame, f{label} {score:.2f}, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) # 仅在检测到违规时写日志避免正常帧刷屏 if label ! normal: timestamp time.strftime(%Y-%m-%d %H:%M:%S) log_file.write(f{timestamp}|{label}|{score:.2f}\n) log_file.flush()日志格式用|分隔而不是纯文本描述目的是方便后续用脚本统计比如按小时统计违规频次、按行为类型分析高发时段。flush()每次写入立即落盘防止程序异常退出时丢失最后几条告警记录。这个习惯是我吃过亏后养成的——有一次演示到一半进程崩了日志文件里恰好丢了最关键的一条违规记录现场非常尴尬。5. 训练与推理参数调优置信度、IoU、帧率这三组数怎么配5.1 超参数配置表哪些参数影响精度哪些影响速度参数乱调是新手最容易犯的错默认值不是给你随便改的。我把核心参数按「精度向」和「速度向」分成了两类参数默认值主要影响调参方向建议CONF_THRESH0.45检测框数量与误检率调高减少误报调低减少漏报IOU_THRESH0.5NMS 后留下的重复框调低更容易合并重叠框调高保留更多细节FRAME_SKIP2推理频率直接影响 fps调高速度快但短动作可能漏掉CLS_THRESH0.6行为分类判定的置信度门槛调高减少违规误报调低更敏感输入分辨率640小目标烟头能否被检出调高精度好但推理时间翻倍这套组合的底层逻辑是精度和速度不可能两头都占必须根据你的部署场景压一边。毕设演示通常不需要真实时可以偏精度真要做车队告警速度和漏报率才是第一位的。5.2 置信度与 IoU 的取舍漏报和误报挑一个CONF_THRESH和IOU_THRESH是两兄弟但管的不是一回事。CONF_THRESH过滤的是「模型有多大把握认为这是个目标」IOU_THRESH处理的是「两个重叠框是不是同一个目标」。实际操作中有一个血泪经验抽烟行为的检出率经常上不去因为烟头在图像里小到只有几个像素检测模型给它的置信度天然偏低。如果你把全局CONF_THRESH从 0.45 降到 0.3抽烟能检出来一些但误检也会明显变多——画面里的方向盘 logo、杯架上的银色杯子都会被框出来。常见的做法是给不同类别设不同的置信度阈值。检测模型的输出里带了class_id你完全可以在解析结果时写一个分支判断。比如手机类用 0.45而烟这个类别单独降到 0.3代价是多处理几个假框但关键的违规样本保住了。这是我认为这套源码最值得改的一处逻辑。IOU_THRESH的坑则藏在另一个方向设得太高NMS 可能合并不掉同一只手和手机的重叠框一个违规行为被画成两个框日志里就会重复记录设得太低框的裁剪区域不完整分类网络拿到的可能只是一只手的局部分类结果自然不准。默认 0.5 是平衡值除非你明显看到重复框问题否则不用动。5.3 帧率与抽帧策略实时系统的流量控制FRAME_SKIP 2的真实含义是每 3 帧里只推理一次另外两帧直接用上一次的检测结果。这样系统 fps 可以提升三分之一到一半但也会让动作检测产生「颗粒感」。做一个简单换算视频源是 30fps取FRAME_SKIP 2后实际每秒只分析 10 帧一个持续 2 秒的接电话动作在这 2 秒里最多只能抓到 20 个关键帧减去动作起止的模糊帧真正有效的可能只有 10 帧左右。如果你接的是摄像头实时流还有一个额外因素摄像头读取帧的速度有可能比推理速度慢导致cap.read()阻塞这时加再多的FRAME_SKIP也没有用。我之前的处理方式是把摄像头帧读取和推理分开在两个线程读帧线程只管往队列里塞推理线程按自己的节奏消费。毕设源码里不一定要做到这个程度但你要知道瓶颈在哪里——先用time.time()分别统计读帧耗时和推理耗时哪个大就优化哪个而不是盲目调参。另外有一点容易被忽略离线视频处理和实时摄像头处理的参数组合应该不同。离线视频可以从头到尾每帧都推理因为出错可以倒回去重看但实时告警系统一旦漏掉就再也没有补救机会。我的做法是在config.py里写死两套预设离线用PRESET_OFFLINE实时用PRESET_REALTIME切换的时候直接改SOURCE_TYPE就行不用每次手动调三个参数。6. 常见问题避坑与部署进阶五个高频坑和一个交付技巧6.1 五个高频坑的现场表现与解决方式坑一程序启动报错module cv2 has no attribute imshow。现象明明pip install opencv-python成功了代码跑到cv2.imshow就抛异常。原因Linux 服务器上装的是opencv-python-headless或者 OpenCV 版本太旧图形接口被裁剪了。解决在虚拟环境里重装pip uninstall opencv-python-headless后再装opencv-python。如果你不需要弹窗显示只做离线批处理直接用 headless 版反而更轻。坑二驾驶员位置换了系统把副驾的持手机动作也算违规。现象换个摄像头角度检测框频繁落在副驾位日志里全是无效告警。原因ROI参数还是原来的位置没有跟着摄像头安装位置调整。解决把当前画面的截图打印出来手动量一下驾驶员所在区域的像素坐标更新config.py里的ROI。grid.npy如果保存的是区域划分信息也需要重新生成这一点很多毕设源码的 README 里都没写。坑三换了自己的测试视频结果全是normal一条违规都检不出来。现象视频能正常读框也能画出来就是永远输出正常。原因训练数据里正常样本比例过高或者测试视频与训练集的亮度、色彩差异太大。解决先看检测阶段是否框到了手部区域——如果检测框就没找到手问题在检测模型如果检测框正常而分类全偏问题在分类模型。按这两条路径排查比瞎调参数有效。数据层面可以做亮度扰动和直方图均衡化让模型对光照变化更鲁棒。坑四告警有延迟违规动作已经结束 2 秒后才弹出来。现象实际违规发生在第 5 秒日志里记录的时间是第 7 秒。原因FRAME_SKIP设置过大或推理本身耗时过长系统根本来不及实时跟上画面。解决分别统计单帧推理耗时和目标检测耗时如果在 CPU 上单帧推理超过 150ms就不要强行追求实时把SOURCE_TYPE改成离线视频等结果延后几秒不影响判断如果有 GPU把DEVICE改成cuda通常能直接快 5 倍以上。坑五模型训练时 loss 降不下去验证集上频繁震荡。现象训练前 20 个 epoch 里 loss 像心电图一样上下跳动最优值始终徘徊在同一个区间。原因学习率设置过高或 batch size 太小优化器在损失曲面边缘反复横跳另一个可能是数据集的标注存在大量噪声比如一张手部被方向盘遮挡的图被标成了phone。解决先把学习率降到当前的十分之一观察一轮如果 loss 平滑了说明是优化问题如果还是震荡检查标注。我一般会在训练脚本里加一个每 5 个 epoch 保存一次 checkpoint 逻辑这样即使后面训练崩了也能回退到之前的稳定权重。6.2 部署进阶导出 ONNX 并集成到告警系统毕设跑通之后如果还想往前走一步最值得做的就是把 PyTorch 权重导出成 ONNX。PyTorch 模型依赖训练框架导出成 ONNX 后就能脱离 PyTorch 用onnxruntime推理体积更小推理速度更快部署到服务器不需要装笨重的torch全家桶。导出脚本很简单import torch model torch.load(weights/best.pt, map_locationcpu) model.eval() dummy_input torch.randn(1, 3, 640, 640) torch.onnx.export( model, dummy_input, weights/best.onnx, opset_version11, input_names[input], output_names[output] ) print(export done)导出后验证一下输出是否一致用onnxruntime跑一次试试import onnxruntime as ort import numpy as np sess ort.InferenceSession(weights/best.onnx) input_name sess.get_inputs()[0].name # 假设这里有一张预处理后的图片shape 为 (1,3,640,640) result sess.run(None, {input_name: preprocessed_image}) print(result[0].shape)这里的关键是dummy_input的 shape 必须和训练时一致如果你的检测模型输入是 640x640导出时的dummy_input就不能用别的分辨率。导完之后整个系统在部署机上只依赖onnxruntime和opencv-python依赖链条短了一半现场演示时翻车的概率也小一半。我自己的习惯是每次拿到一份毕设源码先把配置文件和模型导出脚本走一遍确认「没跑通前不入参调优」——省得参数改了环境没搭建好根本判断不了是哪个环节出了问题。从那以后我每次给团队演示前都强制先走一遍「环境重建 → 离线视频 → 模型导出」三个步骤确认整条链路干净了才动配置。这套方法帮我避开过很多次现场事故希望帮到你。本文还有配套的精品资源点击获取
返回列表