简介LNTON羚通烟火识别算法与烟雾检测工具面向安防监控、智慧消防及视频分析方向的开发者与工程人员解决图片、RTSP实时流和mp4视频中烟火目标自动检测与告警输出的问题。资源包共874个文件约324.75MB以843张jpg样本图片为主体配合15个dll动态库、4个exe可执行程序、2个bat批处理脚本及onnxruntime、opencv等依赖组件另含weights模型权重、mp4测试视频、pdf使用文档与txt说明覆盖从样本准备、模型推理到告警图片叠框的完整链路。已有241人学习下载。读者可借助内置工具与文档快速跑通烟火识别流程理解样本生成、模型加载与结果可视化等关键环节适合需要落地烟雾检测功能或进行算法验证的技术人员参考。1. 烟火识别算法落地从一张告警叠框图说起凌晨两点某园区仓库的监控画面里出现一小簇橘红色火光。值班人员没盯着屏幕但系统在 800 毫秒内截下了这一帧画上红框写清置信度推到了告警群。第二天复盘时这张叠框图片成了最直接的证据——它比一段视频更容易归档、检索和追责。这就是烟火识别算法最朴素的价值把「可能出事」的瞬间变成一张能被人立刻看懂的图片。LNTON羚通烟火识别算法、烟雾检测工具支持图片、RTSP实时流、mp4文件中的烟火检测和烟雾识别输出告警图片叠框。这个标题里其实藏着三类输入源和一条输出链路图片是离线批处理RTSP是实时流mp4是录像回放输出统一收敛到「告警图片叠框」。适合谁做园区安防的集成商、做工业巡检的算法工程师、手里有一批摄像头但不想换硬件的运维团队。它不解决「火到底会不会烧起来」它解决的是「在火还小的时候让系统先喊一声」。2. 三种输入源怎么接图片、RTSP、mp4 的工程差异2.1 图片输入最容易被低估的批处理场景很多人以为图片检测最简单实际上它是验证算法下限的第一道关。烟火识别在图片上的难点不是模型本身而是图片的来源太杂手机拍的、监控截的、无人机航拍的分辨率从 320×240 到 4K 都有。我一般会先把输入统一到模型训练时的尺度但保留原始图用于叠框输出。import cv2 import numpy as np def preprocess_image(img_path, target_size640): 读取图片并做保持宽高比的缩放避免烟火被拉变形。 target_size: 模型输入边长常见 640 或 416。 img cv2.imread(img_path) if img is None: raise ValueError(f无法读取图片: {img_path}) h, w img.shape[:2] scale target_size / max(h, w) new_w, new_h int(w * scale), int(h * scale) resized cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_LINEAR) # 填充到正方形避免后续坐标映射出错 canvas np.full((target_size, target_size, 3), 114, dtypenp.uint8) canvas[:new_h, :new_w] resized return canvas, scale, (w, h)这段代码的关键在scale和(w, h)的返回。叠框时要把模型输出的归一化坐标乘回原始尺寸如果预处理时直接暴力 resize框会偏。参数target_size不是越大越好640 在大多数烟火场景够用再大推理时间线性上涨小烟雾反而因为下采样丢失。图片批处理还有一个坑EXIF 旋转。手机拍的图在 OpenCV 里读出来可能是躺着的检测框自然对不上。常见做法是读 EXIF 的 Orientation 标签先转正再进预处理。2.2 RTSP 实时流拉流、解码、跳帧的取舍RTSP 是烟火识别最核心的输入源也是翻车最多的地方。标题里「RTSP实时流」四个字背后是拉流协议、解码器、缓冲队列三件事。海康、大华、臻识这些摄像头的 RTSP 地址格式不同但通用结构是rtsp://user:passip:554/...。我一般先用 ffprobe 确认流能不能通再写代码。# 探测 RTSP 流的基本信息确认编码格式和分辨率 ffprobe -v error -rtsp_transport tcp \ -i rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 \ -show_entries streamcodec_name,width,height,avg_frame_rate \ -of defaultnoprint_wrappers1-rtsp_transport tcp是血泪经验。UDP 在丢包网络里会花屏解码器报错后整个管道卡死TCP 牺牲一点延迟换稳定烟火检测对 200ms 的延迟不敏感。如果摄像头是 H.265 编码OpenCV 默认后端可能解不了需要换 FFmpeg 后端或 GStreamer 管道。import cv2 def open_rtsp_stream(rtsp_url, use_tcpTrue): 打开 RTSP 流强制 TCP 传输设置缓冲区为 1 减少延迟。 if use_tcp: # 通过环境变量让 FFmpeg 走 TCP import os os.environ[OPENCV_FFMPEG_CAPTURE_OPTIONS] rtsp_transport;tcp cap cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # 只保留最新帧 if not cap.isOpened(): raise RuntimeError(f无法打开 RTSP 流: {rtsp_url}) return capCAP_PROP_BUFFERSIZE设为 1 是实时场景的后悔药。默认缓冲会积压好几帧检测结果滞后于现实告警图片里的火可能已经烧大了。跳帧策略也要想清楚25fps 的流没必要每帧都推理隔 3 帧取 1 帧算力省下来可以多接几路。但跳帧不能太狠烟雾扩散快的时候2 秒一帧可能就漏了。2.3 mp4 文件离线回放与逐帧检测mp4 输入常用于事后取证和算法调参。和 RTSP 不同mp4 可以按帧率稳定读取适合做批量验证。但 mp4 的编码格式五花八门HEVC、H.264、甚至一些监控私有封装OpenCV 读不了的时候别硬扛先用 ffmpeg 转成标准 H.264。def process_mp4(video_path, detect_fn, output_dir, stride5): 逐帧读取 mp4每 stride 帧做一次检测保存告警叠框图。 detect_fn: 接收帧返回 [(x1,y1,x2,y2,score,label), ...] cap cv2.VideoCapture(video_path) frame_idx 0 saved 0 while True: ret, frame cap.read() if not ret: break if frame_idx % stride 0: detections detect_fn(frame) if detections: vis draw_boxes(frame, detections) out_path f{output_dir}/alert_{frame_idx:06d}.jpg cv2.imwrite(out_path, vis) saved 1 frame_idx 1 cap.release() return savedstride参数决定检测密度。调参阶段可以设 1每帧都看实际回放取证设 5 到 10够用且快。输出文件名带帧号方便回溯到视频时间点。注意 mp4 读取完要release()否则文件句柄泄漏批量处理几百个文件后会报「too many open files」。3. 烟火检测模型选型与告警叠框实现3.1 为什么烟火检测不能直接套用通用目标检测烟火识别和行人、车辆检测有本质区别。火焰和烟雾是「非刚性、半透明、边界模糊」的目标通用检测模型在 COCO 上训出来的先验放到烟火上会水土不服。常见做法是拿 YOLO 系列做 backbone但在烟火数据集上重新训。选型时看三个指标小目标召回、误报率、推理速度。小目标召回决定能不能在火苗阶段发现。烟雾在画面里往往只占几十个像素模型下采样 32 倍后特征几乎消失。我一般会在 neck 部分保留一个高分辨率分支或者用 1280 的输入尺寸换召回。误报率决定这套系统能不能活过第一周——灯光反射、夕阳、红色工服都会触发误报。训练时负样本要专门收集这些「像火但不是火」的图。推理速度决定单机带几路。YOLOv8n 在 CPU 上大概 30ms 一帧GPU 上 5ms 以内。如果只有 CPU建议 RTSP 流降到 5fps 检测或者用 OpenVINO 量化。3.2 告警图片叠框坐标映射与样式设计叠框不是画个矩形就完事。模型输出的是预处理后的归一化坐标要映射回原始帧框的颜色、线宽、标签内容都影响可读性。def draw_boxes(frame, detections, color(0, 0, 255), thickness2): detections: [(x1,y1,x2,y2,score,label), ...] 坐标为原始帧像素 返回画好框的帧。 for x1, y1, x2, y2, score, label in detections: x1, y1, x2, y2 map(int, [x1, y1, x2, y2]) cv2.rectangle(frame, (x1, y1), (x2, y2), color, thickness) text f{label} {score:.2f} # 文字背景条避免浅色背景上看不清 (tw, th), _ cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1) cv2.rectangle(frame, (x1, y1 - th - 6), (x1 tw, y1), color, -1) cv2.putText(frame, text, (x1, y1 - 4), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1) return frame坐标映射的坑在于预处理时做了 letterbox 填充映射回去要先减去填充偏移再除以 scale。如果这一步错了框会整体偏移或缩放。我一般会写一个scale_coords函数专门处理并在单元测试里用一张已知图验证。告警图片的命名和存储也要设计。按日期/摄像头ID/时间戳.jpg分层避免单目录文件过多。同时写一条 JSON 元数据记录检测类别、置信度、原始帧号方便后续检索。3.3 从检测到告警阈值、去重与推送检测出烟火不等于要告警。连续 3 帧都检出同一区域才认为是一次有效告警这叫「时序确认」。单帧误报被过滤掉代价是告警延迟增加 100ms 左右可以接受。去重逻辑用 IOU 做新框和已有告警框 IOU 大于 0.5 就合并更新置信度不重复推送。推送内容就是叠框图片加一句文字描述。如果对接的是企业微信或钉钉机器人图片先存本地或对象存储推送时带 URL。阈值设置上火焰置信度可以设 0.5烟雾设 0.4。烟雾本身模糊阈值太高会漏。但烟雾误报也多所以烟雾告警建议加时序确认帧数到 5 帧。4. 避坑与排查RTSP 流、编码、叠框的 5 个常见问题4.1 现象RTSP 流打开成功但读帧一直返回 False原因摄像头子码流地址写错或者鉴权信息里有特殊字符没转义。有些摄像头主码流是 H.265OpenCV 默认后端不支持。解决先用 ffprobe 确认流可读再检查 URL 里的密码是否含、#等字符需要 URL 编码。H.265 流换cv2.CAP_GSTREAMER后端或者让摄像头切到 H.264 子码流。4.2 现象检测框在叠框图片里整体偏移原因预处理做了 letterbox 填充后处理映射时没减去填充偏移或者 scale 用错。解决把预处理返回的 scale 和 padding 值一路传到后处理写一个独立的坐标映射函数用一张已知尺寸的测试图验证。别在多个地方重复算 scale。4.3 现象mp4 文件读了几帧就卡住原因mp4 的 moov atom 在文件末尾OpenCV 需要先扫描整个文件。大文件或网络存储上的文件会卡很久。解决先用 ffmpeg 做-movflags faststart重排把 moov 移到文件头。或者用 PyAV 替代 OpenCV 读 mp4它对异常封装兼容更好。4.4 现象烟雾检测误报频繁灯光和云都触发原因训练集负样本不足模型没见过「像烟的背景」。解决收集误报图片加入负样本重新训。推理阶段加一个简单的颜色和纹理过滤烟雾区域饱和度低、高频分量少灯光区域亮度高且边缘锐利。用这两个特征做二次筛选能砍掉大部分误报。4.5 现象多路 RTSP 同时跑程序越来越慢最后崩溃原因每路流开一个线程解码和推理没做限流内存和句柄泄漏。解决用固定大小的线程池推理做批处理或队列。每路流读完帧后显式释放cap.release()放在 finally 里。监控进程的句柄数和内存超过阈值重启工作进程。5. 进阶技巧用跳帧和 ROI 把单机路数翻倍烟火识别落地到一定规模瓶颈从来不是模型精度而是算力。我试过一个笨办法把所有摄像头都按 25fps 全帧检测结果一台 8 核 CPU 服务器只能带 4 路。后来改成跳帧加 ROI同样硬件带到 12 路误报还没涨。跳帧的策略要按场景分。仓库、车间这种烟火扩散相对慢的场景2fps 检测足够化学品堆场、加油站这种高风险区保持 5fps 以上。实现上不是简单丢帧而是用cap.grab()快速跳过只在需要检测时cap.retrieve()解码省掉大量解码开销。def stream_with_skip(cap, detect_interval5): grab 跳帧retrieve 只解码需要检测的帧。 detect_interval: 每多少帧检测一次。 frame_idx 0 while True: ret cap.grab() if not ret: break if frame_idx % detect_interval 0: ret, frame cap.retrieve() if ret: yield frame_idx, frame frame_idx 1ROI 是另一个杠杆。摄像头画面里真正需要检测的区域往往只占一半天空、墙面、绿化带都是干扰。我一般让用户在配置里画多边形 ROI检测前先做掩膜ROI 外的区域直接置灰。这样模型输入的有效像素更集中小烟雾的相对尺寸变大召回也会好一点。验证这套优化有没有副作用不能只看速度。我会准备一段标注好的 mp4里面包含 10 次烟火事件和 20 次干扰事件跑优化前后的配置对比召回率和误报数。如果召回掉超过 5%跳帧间隔就得往回缩。最后说个习惯每次调完参数把配置、测试视频、检测结果截图存一个文件夹命名带日期。烟火识别这行玄学时刻不少同一个模型换个摄像头就翻车。留着这些记录下次遇到类似问题翻出来比重新试快得多。希望帮到你。本文还有配套的精品资源点击获取
