资讯动态

图像烟雾检测实战:从传统算法到YOLO部署的完整指南

发布时间:2026/9/1 19:05:37 来源:尧图企业网站定制
简介基于MATLAB实现的烟雾检测代码包面向图像处理入门者及安全监控、火灾预警等场景开发人员帮助快速掌握从图像预处理、特征提取到区域分割与结果评估的完整算法流程。压缩包内仅含一个源程序文件体积仅约1KB结构紧凑便于直接运行与修改调试。已有3245人学习参考验证了其实用性代码可读性强适合快速上手与二次开发。文件围绕烟雾的颜色分布、边缘突变、纹理模式等关键特征设计检测逻辑并涉及颜色空间转换、边缘检测算子、纹理特征分析、分类器判别等技术要点适合结合典型烟雾图像开展实验与算法对比。通过阅读和运行该程序读者可直观理解烟雾检测的主流思路并以此为基础扩展实时视频流检测功能进一步搭建完整的预警系统。 第一次接触图像中的烟雾检测时我以为这只是一个“看图识别有没有烟”的小任务真正动手做才发现它比火焰检测难出好几个量级。火焰有相对稳定的形状和颜色烟雾却是半透明、无固定轮廓、会随气流四处扩散的东西同一段烟在不同光照下拍出来可能完全是两种模样。这篇博文是我在实际项目里做烟雾检测的完整记录从传统图像算法到深度学习方案再到部署时的误报排查都会讲到。不管你是刚入门想做安防监控还是已经在用OpenCV做图像预处理、想引入目标检测提升效果这篇文章应该都能给你一些可落地的参考。1. 烟雾检测为什么这么难一个看上去简单但实际情况复杂的任务1.1 烟雾和火焰的本质区别很多人会把“烟火检测”当成一个任务实际上两者难度完全不同。火焰因为温度高、颜色集中在橙红色区域在图像里有清晰的边缘用深度学习检测时标注一个火焰框非常自然——它就是一个“物体”。但烟雾完全是另一回事。烟雾是细颗粒悬浮物它在图像里是半透明的边缘不是一条线而是一段渐变的过渡区域。一个室内小火源产生的烟几秒内就能充满半个房间你要怎么把“一房间的烟”框进一个矩形框里边界在哪里浓度稀薄到多少算烟这些问题在标注阶段就会让人抓狂。我之前做过一个对比实验在同样的视频流里检测火焰和烟雾火焰模型的 mAP 能到 0.85 以上烟雾模型辛苦调到 0.72还伴随着大量误报。这不是模型能力的问题而是目标任务本身的定义就模糊得多。1.2 烟雾检测难在哪儿形状不固定、颜色不统一、背景干扰多具体拆解下来烟雾检测的难点集中在四个维度形状动态变化烟雾受气流影响每秒都在改变形状。上一帧是一个竖直烟柱下一帧可能被吹成一片薄雾。这意味着模型很难学到稳定的形状先验。颜色跨度极大白烟、灰烟、黑烟、黄烟某些化工场景都算烟雾。白色烟雾在亮色背景下对比度很低黑色烟雾在暗色环境中几乎和背景融为一体。颜色这个问题几乎无法用固定阈值解决。背景干扰因素密集雾、蒸汽、灰尘、炊烟、汽车尾气、工厂正常排气在图像上和真正的火灾烟雾高度相似。强日光下的水汽蒸腾也会被视觉算法判定为“白色飘动物”。光照条件不稳定白天强光、傍晚逆光、夜间红外补光烟雾在不同光照下的灰度直方图差异巨大。同一个场景白天有效的参数到了晚上完全失效。再加上安防场景几乎都要求实时性10秒才出一帧结果的算法没有工程价值这进一步压缩了模型选择的空间。所以烟雾检测首先是一个“定义模糊 场景复杂 实时约束”三重压力下的问题想要一个通用方案基本不现实只有在具体场景里做针对性优化才有落地可能。2. 传统图像算法路线从运动检测到特征建模的取舍在深度学习普及之前大家做烟雾检测主要靠传统图像算法组合核心思路是先找出图像中“变化”的区域再对变化区域做特征筛选看像不像烟。这套思路现在依然有参考价值尤其在算力受限、只有CPU可用的场景。2.1 运动检测基础帧差法与背景建模运动检测是传统方案的第一步目的是把“动的区域”从静态背景里抠出来。最基础的是帧差法直接比较相邻两帧对应像素的灰度差import cv2 cap cv2.VideoCapture(smoke.mp4) ret, prev cap.read() prev_gray cv2.cvtColor(prev, cv2.COLOR_BGR2GRAY) while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) diff cv2.absdiff(prev_gray, gray) _, thresh cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY) cv2.imshow(diff, thresh) prev_gray gray if cv2.waitKey(1) 0xFF ord(q): break帧差法实现简单但有两个致命问题一是烟雾运动速度慢相邻两帧之间的像素变化不大很容易漏检二是坏境里任何运动树叶摇动、行人走过、云影移动都会触发大量前景区域。更实用的是背景建模OpenCV 自带的 MOG2 或 KNN 都能做。MOG2 会对每个像素维护一个混合高斯分布适应缓慢的光照变化提取出来的运动前景比帧差法稳定不少。我在早期测试中用 MOG2 提取前景后再做形态学开闭运算去除噪点效果能比帧差法好一倍。但它的弱点也很明显如果摄像头本身有抖动或者场景里有反复运动的周期性物体比如旋转的风扇背景模型会不断被污染最后输出一堆噪声。2.2 颜色和纹理特征如何把“疑似烟雾区域”筛出来拿到运动前景后并不是所有运动的区域都是烟接下来要用颜色和纹理特征做二次筛选。烟雾在图像中最直观的特征是会让背景变得“灰蒙蒙的”而且这个区域内部的纹理往往比较平滑细节被模糊掉。我早期常用的筛选规则大致有这几条颜色空间约束把前景区域转到 HSV 或 YCbCr 空间烟雾区域的饱和度通常偏低亮度处于中间段不是极亮也不是极暗。比如在 YCbCr 空间里烟雾区域往往 $Cb$ 和 $Cr$ 的值非常接近说明它“不偏红也不偏蓝”。暗通道先验这个思路来自图像去雾算法。烟雾和雾类似会让局部区域的暗通道亮度明显高于正常背景。计算每个小块的暗通道值如果偏高就认为有烟雾覆盖的可能。纹理平滑度用拉普拉斯算子的方差来衡量区域清晰度。正常景物边缘多、梯度大拉普拉斯方差高烟雾区域因为散射作用边缘被弱化拉普拉斯方差明显偏低。这个特征在很多场景下比颜色特征更稳定。小波域高频能量烟雾区域的高频分量少小波变换后的高频子带能量低。这个特征计算量稍大但抗光照变化能力更强。把运动前景、颜色约束、纹理特征组合起来可以组成一个多级判断管道。核心逻辑是先找到运动区域再对每个连通域提取特征设定阈值超过阈值就判为烟雾。2.3 传统方法的局限为什么我在复杂场景下最终放弃了纯手工特征这套传统管道在固定摄像头、背景简单、光照稳定的小场景里确实能跑我甚至用它在烟尘试验场里取得了不错的效果。但一旦换到真实监控环境问题就暴露了。首先是阈值不可迁移。我在 A 场景调的饱和度阈值和暗通道阈值拿到 B 场景完全不适用——B 场景背景有大量白色外墙整个画面的暗通道天然偏高误报率直接起飞。其次是运动检测在拥挤场景里基本失效人一多、车一多前景区域连成一片特征筛选无从谈起。最让我崩溃的是周末的树叶场景一棵树的影子随风晃动在运动检测里看起来就是一大片闪烁区域颜色不偏、饱和度也低、纹理也平滑最终被判成烟雾一下午报了 40 多次警。调试到后面我感觉自己不是在“写规则”而是在给每一个背景植物写定制补丁。那一刻我就明白传统特征工程在烟雾检测这种高度依赖语义的任务上天花板太低了。于是我把重心转向了深度学习。3. 深度学习方案基于YOLO的烟雾检测落地换成深度学习的直接原因是模型可以把“什么是烟”的语义判断交给大量数据来学习而不是靠我人去总结规则。在实测对比中一个训练得当的 YOLO 模型在复杂背景下的准确率轻松超过了我用两周时间调出来的传统管道。3.1 为什么选择目标检测而不是分类或分割同样是深度学习选哪种任务形式也值得想清楚。我当时的备选有三个图像分类、目标检测、语义分割。图像分类只能回答“画面里有没有烟”但如果一个画面里有三个烟源分类模型既不能给出位置也没法区分是几个烟源在冒烟。对安防联动来说只知道“有烟”却不知道“哪里在冒烟”价值很有限。语义分割虽然能给出像素级区域但烟雾本身边界模糊标注员连边界都难以画准标注一致性会非常差。我自己尝试验证过同一段烟雾两个标注员画出的边界 IoU 只有 0.6 左右这样的标签会严重干扰分割模型的训练。目标检测的矩形框标注最简单也最能容忍烟雾边界的不确定性——框住“烟源附近的核心浓烟区”即可不需要精确到像素。同时检测框自带位置信息可以联动摄像头云台还能输出多目标数量。所以最终选了目标检测路线。实际项目中我同时检测火焰和烟雾两个类别用同一个检测模型输出两类框效果和后续告警联动都很方便。3.2 数据集整理难点与Labeling经验模型再好没有数据也是白搭。烟雾检测的数据集是我投入时间最多、也最容易被低估的部分。公开数据集方面现有的火灾类数据集大多以火焰为主烟雾样本占比少且场景单一。我收集了几个开源数据集后发现以下问题白天户外场景占绝大多数夜间红外帧少工厂室内场景几乎为零工业烟囱排放与火灾烟雾没有区分标签。所以光靠公开数据远远不够必须自己补充。补充数据最实用的来源有三个一是从监控视频里截帧找一个安全的可控测试点用烟雾弹或烟饼制造烟雾进行拍摄二是从公开的视频网站抓取森林火灾、工厂烟雾等真实片段再按帧抽取三是用图像增强手段扩展现有样本。数据集整理阶段最大的坑是标注规范。烟雾透明区域到底算不算目标我的处理原则是只标注肉眼能明显识别的浓烟区域淡到几乎看不见的不标。这样做的好处是标注一致性好模型学到的是“明确烟雾”的视觉特征而不是在透明区域上反复摇摆。另一个标注问题是不完整目标——有些烟雾从画面边缘蔓延进来只有一个部分的框这类“截断目标”我选择保留因为真实检测中同样会遇到边缘起烟的情况。数据增强上除了常规的翻转、缩放、颜色抖动我强烈建议加两种专门针对烟雾的增强一种是模拟不同光照的亮度扰动让模型适应白天和夜间差异另一种是随机在背景区域粘贴烟雾图片块类似CutMix增加“小目标烟雾”的样本数量。我做了几十轮实验发现对小烟雾目标来说这种粘贴增强比单纯调亮度更有用。3.3 模型选型与推理优化YOLOv5 vs YOLOv8 vs 定制轻量模型模型选型上我依次试过YOLOv5s、YOLOv8n、YOLOv8s以及更轻量的NanoDet下面是实测对比同数据集、同输入尺寸640x640模型参数量输入尺寸推理耗时Jetson NanomAP50备注YOLOv5s7.2M64085ms0.74稳定部署资料多YOLOv8n3.2M64055ms0.71轻量适合边缘设备YOLOv8s11.1M640120ms0.78精度最高实时性吃紧NanoDet-Plus4.1M64048ms0.69最快但小目标偏弱综合评判之后我选择了YOLOv8n作为主力模型精度只比v5s低3个点但速度几乎快一倍而且在烟雾这种“目标虽大但纹理弱”的场景里参数量差异带来的精度差距并不大。真正让精度提升的不是模型结构而是数据质量。部署优化这块如果只跑推理不管效率模型很难投入实际使用。我在NVIDIA边缘设备上会做 TensorRT 加速用 FP16 精度YOLOv8n 推理耗时能从55ms降到25ms左右满足单路摄像头实时处理。如果设备更弱只能CPU跑可以尝试 OpenVINO 转换配合 INT8 量化效果也很可观。有一点要注意INT8 量化对烟雾这种低纹理目标敏感量化后 mAP 可能掉 5 个点以上需要实测评估不能为了省算力盲目量化。4. 部署时最容易被忽视的问题误报率、阈值和场景泛化很多人在实验室里模型 mAP 挺高一部署到现场就翻车问题几乎都出在误报率上。烟雾检测的误报和一般目标检测误报不一样消防物联网场景里用户对“假警报”的容忍度极低——误报报多了后面真火警来也没人信了。4.1 误报处理云雾、蒸汽、灰尘和烟的区别我在项目里遇到最多的误报不是随机噪声而是三类“看起来很像烟”的东西云和雾整体缓慢漂移、半透明、颜色灰白尤其阴天低空云层几乎就是“放大的烟雾”。但云雾的运动尺度极大覆盖画面往往超过三分之一而且短时间内整体位移很小不像火灾烟雾从局部源点扩散。蒸汽厨房、工厂锅炉房里的蒸汽上升速度快也半透明非常难区分。我发现一个相对有用的特征蒸汽在与空气接触后迅速消散纹理变化快烟雾则相对稳定从上往下呈现出一个相对持续的扩散过程。灰尘/扬尘工地扬尘、车辆驶过扬起的尘土在逆光下看起来和黄色烟雾几乎一样。但扬尘通常伴随明显的运动轨迹车辆经过且会很快落定。针对这些误报纯单帧图像很难区分必须引入时序信息。我用了一种简单的运动一致性后处理连续10帧中检测框需要至少8帧都出现且中心点位移不超过某个阈值根据图像尺寸比例设定才触发一次有效告警。这个策略简单粗暴但能把云、蒸汽、瞬时光线变化引起的误检压掉一大半。真正的火灾烟雾是持续扩散的不会只闪一两帧。4.2 动态阈值与置信度策略训练好的模型会输出一个置信度分数部署时通常取一个固定阈值比如0.5。但这个固定阈值在真实场景里不好用。我遇到的实际问题是白天光线充足时模型对烟雾的置信度普遍较高0.5 阈值很好用但到了夜间红外模式下同一片烟雾置信度会掉到0.3~0.4如果坚持0.5阈值就会直接漏报。如果反过来把阈值降到0.3白天误报又会增加很多。解决方案是分时段设置阈值。在系统中读取当前时间白天用0.45夜晚用0.3并且对夜间低置信度的告警强制进入“延时确认”流程不在第一次检测到时立刻报警而是连续确认5帧后再发出。这样既保住了夜间召回又控制住了误报。另外一个实用技巧是做区域级ROI设定。比如客户只关心车间东侧区域是否有烟就可以通过配置界面把检测区域画出来模型只对ROI内的检测框做统计。这个做法不是提升模型精度而是从工程上把无关区域的误报直接排除属于投入最小、收益最大的一项配置。4.3 实际测试评估我如何用混淆矩阵做评测学术上习惯用 mAP 评价模型但工程部署我更关心的是混淆矩阵实际跑了一天监控视频模型报了多少次真的烟雾、多少次误报、多少次漏报。我在评测时会准备三段不同场景的连续视频一段正常工厂作业含蒸汽和叉车一段晚间低照度场景一段真实的烟雾火灾测试视频。跑完推理后把模型输出的每个检测框按时间线保存为截图然后人工逐张标注是 TP真阳性、FP假阳性还是 FN假阴性。最终统计误警率 FP / (TP FP)这个指标在安防场景里比 mAP 更重要它直接决定了客户会不会关掉报警功能。漏报率 FN / (TP FN)漏报意味着真火灾没发现这是绝对底线。实际评估中我发现很多模型漏报的不是“完全看不见的烟”而是“烟还很淡、面积很小”的早期烟雾。针对这一点后续优化重点应该放在采集更多早期浓烟小块的样本上。多帧确认之后误警率可以明显下降因为大部分误报目标不会连续出现在8帧以上。真正导致误警率居高不下的往往是场景里有一个持续存在的“假烟雾源”比如固定排气口长时间喷出的蒸汽。这种问题靠模型和后处理都很难完全解决我的做法是允许用户为每个摄像头单独配置“忽略区域”把这种固定误报源直接框掉。5. 我的实际项目总结与经验建议5.1 一个完整的烟雾检测系统架构参考到目前为止一套可以稳定运行的烟雾检测系统大概长这样图像采集层RTSP/RTMP 拉取摄像头视频流或者用 SDK 直连网络相机。多路摄像头场景建议每路一个独立线程做拉流和推帧避免一路卡顿拖垮全部。预处理层统一缩放输入到640x640做归一化低照度场景酌情做亮度和对比度增强。注意预处理一定要和训练时保持一致否则模型效果会打折扣。推理层ONNX Runtime、TensorRT 或 OpenVINO 加载检测模型输出检测框和置信度。后处理层NMSROI过滤时序确认连续N帧有效才判定分时段阈值管理。告警联动层判定为烟雾后触发告警保存当前帧截图记录时间戳、置信度、摄像头编号推送通知到监管平台。在部署时我把模型推理封装成独立服务对外暴露一个简单接口输入一帧图像返回是否存在烟雾、检测框坐标、置信度。这样做的好处是更换模型或升级算法时上层告警逻辑完全不用动。系统上线后我还在持续记录误报警例每两周挑一批典型误报补充进训练集做微调模型的现场表现会越来越好。5.2 给新手的建议与踩坑提示如果你也准备做图像中的烟雾检测根据我踩过的坑有几条建议可以提前给到你先跑通再优化不要一上来就追求完美。第一版直接用 YOLOv8n 公开数据集 默认超参数先跑通整个链路看看漏报误报长什么样再针对性地提升。标注质量比模型结构重要。烟雾检测模型最怕的是标签不一致。同样的半透明烟有人标了有人没标模型学到的特征就会飘。不要追求样本数量级很大5000张高质量标注图的效果可能比20000张草率标注的图更好。不要用测试集调参。训练阶段用验证集挑模型最后才用没见过的测试视频做最终评估否则你在测试集上看到的“高分”全是假象。我在项目中期犯过这个错误调出来的模型在自己的测试片上几乎满分一换新场景立刻露馅。生产环境的误报会摧毁用户信任。宁可让系统延迟10秒报警也要把瞬时误报压住。用户宁可看到晚一点的准确告警也不希望一天收10条假警报。最后分享一个小技巧验证模型效果不必非等实时视频流先拿历史监控视频离线抽帧跑一遍推理观察检测框稳定性和置信度分布就能提前发现大部分问题。等离线测试结果满意了再上实时链路能省下大量现场调试时间。烟雾检测这个方向真正的难点从来不是跑通模型而是在复杂现实场景里让系统可靠、稳定、不扰民。希望这篇文章能帮你少走一些弯路把你的检测系统稳稳地推进到生产环境里。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价