ARTICLE DETAIL

资讯详情

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

基于YOLOv12的吸烟识别检测系统实战:从数据集到PyQt5部署

基于YOLOv12的吸烟识别检测系统实战:从数据集到PyQt5部署 从挑选模型到部署成完整系统这篇把基于YOLOv12的吸烟识别检测项目拆开揉碎。环境配置、数据集准备、训练参数、PyQt5界面、登录注册、摄像头联调每个环节都用实际跑过的配置和踩坑记录说话照着操作能少走很多弯路。1. 为什么这个项目非要上YOLOv12换个老模型不行吗1.1 吸烟检测到底难在哪目标小、光线乱、姿态多做吸烟识别很多人第一反应是把“叼着烟的人”当成一个整体目标去训练。这个思路在固定机位的室内场景勉强能跑通一旦放到楼道、园区、办公区这类半开放环境立刻原形毕露——人姿态稍微偏一点或者手里烟被身体挡住半边模型就识别不出来了。真正稳定可靠的做法是检测两个层次的目标烟支本身cigarette和人的头部区域。烟草的火光、烟支轮廓在监控画面里往往只有十几个像素宽属于典型的小目标检测。这也是为什么这个项目选YOLOv12而不是更老牌的YOLOv5。YOLOv5在小目标上的表现不是不能看而是在实际监控的远距离画面里漏检率偏高尤其当烟雾背景杂乱时候选框容易被噪声干扰。我一开始也想着用YOLOv8n直接上毕竟网上资料多、踩坑少。但对比下来发现YOLOv12在特征提取阶段做了更细的跨尺度融合对小尺寸物体的响应更敏感。换成大白话说v12在“看不清的小东西”上比v8多了一层注意力补偿这恰恰是吸烟检测最需要的。1.2 YOLOv12针对这些痛点做了哪些改进YOLOv12最核心的变化是把注意力机制重新设计了一遍。以往Transformer结构里的全局注意力计算量太大放到YOLO这种追求实时性的模型里帧率根本撑不住。v12引入了区域注意力area attention和可变形卷积的组合在不显著增加推理成本的前提下让模型能更灵活地聚焦到图像中的关键区域。从这个项目的实际需求看这个改动的价值体现在两个地方。第一烟支目标太小普通卷积的固定感受野很难在一开始就覆盖到可变形卷积会根据特征动态调整采样点位置相当于让模型“主动找烟”。第二监控画面的光照变化剧烈——白天逆光、晚上霓虹灯、雨天反光区域注意力让模型对局部亮度变化没那么敏感训练出来的权重在跨场景迁移时更稳。我实际对比过同一个数据集上YOLOv8n和YOLOv12n的表现mAP50提升不算夸张大概4到6个点但漏检率下降比较明显。烟支这种连续帧里可能出现又消失的目标漏检率比mAP更能说明问题。所以这个项目最终锁定了YOLOv12。1.3 模型选型v8n、v11n、v12n到底怎么选这里要给个清楚的选择逻辑。YOLOv8成熟稳定TensorRT部署资料多适合机器性能有限、又不想折腾新框架的团队。YOLOv11在v8基础上优化了C3K2模块和训练策略精度小幅提升但网络结构相对保守对小目标的改善有限。YOLOv12在架构层面动了刀精度上限更高代价是新的注意力模块在某些老显卡上优化不到位推理速度可能比v11略慢。我在项目里最终用的是yolov12n.pt作为预训练起点然后基于它微调。选择n版而不是s或m理由非常现实这套系统要同时开摄像头预览、跑推理、刷新UI显卡稍有压力的环境下n版在1080p视频流上能保持20到30帧s版直接掉到15帧以下。如果你们之后要接多路摄像头起步一定要留足性能余量。2. 环境配置YOLOv12最容易翻车的地方全在这2.1 CUDA、PyTorch、Ultralytics的版本三角关系YOLOv12的环境配置比v8时代要敏感得多原因在于新模型依赖的算子更复杂PyTorch版本和CUDA版本一旦不匹配训练时会出现各种奇怪的报错。不少初学者问“yolov12环境怎么配”其实核心就一句话让Ultralytics包的依赖需求和PyTorch的CUDA支持版本对齐。我实测下来比较稳的组合是Python 3.10或3.11不要用3.13很多深度学习依赖还没跟上CUDA 11.8 cuDNN 8.9PyTorch 2.1.2或2.2.2Ultralytics 8.3.x及以上v12权重需要这个版本才认opencv-python 4.9以上如果你用CUDA 12.1对应的PyTorch要选2.3或2.4那一档。这里最容易出的问题是先装了Ultralytics最新版然后装PyTorch时选了不匹配的CUDA轮子结果训练时直接报CUDA error: no kernel image is available。这个报错基本就是版本错位重装一套对齐的版本就好别急着怀疑代码。2.2 配置建议用虚拟环境隔离依赖我习惯为每个视觉项目单独建一个conda环境吸烟识别这个项目也不例外。命令就这几条conda create -n smoke python3.10 conda activate smoke pip install torch2.2.2 torchvision0.17.2 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pyqt5有一点要提醒PyQt5和Ultralytics之间偶尔会撞Qt插件版本表现出来就是界面启动时报“qt.qpa.plugin: could not load the xcb plugin”。解决办法是检查PyQt5和PyQt5-Qt5的版本一致性缺了就一起重装。这个坑我在配置参考里给过后来换到新环境时又踩了一遍确认是官方源里PyQt5-Qt5版本滞后导致的直接指定版本安装就能稳定解决。3. 数据准备与标注COCO里只有几十张吸烟图模型靠这个学不会3.1 数据集从哪里来公开数据集和自采数据怎么搭配这是整个项目里最花时间、也最决定效果上限的环节。COCO数据集里有80类其中确实包含cigarette这个类但我粗略统计过train2017的标注文件cigarette类对应的图片数量大概只有六七十张实例数也刚到一百出头。拿这点数据去训练一个专用吸烟检测模型纯属杯水车薪。很多YOLO开源项目宣传“下载即用”其实都是在这个基础上硬扛效果好不了。正确做法是三路数据混合第一路从Visual Genome、Open Images这类公开数据集里抽取带有cigarette标签或近似标签如cigar、lighter的图片。注意标签不一定完全匹配下载后要人工过一遍把标注框明显不准的挑出去。第二路自己拍摄。不需要专业设备手机就行。我找了几个抽烟的同事在园区吸烟亭、楼道拐角、地下停车场不同光照条件下拍了大概两千张素材。重点拍远距离、侧身、逆光、遮挡的情况这些才是监控场景的真实挑战。第三路公开的吸烟识别专项数据集。Github上搜smoking detection dataset能翻到一些质量参差不齐但可以作为补充样本。使用前务必确认授权协议毕竟这个项目之后可能会商用。3.2 标注工具的选型与标注规范标注工具我推荐X-AnyLabeling。相比老牌的LabelImg它对YOLO格式的支持更好直接输出txt格式不用费劲做格式转换而且支持长条目标的旋转框标注烟支这种细长目标很适用。类别定义不要只标cigarette。我建议按场景定义两类cigarette烟支、烟头、打火机点火状态smoke明显烟雾区域可选项用于辅助判断为什么单列一个smoke类因为很多监控画面上烟支被手指挡住只有烟雾可见。如果模型能看到烟雾但看不到烟支后续规则引擎可以给它一个较低置信度的吸烟提示。当然这两类的数量要控制比例避免smoke样本太多导致模型主次不分。我最终cigarette类标注了4000多框smoke类标注了1200框左右训练时把smoke类权重压低问题不大。3.3 数据清洗与目录组织容易忽略但极其重要的一步清洗比标注还重要。我第一版模型效果差后来检查发现训练图里有将近两百张是重复帧——拍视频截帧的时候相邻帧几乎一样模型相当于把这些重复样本反复学习验证集稍微变一下角度就崩。清洗策略分三步按感知哈希去重相似度超过0.9的直接删掉。遍历所有xml或txt标注文件筛选出宽高小于8像素的框——这种框太极端会让模型学到无意义的纹理删掉更干净。检查类别标签是否写错特别是公开数据集抽出来的统一脚本核对一遍。目录结构按YOLO标准格式组织dataset/ images/ train/ val/ labels/ train/ val/划分比例8:2。划分时保证同一批连续帧不落在两个集合里不然模型被“剧透”验证指标漂亮得离谱落地上根本没那么强。4. 训练参数设计与结果判读别只盯着mAP504.1 关键训练参数怎么调训练过程中我尝试了多组参数组合最终比较顺的配置是参数取值说明modelyolov12n.pt预训练权重初始化imgsz640兼顾小目标与推理速度epochs120数据量不大120轮足够收敛batch16根据显存动态调整optimizerAdamWv12默认配AdamW比SGD稳一点lr00.001微调场景别太高lrf0.01余弦退火收尾mosaic0.8保留数据增强提升泛化close_mosaic10最后10轮关闭Mosaic避免过度增强干扰cos_lrTrue配合学习率调度开头几轮不要急着看效果。抽烟目标和人的脸、手重合度高模型前期会把几乎所有细长目标都当成cigarette这是正常的。重点看第30轮到60轮之间的收敛曲线如果mAP50一直不升优先检查数据清洗而不是调参。4.2 结果文件怎么读混淆矩阵和PR曲线要结合看训练结束后Ultralytics会输出results.csv和混淆矩阵。很多人只翻一眼mAP50就下结论这对吸烟检测项目来说容易误判。原因在于这类项目存在严重的正负样本不均衡——背景区域远比烟支区域多mAP50高可能只是因为负样本判得准并不意味着烟支不漏检。我习惯这样看结果先看混淆矩阵中cigarette类的漏检率真实为cigarette但预测为background。再看PR曲线在低置信度区间是否还有明显拐点。如果模型在Recall0.8附近置信度就掉得很快说明训练数据里的难样本不够需要补充遮挡场景。最后看F1-confidence曲线找到F1最高的置信度区间。我这个项目最终把置信度阈值定在0.4既能压住误检也不至于把真烟滤掉。4.3 训练过程中最常见的两个坑第一个坑是Mosaic增强关闭时机不对。有次我把close_mosaic设成0最后一轮精度掉得厉害val/box_loss突然抬头。后面改成最后10轮关闭验证集损失平稳多了。第二个坑是权重文件路径选错。训练结束后文件夹里有best.pt和last.pt网上有些教程让直接用last.pt部署其实last是最后一步的模型状态不是最优状态。部署务必用best.pt这个错特别低级但群里见到的频率不低。5. 登录注册与用户管理PyQt5界面里最容易被忽略的模块5.1 为什么识别系统要带登录注册很多同类项目demo就是打开界面直接开始识别我偏要加一个登录注册模块。原因不复杂识别系统的运行日志里包含时间、截图、统计结果这些信息属于行为数据如果人人都能打开软件随意查看后续的管理责任说不清。另一个原因是多用户场景——不同管理员关心的摄像头分组、统计报表不一样登录后才能按用户加载对应的配置。在这一步上我没有做复杂的权限系统一个轻量的用户表足够。注册、登录、记住密码、退出登录四项功能齐全数据落在本地SQLite里不需要额外部署数据库服务。5.2 SQLite存储与密码安全策略用户表结构大概是这样CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, salt TEXT NOT NULL, password_hash TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );密码绝对不能明文存。我用的方案是每个用户随机生成一个salt然后对“salt密码”做SHA-256哈希库里面存salt和hash两个字段。登录时重新拼接计算比对哈希结果。这里说明一下生产级系统建议升级到bcrypt或PBKDF2这个项目是桌面单机部署SHA-256加salt已经够用但读者如果做Web版不要学这个简化版本。注册界面的校验也要做完整用户名非空、长度3到20位、不包含特殊字符、两次密码一致、用户名不重复。每一项用弹窗提示反馈不能静默失败否则用户不知道发生了什么。5.3 登录窗口与主窗口的切换逻辑PyQt5里最常见的错误是登录窗口关闭了但主窗口还没初始化导致程序直接退出。我的做法是在主程序入口先创建登录窗口并exec()登录成功后把用户信息传给主窗口构造函数主窗口show()登录窗口hide()而不是close()。这样退出主程序时才真正释放登录窗口资源。切换代码骨架大致是这样app QApplication(sys.argv) login LoginWindow() if login.exec() QDialog.Accepted: main_win MainWindow(login.user_info) main_win.show() sys.exit(app.exec_())这套逻辑跑得很顺。记住用户名、密码的“记住我”勾选框用QSettings存起来下次启动自动填充体验会好很多。6. 识别主界面与多线程推理UI卡顿的根因和解决办法6.1 主界面布局预览区、控制区、日志区三块主窗体设计不追求花哨追求信息一目了然。顶部是一块大幅图像预览区用来显示原始摄像头画面或识别后的标注画面。左侧放控制面板模式切换按钮图片/视频/摄像头、文件选择按钮、开始识别与停止识别按钮、置信度滑块。底部是日志输出区用来打印识别时间、目标数量、帧率。识别结果除了画框还把每一帧的检测框数量做一个累计统计。点击“导出报表”按钮可以生成当天的检测记录CSV文件包含时间、置信度、框坐标和截图路径。这个功能直接对应落地场景——门卫或者后台管理员不需要盯画面只看报表就能知道哪个时间段、哪个点位出现过吸烟行为。6.2 多线程推理不能在UI线程里跑YOLOUI卡顿是所有PyQt视觉项目的通病根因只有一个把模型推理、图片缩放、格式转换这些耗时操作全部放在主线程里执行。QTimer每30毫秒触发一次刷新结果上一次推理还没跑完下一次又来了消息队列堆满界面自然僵住。正确做法是独立推理线程。我在项目里用QThread实现主线程只负责发信号和接收结果class InferenceThread(QThread): frame_ready pyqtSignal(object) result_ready pyqtSignal(object) def run(self): while self.running: ret, frame self.capture.read() if not ret: continue results self.model.predict(frame, confself.conf_thres, verboseFalse) annotated results[0].plot() self.frame_ready.emit(annotated)注意emit的是深拷贝对象或只读数据不要在子线程里直接修改主线程的界面控件否则大概率出现段错误。我踩过一次原因就是在线程里调了QLabel.setText程序随机崩溃调了半天才发现是线程安全问题。6.3 摄像头模式下OpenCV的读取与显示优化读摄像头用cv2.VideoCapture(0)默认分辨率可能只有640x480画质太差。手动设置成1280x720cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)如果要追求更高帧率可以把推理帧率限定在15帧预览帧率保持30帧。这里不搞复杂方案做法就是解码后先进一个队列推理线程按帧间隔取帧展示的是最近一帧而不是逐帧全部处理观感上很流畅CPU和显存占用又低。7. 部署实测摄像头、图片、视频三种模式下的真实表现7.1 三种输入源如何统一推理接口我把识别逻辑封装成一个SmokeDetector类内部只暴露process_frame方法输入是ndarray输出是标注后的ndarray和检测统计。图片模式下直接整帧调用一次视频模式下按帧循环调用摄像头模式下由推理线程循环调用。三个模式共用同一套检测逻辑代码量少维护也省心。图片模式适合单张快速验证响应时间在最严苛的CPU环境下也就1到2秒。视频模式对历史录像做批量分析比如楼道摄像头存下来的文件拖进去就能跑全程不需要人工干预。摄像头模式则是实时巡检识别到目标后会在框上标出置信度同时写入日志。7.2 实测数据CPU和GPU的帧率差异我自己的测试环境有两台一台是RTX 3060 Laptop6G显存另一台是纯CPU核显笔记本。实测数据供参考RTX 3060 YOLOv12n640x640输入推理耗时约25到35毫秒加上NMS后整体30到45毫秒界面显示约20帧。纯CPUi7-12700H单帧推理约400到600毫秒界面只能到2到3帧明显不够实时。摄像头1280x720分辨率下输入先缩放再推理帧率损失不大但预览画面质量保持得很好。如果你CPU部署还嫌慢可以考虑把模型导出为ONNX用onnxruntime的CPU执行提供优化速度通常比PyTorch原版推理快30%以上。我这把导出命令留给你们参考yolo export modelruns/train/exp/weights/best.pt formatonnx imgsz640 dynamicFalse导出后替换加载权重的方式用onnxruntime跑推理注意检查一下输出层的格式和NMS前置处理不然容易出漏框。7.3 部署过程中碰到的两个隐蔽问题第一个是中文路径问题。Windows下如果项目目录、视频文件或图片文件名带中文cv2.imread经常读取失败或返回None。这跟OpenCV的imread内部编码有关解决方法是先用numpy和cv2.imdecode绕一圈data np.fromfile(file_path, dtypenp.uint8) img cv2.imdecode(data, cv2.IMREAD_COLOR)第二个是置信度阈值与环境光敏感。同一套权重在室内日光灯和傍晚黄昏下误检数量能差出一倍。我最后做了一层业务侧过滤检查检测框区域的平均亮度如果低于某个阈值就降低判决权重避免把反光、玻璃上的影子当成烟支。这个规则简单但有效比重新训练一个模型成本低得多。7.4 安全与合规建议吸烟识别系统涉及对人员行为的监控部署前务必跟使用方明确告知政策边界。建议系统只处理检测结果和统计信息不长期保存原始视频流。日志里不要记录可识别身份的人脸截图如果确实需要留存建议对检测区域做马赛克处理再存储。这个建议不仅在法律层面有意义也是减少后续运维纠纷的必要手段。8. 一套跑完说点掏心窝子的经验这套项目做完最大的感悟是数据和规则设计决定了最终效果模型架构和调参只是工具层面的事情。YOLOv12提供了一个更强的骨架但如果Cigarette类只有几十张公开图任何新模型都救不回来。真正要花精力的地方在数据采集、清洗和场景规则的设计。最后分享两个实际心得。第一界面上的置信度滑块一定要保留不要写死。不同点位的摄像头光照条件不同最佳置信度阈值可能差0.1到0.2滑块调优比频繁换模型权重省事得多。第二save_dir建议自动按日期生成每天一个数据库按月归档后续统计分析会非常方便。我一开始把全部识别记录写到一个数据库文件结果一个月跑了八十万条记录查询速度肉眼可见地变慢拆分之后顺畅多了。如果你现在正准备做类似系统我建议先拿YOLOv12n跑通端到端流程再考虑换更大的s或m模型。小模型先把数据闭环跑清楚发现瓶颈是在数据还是在模型然后有针对性地优化——这套顺序永远比一上来就追求高精度更有效。
返回列表