news 2026/9/1 19:05:25

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
图像烟雾检测实战:从传统算法到YOLO部署的完整指南

简介:基于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 Nano)mAP50备注
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 加载检测模型,输出检测框和置信度。
  • 后处理层:NMS;ROI过滤;时序确认(连续N帧有效才判定);分时段阈值管理。
  • 告警联动层:判定为烟雾后触发告警,保存当前帧截图,记录时间戳、置信度、摄像头编号,推送通知到监管平台。

在部署时,我把模型推理封装成独立服务,对外暴露一个简单接口:输入一帧图像,返回是否存在烟雾、检测框坐标、置信度。这样做的好处是更换模型或升级算法时,上层告警逻辑完全不用动。系统上线后,我还在持续记录误报警例,每两周挑一批典型误报补充进训练集做微调,模型的现场表现会越来越好。

5.2 给新手的建议与踩坑提示

如果你也准备做图像中的烟雾检测,根据我踩过的坑,有几条建议可以提前给到你:

  • 先跑通再优化,不要一上来就追求完美。第一版直接用 YOLOv8n + 公开数据集 + 默认超参数,先跑通整个链路,看看漏报误报长什么样,再针对性地提升。
  • 标注质量比模型结构重要。烟雾检测模型最怕的是标签不一致。同样的半透明烟,有人标了有人没标,模型学到的特征就会飘。不要追求样本数量级很大,5000张高质量标注图的效果,可能比20000张草率标注的图更好。
  • 不要用测试集调参。训练阶段用验证集挑模型,最后才用没见过的测试视频做最终评估,否则你在测试集上看到的“高分”全是假象。我在项目中期犯过这个错误,调出来的模型在自己的测试片上几乎满分,一换新场景立刻露馅。
  • 生产环境的误报会摧毁用户信任。宁可让系统延迟10秒报警,也要把瞬时误报压住。用户宁可看到晚一点的准确告警,也不希望一天收10条假警报。
  • 最后分享一个小技巧:验证模型效果不必非等实时视频流,先拿历史监控视频离线抽帧跑一遍推理,观察检测框稳定性和置信度分布,就能提前发现大部分问题。等离线测试结果满意了,再上实时链路,能省下大量现场调试时间。

烟雾检测这个方向,真正的难点从来不是跑通模型,而是在复杂现实场景里让系统可靠、稳定、不扰民。希望这篇文章能帮你少走一些弯路,把你的检测系统稳稳地推进到生产环境里。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 19:05:04

激活老系统:从基础设施到架构治理的落地路径

在真实的工程开发里,“接手一个没人维护的老系统”和“搭建一个能支撑业务增长的新系统”是两种常见的起点,但大多数团队遇到的是前者:系统还能跑,文档不完整,结构混乱,离开核心开发就没人敢改。这种时候&a…

作者头像 李华
网站建设 2026/9/1 19:04:55

基于STM32F103的智能晾衣架控制系统设计与实现

相信不少做过嵌入式课设或实际小项目的朋友都有过这种体会:功能需求其实不复杂,但把传感器、电机、显示、控制逻辑全部整合到一块 STM32 上时,硬件接线、驱动移植、状态切换、调试排错很容易把人绕晕。尤其是“自动收衣服”这种带实时判断的场…

作者头像 李华
网站建设 2026/9/1 19:03:16

Matlab实现3D直流电法正演:有限单元法全流程解析

简介:本资源是一套面向地球物理勘探初学者与科研人员的Matlab三维有限元直流电阻率正演模拟实践包,聚焦电阻率法正演建模核心能力训练,适用于水文地质调查、矿产勘查及工程地质建模等实际场景。压缩包含99个文件(1001KB&#xff0…

作者头像 李华
网站建设 2026/9/1 19:00:02

STM32F407嵌入式AI实战:手写数字识别从PyTorch到C代码部署

简介:本资源是面向嵌入式初学者与STM32进阶开发者的手写数字识别实战项目,聚焦于在资源受限的STM32F407系列MCU上部署轻量级图像识别功能,适用于智能终端、人机交互界面及IoT边缘识别等场景。压缩包共635个文件,含279个C源码&…

作者头像 李华
网站建设 2026/9/1 18:59:33

Java+SpringBoot+Vue+MySQL美妆购物网站毕设全流程解析

简介:这是一套面向计算机专业本科生的高分毕业设计级美妆购物网站实战项目,适用于毕设开题、课程设计与期末大作业,解决电商类系统从需求分析到部署落地的全流程实践难题。资源包共839个文件,涵盖133个Java后端逻辑文件、76个Vue前…

作者头像 李华