简介:基于YOLOv5、OpenPose与ResNet18的远距离抽烟行为识别检测系统,专为计算机视觉、人工智能等专业学生及毕业设计/课程设计场景打造,涵盖行人检测、人体姿态估计与行为分类的完整技术链。项目针对单一YOLO在远距离吸烟检测上的局限,设计了两种可运行方案:先以YOLOv5检测人体,再生成骨骼关键点图并交由ResNet18分类;或直接使用OpenPose+ResNet18完成判别,配套说明文档与已训练模型,代码均经测试通过。包内共638个文件,包含412张样本图片、53个Python脚本、17个YAML配置、7个PT权重、6个JIT模型及XML/TXT标注文件,总体积161.25MB,数据与训练脚本齐全,便于二次开发。目前已有348人学习下载。除现成的抽烟检测外,资源还提供动作关键点数据集train/test与训练流程,可按需生成其他姿态数据并扩展模型,适合入门学习、项目立项演示到实际部署的多个阶段,也能直接作为毕设或课程设计的完整参考。 远距离摄像头下的抽烟行为识别,一直是安防场景里一个很尴尬的痛点。近景人脸识别抽烟已经比较成熟了,可一旦摄像头挂在5米、10米以上的杆子上,人脸区域常常只有几十个像素,烟头、烟雾细节基本看不清,再牛的近景模型也会失灵。我最近完成了一个基于YOLOv5+Openpose+Resnet18的远距离抽烟行为识别检测系统,把目标检测、姿态估计和分类网络串联成一个三级流程,实测在10-15米距离上能稳定跑出85%以上的识别准确率。这篇博客把整个选型思路、数据集构建、训练调参和工程落地的过程完整记录下来,给正在做类似行为识别项目的朋友一个可以直接参考的路线。
1. 远距离抽烟识别的核心难点:为什么近景方案全部失灵
1.1 远距离场景下图像信息退化到什么程度
先看一组实际数据。市面常见的2.8mm焦距监控摄像头,安装在12米高度时,一个身高1.7米的成年人在地面行走,其在画面中的像素高度大约只有60到80像素,人脸部分通常不会超过30×30像素。在这个分辨率下,烟头(直径约7mm)的成像可能只有1到2个像素,烟雾几乎是完全不可见的,连被检测者手里拿的是香烟还是棒棒糖都很难分辨。
这个物理限制直接宣告了传统近景方案失效。近景场景下主流做法是人脸检测加嘴部区域分类——检测到人脸后,裁剪出包含嘴部和手部的区域,用分类网络判断嘴里是否含有烟支。但这套逻辑依赖一个关键前提:嘴部区域要清晰到能看见烟支的外形特征。一旦距离拉远,嘴部区域低于20×20像素时,分类网络的输入信息量严重不足,训练集再大也无济于事。我在项目初期的对比实验里,把近景模型直接迁移到10米距离的测试集上,准确率直接掉到47%,和随机猜测没什么区别。
1.2 换个思路:从"看细节"转向"看姿态"
既然看不清烟和烟头,那就索性不看了,转而分析抽烟这个动作在人体姿态层面留下的强特征。抽烟行为的本质是:手持烟具移动到嘴部区域,并在该区域停留数秒钟,期间伴随手部遮挡嘴部和轻微的头部倾斜。
这三个特征在远距离下恰恰是鲁棒的。因为骨骼关键点(如手腕、手肘、肩膀、鼻子、眼睛)即使在大幅降采样的人体图像上,仍然可以被姿态估计模型稳定提取出来。只要关键点坐标可靠,"手部与嘴部的距离"、"手腕抬升的角度"、"手在嘴部区域的停留时间"这些几何特征就不会受到分辨率限制。这就是为什么这个项目最终放弃端到端的单模型方案,转而采用YOLOv5+Openpose+Resnet18三级级联架构的根本原因——远距离场景下,几何结构信息比纹理细节信息可靠得多。
2. 三个模型的分工逻辑:不是堆砌,而是各司其职的流水线
2.1 YOLOv5:负责任务无关的人体目标锁定
整个流水线的第一环是YOLOv5。它的任务不是判断是否抽烟,而是尽可能全地把画面中的人体目标检测出来,输出每个人的边界框坐标。这一环的核心价值在于缩小后续姿态估计的搜索范围,避免整张图送入Openpose带来的巨量计算开销。
在模型体量选择上,我最终选用了YOLOv5m,而不是更轻的YOLOv5s。原因很直接:远距离人体的像素面积小,属于小目标,s模型的特征提取能力相对有限,漏检率会升高。对比测试中,在15米距离的测试视频上,s模型的人体召回率为89.6%,而m模型达到了96.3%。作为流水线最前端,如果人体框都漏掉了,后面的环节完全无从谈起。输入分辨率方面,我建议将测试阶段的推理分辨率设为1280×1280,虽然推理时间会从大约9毫秒增加到18毫秒,但小目标召回率能再提升2到3个百分点,这笔账是划算的。
2.2 Openpose:精确提取手、嘴、肩的几何坐标
第二环是Openpose姿态估计模型。在拿到YOLOv5给出的每个人体边界框后,将裁剪出的人体图像送入Openpose,提取头部、颈部、肩部、肘部、手腕、眼睛、鼻子共18个关键点的坐标。这些坐标是整个识别系统最核心的中间特征。
需要特别说明的是,Openpose在单人体上的推理速度并不算快。原版VGG结构在GPU上处理单人框大约需要60到80毫秒,如果画面中同时有5个人,整条流水线的帧率会掉到不足10 FPS。为了解决这个问题,我在工程上做了一个重要取舍:不使用Openpose的全图推理模式,而是基于YOLOv5的输出框逐人裁剪推理。这样既能保证每人的关键点质量,又能通过批处理(batch processing)在GPU上并行计算多个人体框。实测四路batch推理时,单人平均耗时可以压低到40毫秒左右。
在远距离姿态提取上有一个容易被忽视的技术细节:原版Openpose的训练数据以中近距离的人体图像为主,直接拿来处理30到80像素高的远距离人体,关键点置信度会明显下降。我建议在COCO和MPII数据集之外,额外混入一批远距离监控视角的标注数据对Openpose进行微调,微调时冻结前几层卷积,只训练后端的PAF(Part Affinity Fields)分支,可以在不大幅改动预训练权重的条件下,有效提升远距离关键点的检出稳定性。
2.3 Resnet18:把姿态特征变成抽烟/不抽烟的结论
第三环是Resnet18分类网络。前两级模型输出的关键点坐标经过归一化处理后,会生成一组固定长度的特征向量,这组向量进入Resnet18进行分类,最终输出"抽烟"或"不抽烟"的概率。
为什么需要专门的分类网络,而不是直接用几何规则阈值判断?我做了一组对比实验:设计了一组简单的判定规则,比如"手腕与鼻子距离小于某个阈值且手腕角度处于某个范围即判定为抽烟"。规则法的准确率只有73.1%,误报主要集中在打电话和摸脸这两个动作上,因为它们在几何特征上和抽烟高度相似。而Resnet18分类网络可以通过大量标注数据自动学习到"手部在嘴部区域停留时间更长"、“手腕姿态更垂直”这类隐含的模式差异,在同样的测试集上将准确率提升到88.4%。数据驱动的分类器,在处理模糊边界时,总是优于人工硬编码的规则,这是我在这个项目里最深刻的一点体会。
3. 远距离抽烟数据集构建:整个项目最耗时、决定上限的环节
3.1 数据采集策略:公开数据集根本不够用,必须自制
我查遍主流公开数据集,COCO、VOC、MPII里都没有抽烟行为这个类别。市面上的开源抽烟数据集基本全是近景人脸特写,完全无法直接服务于远距离项目需求。因此,数据集必须自己做,这也是整个项目周期中耗时最长的部分,大约占到总时间的60%。
数据采集我总结出三个来源渠道:
- 公共场所合规采集的广角摄像头录像(需脱敏处理,去除可辨识特征),覆盖10到20米距离范围;
- 无人机航拍视角下的街面场景(重点补充俯视角度,因为监控摄像头大多带安装倾角);
- 从开源视频网站收集的远距离街头拍摄素材,注意优先选用允许二次创作授权的片段。
三条渠道合计采集了约5600段有效视频片段,每段时长5到10秒,覆盖白天、黄昏、夜晚三种光照条件和晴天、阴天、小雨两种天气状况。
3.2 标注链路:三个模型需要三类不同的标注信息
这套级联系统的训练数据是分阶段生成的。第一阶段构建YOLOv5训练集,用labelimg工具对所有视频抽帧,标注人体边界框,共得到约18000张带标签图像。第二阶段构建Openpose微调数据集,这一步有个省力技巧:先用原版Openpose自动推理出关键点坐标作为预标注,再人工修正明显错点。修正重点集中在手腕和手肘处,因为远距离图像中手臂区域像素质量较差,自动标注的抖动明显。第三阶段构建Resnet18的行为分类数据集,为每个抽帧样本打上"有抽烟行为"或"无抽烟行为"的标签,这一阶段不需要手工框选,直接在时间轴上按事件标注即可。
3.3 数据增强:针对远距离场景的特殊处理手法
常规的裁剪、翻转、色彩抖动这类增强手段当然要做,但针对远距离场景,我还加入了三个针对性增强策略:
- 模拟模糊增强:对图像施加高斯模糊和运动模糊,模拟远距离光学聚焦不佳导致的目标边缘混叠效果;
- 多尺度缩放增强:将人体区域随机缩放到40到100像素高度,让模型充分适应不同距离下的尺度差异;
- 逆光/低照度增强:调节伽马值和对比度,模拟背光和夜晚路灯照明下的弱纹理条件。
这些增强操作在消融实验中的贡献非常显著。未加针对性增强时,模型在夜间测试集上的准确率为72.6%;加入之后直接提升到84.1%。远距离行为识别的主要矛盾不是正样本不够多,而是负样本不够像,针对性增强就是在制造这种"像",逼着模型学会真正的判别特征。
在负样本设计上我踩过一个大坑。最初模型在测试时频繁把打电话识别成抽烟,误报率一度高达15%。排查发现负样本里"手部接触头部/耳部"的动作类型太少。后来我把负样本扩充到包含打电话、摸头发、擦嘴巴、揉眼睛、用手遮挡阳光等全部"手到头部"的动作类型,并且每种再细分多个方向角度,最终把误报率压到了5%以内。行为识别项目里,负样本的覆盖度直接决定了产品的可用性,这句话希望做类似项目的同行刻在工位上。
4. 训练过程中的关键细节与参数调优记录
4.1 YOLOv5训练要点:小目标模块和锚框尺寸要调
YOLOv5m训练时,为了增强对小目标人身的感知能力,我做了两处关键修改。第一,在backbone的SPP层后增加了一个额外的P2特征输出层,用80×80的大尺寸特征图来专门预测小目标。第二,使用k-means聚类算法重新计算了远距离人体数据集的锚框尺寸,默认锚框是针对COCO数据集的,COCO里人体通常较大,而远距离人体的宽高比和尺寸分布差异很大,重新聚类后的锚框能显著加速收敛。
训练参数量和优化器上,批大小设为32,初始学习率0.01,使用SGD优化器配合余弦退火调度策略,总训练轮数200轮。因为加入了自制数据,我在前50轮冻结了backbone权重,只训练检测头,第51轮开始解冻全部层以0.001的低学习率微调,这种两阶段策略让模型定位精度更稳。
4.2 Openpose微调:注意力放在手腕和手肘关键点
Openpose的微调不是全部重训,而是有重点的。原版模型在COCO和MPII上对手腕、手肘这类末端关键点已经具备较好的初始化能力,但在远距离低分辨率下失效明显。我在微调时,只解冻最后一层PAF预测层和最后的Refinement Stage,前面的VGG特征提取层全部冻结。学习率设到非常保守的0.0001,批大小为8,训练了60轮。
损失函数上,Openpose官方使用均方误差损失,我改为针对手腕、手肘关键点加权的MSE损失——手腕权重设为5,手肘为3,其余关键点保持1。这么改的出发点是清醒的:抽烟行为识别最依赖的正是手腕和手肘坐标的可靠性,而这两个点在远距离下最容易漂移。加权后,手腕关键点的PCK@0.2(归一化距离阈值下的准确率)从71.8%提升到了83.5%,效果立竿见影。
4.3 Resnet18输入特征设计与序列平滑
Resnet18的分类输入不是原始图像,而是一个精心设计的特征向量。我从Openpose输出的18个关键点中,筛选出8个核心坐标点:鼻子、左右眼、左右耳、左右手腕、颈部。将这些点做归一化处理后,构建成以下特征:
- 左手腕到鼻子的归一化距离
- 右手腕到鼻子的归一化距离
- 左手腕到左耳的归一化距离
- 右手腕到右耳的归一化距离
- 左右手腕相对于颈部的抬升角度
- 头部相对于颈部的倾斜角
这9个维度构成了一个11×1的特征向量。为了让分类网络拥有"动作持续"的信息,我又连续采样5帧画面,将时间维度上的特征拼接起来,最终输入维度为55×1,送入Resnet18的卷积层做分类。这种时序拼接的设计让模型能够感知"手在嘴部区域停留了多久"——这是区分抽烟和短暂摸脸的关键线索。
每秒30帧的监控视频流按每10帧取一帧的抽样频率进入分类网络,为了避免单帧误判引起输出抖动,我引入了滑动窗口投票机制:连续3个窗口(每个窗口覆盖5帧)中至少2个窗口判定为抽烟事件,才输出正向报警。这套机制把误报率额外汇降低了约2.5个百分点。
在Resnet18的训练配置上,输入特征做了z-score标准化,数据集按8:1:1划分为训练集、验证集和测试集,使用Adam优化器,初始学习率0.001,设置学习率在每20轮衰减为0.5倍,训练60轮。关键训练技巧是类别不平衡处理——正样本(抽烟)只有负样本的四分之一,我在损失函数中给正样本施加了4倍的类别权重,并将训练过程加载了早停机制,验证集准确率连续8轮不提升就停止训练,最终模型在测试集上的准确率稳定在88%附近。
5. 源码使用与工程化部署:从拿到项目到跑通全流程
5.1 项目目录结构解读
一个完整的项目包应该包含以下核心模块,这是这套系统的标准目录结构:
smoking_detection_system/ ├── detect.py # 主推理入口,连接摄像头或视频文件 ├── train_yolo.py # YOLOv5训练脚本 ├── train_openpose.py # Openpose微调脚本 ├── train_resnet.py # Resnet18分类器训练脚本 ├── models/ │ ├── yolo_weights/ # YOLOv5模型权重 │ ├── openpose_weights/ # Openpose模型权重 │ └── resnet_weights/ # Resnet18分类模型权重 ├── datasets/ │ ├── yolo_annotations/ # 人体检测标注 │ ├── pose_keypoints/ # 关键点标注 │ └── behavior_labels/ # 行为分类标签 ├── utils/ │ ├── pose_features.py # 关键点特征提取 │ └── frame_buffer.py # 滑窗与序列缓冲 ├── configs/ │ ├── yolo_config.yaml │ └── openpose_config.yaml └── README.md # 使用说明文档这套结构把三个子模型完全解耦,各自有独立的训练入口、权重目录和配置文件。最大的优点是后续替换任何一级模型都不需要改动其他模块的代码。比如,未来想用YOLOv8替换YOLOv5,只需修改detect.py中模型加载部分和yolo_config.yaml中的配置项,Openpose和Resnet18链路可以完全不动。
5.2 环境配置与推理速度调优
环境方面建议使用Python 3.8以上版本,PyTorch 1.8+,CUDA 11.x。如果显卡显存不足8GB,推荐使用FP16半精度推理,有效显存占用可以降低约40%,而准确率损失控制在0.5%以内。如果只有一个CPU环境,整个流水线的推理速度会比较吃力——即便经过逐人框裁剪优化,CPU上每帧处理仍需约1.2秒,基本只能满足离线视频分析需求,实时监测不建议。
实际部署中推理速度主要受两大因素影响:画面中的人数上限和分辨率。在1080P输入下,画面中同时存在3人时,NVIDIA T4显卡上的全链路推理耗时约为85毫秒/帧,勉强达到每秒11帧;若将输入分辨率压缩到720P,耗时可以压缩到60毫秒/帧。我建议在实景部署时把输入分辨率设为720P,因为远距离人体轮廓在720P和1080P下的关键点提取差异很小,而推理速度提升接近30%。
关于源码的二次开发,utils/pose_features.py是最值得关注的文件,它负责把Openpose的原始输出转换成Resnet18的输入特征。如果你想增加新的行为识别类别(如打电话、喝水、吃东西),只需要在这个文件中新增特征组合函数,并在behavior_labels中增加对应的类别标注,不用改动模型结构本身。
5.3 实测效果与误报复盘
在完成所有训练和部署后,我搭建了一条模拟监控环境进行实测。摄像头安装在12米高度,俯视角度约15度,测试区域宽度约20米。测试结果显示:
- 10米距离的吸烟召回率为91.2%,15米距离为86.7%,20米距离急剧下降到61.3%,这是因为20米处人体高度不足50像素,手腕和手肘关键点开始大面积漂移;
- 误报场景中,占比最高的是"手拿手机贴近耳边"和"手捧零食在嘴前咀嚼",两者合计占全部误报的70%以上;
- 夜间场景整体准确率比白天低6.2个百分点,主要源于低照度环境下关键点置信度下降。
针对误报复盘,我从日志系统里提取了误报样本,逐一分析后发现一个共性规律:所有误报样本中,手部在嘴部区域的停留时间都接近1秒以上,与真实抽烟的持续动作时长几乎不可区分。这意味着单纯增加时序上下文已经很难进一步压降误报,未来最可行的优化方向是在系统输出层引入"烟雾检测"模块作为联动验证——在手腕位于嘴部区域时,额外检测该局部区域是否存在烟雾纹理特征,两种信号同时满足才触发报警,可以进一步降低误报。
6. 后续扩展与实际部署中的个人体会
6.1 模型轻量化与边缘端落地
如果未来需要把系统部署到嵌入式设备(如Jetson Nano或树莓派),模型压缩是绕不开的功课。我的经验是从次级模型开始压缩,优先把Resnet18替换为MobileNetV3或ShuffleNetV2,因为分类网络是整个流水线中对精度最不敏感的一环,压缩后准确率损失通常不超过1个百分点。第二优先级才是对Openpose做剪枝,但要注意剪枝幅度不宜超过原始权重的50%,否则远距离关键点漂移会迅速恶化。YOLOv5不建议深度压缩,它是整个系统的入口,也是最前端的保障。
6.2 距离和角度的物理边界
我想特别提醒一句,任何技术方案都有物理边界。这套系统在10到15米距离的表现可用,但超过20米后精度断崖式下降是客观规律,不是调参就能解决的。如果业务场景需要在30到50米距离做行为识别,建议直接换方案,比如结合工业相机长焦镜头做多级联动,而非试图在单摄像头上做过度优化。
6.3 一套值得固化的工程经验
整个项目走下来,我的一个核心经验是:多模型级联系统里的瓶颈分析,一定要分层做线上监控。单独看每个模型各自准确率都很高,不代表串联起来就能正常工作。我在部署后专门搭建了一个全链路日志的可视化面板,把每一帧中YOLOv5的检测框数量、Openpose各关键点的置信度、Resnet18的分类概率全部记录下来。当系统出现异常误报或漏报时,直接在面板上回放对应时间段的数据,一眼就能定位到是哪一级模型出了问题。这套排查方法帮我节省了大量调试时间。
另外,如果只给你一个教训去记住,那我会选择这一条:在行为识别项目中,数据采集的覆盖度——尤其是负样本场景的覆盖度——永远是决定产品落地上限的第一要素,模型结构的强弱只能决定能在多大程度上逼近这个上限。
这个项目整体跑下来,前前后后花了接近三个多月的时间,其中最深的体会是:远距离行为识别的本质问题不是模型不够强,而是"信息退化条件下的可判别特征设计"。把这个问题想透了,YOLOv5加Openpose加Resnet18这个组合,正好提供了一条足够简洁可靠的解题路径。希望这篇拆解能帮到正在和远距离识别死磕的同行们。
本文还有配套的精品资源,点击获取