简介:面向视频监控烟火检测场景的LNTON羚通烟火识别算法工具,支持对图片、RTSP实时流及mp4视频文件进行火焰与烟雾检测,识别到目标后自动输出叠框告警图片,适合安防、消防等领域的算法验证与快速部署。压缩包共874个文件,约324.75MB,其中包含843张jpg样例图片、15个dll动态库、4个exe可执行程序、2个bat批处理脚本及2个mp4测试视频,另有weights模型权重、pdf使用文档等,资源类型清晰,便于按需调用。目前已有241人学习使用,工具实测可用,随包附有详细使用文档,可帮助用户快速掌握调用流程、参数配置及告警结果输出方式。整体结构兼顾模型、演示素材与运行依赖,是一套开箱即用的烟火识别实用工具。
1. LNTON羚通烟火识别算法:从一张告警叠框图讲起的实用工具
很多人第一次接触烟火识别,以为难点全在“算法能不能认出来”。放到现场走一圈才发现,真正让烟火检测工具翻车的,往往是输入源——RTSP流十分钟断一次、mp4解出来的画面是花的、上午十点的阳光被当成烟雾反复告警。LNTON羚通烟火识别算法就是这类烟火检测工具的典型代表:同时支持图片、RTSP实时流和mp4文件三种输入,对火焰和烟雾输出检测框,并把告警帧存成叠框图片。它适合园区安防、森林防火、工地监控的集成商和运维工程师,也适合需要在现有摄像头网络里快速加一路烟火识别能力的开发者。这篇文章按“选型—跑通—接流—避坑—调优”的顺序,把这三条输入路径一次讲透。
2. 烟火检测的选型逻辑:为什么说输入源决定算法成败
2.1 烟火检测为什么选检测网络,而不选分类网络
烟火识别算法在落地时,最容易被低估的一点是:烟雾没有固定边缘,火焰没有固定颜色。一团烟在顺光、逆光、夜间红外补光下完全是两种纹理,火焰从橙红到蓝白都有,单独用颜色阈值或者图像分类做,出门就会被现实教育。
所以市面上的烟火检测工具,基本都走目标检测路线。分类网络只能给整张图打一个“有烟”或“没烟”的标签,但报警联动时需要知道火在画面哪个位置、烟雾扩散到了哪个区域,还要在原始帧上叠框输出给值班人员看。检测网络天然输出边界框坐标,直接对应叠框需求。
具体到模型选型,YOLO 系是常见选择,原因有三个。第一,单阶段检测在 CPU 和边缘设备上都能跑出现实可用的帧率;第二,烟火这类目标不像人车那样有丰富公开数据集,训练样本往往要现场自采,YOLO 系的迁移学习成本低;第三,部署生态成熟,OpenCV、ONNX Runtime、各家的 NPU 工具链都有现成转换路径。
实践里我一般会盯两个点。输入分辨率不低于 640,最好上 1280,烟火目标在监控画面里通常占比很小,分辨率不够时小火苗直接被下采样丢掉。另一个是模型的类别数,不要把“火焰”“烟雾”合并成一类,两类分开训练、分开调阈值,因为烟雾的置信度天生比火焰低一截,合并后要么火焰漏报,要么烟雾全是误报。
2.2 输入源对比:图片适合验算法,mp4 适合复核,RTSP 负责实时
同一个烟火识别算法,喂图片、mp4、RTSP,跑起来完全是三套逻辑。图片是单帧静态输入,没有时序信息,只能依赖单帧的空间特征做判断,适合验证算法“能不能认出烟火”;mp4 是离线视频,可以抽帧、跳帧、反复回放,适合做批量复核和事后取证;RTSP 是实时流,对延迟、断线重连、帧率控制都有额外要求。
| 输入源 | 典型场景 | 帧率/处理方式 | 最常踩的坑 |
|---|---|---|---|
| 图片 | 抓拍机上传、单帧验证 | 一次性推理 | 通道顺序、分辨率过低 |
| mp4 | 历史录像复核、离线批量检测 | 抽帧后逐帧推理 | h265 解码花屏、时间戳对不上 |
| RTSP 实时流 | 摄像头实时监控预警 | 实时拉流+抽帧 | 断线、延迟堆积、UDP 丢包 |
理解了这三类输入的区别,就不会出现“拿图片测好了,接到实时流上全是告警”这种翻车。很多团队第一次接 RTSP,直接把图片检测脚本套上去,每一帧都推理,结果 CPU 跑满、延迟拉高、告警风暴,这不是算法问题,是输入源的处理方式没跟着换。
2.3 硬件后端与模型档位:CPU、GPU 与边缘盒子怎么选
烟火识别工具跑在什么硬件上,直接决定模型档位和推理帧率。工控机 CPU 上跑轻量级模型,单帧推理 20 到 50 毫秒是常见区间,足够了;带 GPU 的服务器可以跑更大模型,在 4K 画面上保持实时;而像 RV1106 这类边缘 IPC 方案,一般要把模型量化转成 RKNN 格式,用 NPU 推理。选型时记住一个原则:先定输入路数和帧率,再定硬件,最后定模型。一个点位一路 RTSP 流,CPU 完全能扛;八路摄像头集中检测,就该考虑 GPU 或边缘盒子分摊。
3. 用最小代码跑通烟火识别:图片、mp4 检测与告警叠框输出
3.1 图片烟火检测的最小代码骨架
不管你最后用命令行还是写服务,图片检测都是最基础的一环。下面这套骨架基于 OpenCV 加统一检测接口,LNTON 羚通这类工具包的 Python 调用逻辑也基本一致,核心是“读图—推理—画框—落盘”。
import cv2 # 以通用烟火检测模型接口为例,模型内部已经包含 # 火焰、烟雾两个类别的检测权重 model = SmokeFireDetector( model_path="weights/fire_smoke.pt", conf_thres=0.25, # 置信度阈值,烟雾建议调低到 0.2 附近 iou_thres=0.45 # NMS 的 IoU 阈值 ) img = cv2.imread("scene_20250314.jpg") # BGR 读入 img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 模型训练时用的是 RGB detections = model.detect(img_rgb) # 返回 [x1, y1, x2, y2, cls, conf] for box in detections: x1, y1, x2, y2, cls, conf = box color = (0, 0, 255) if cls == "fire" else (128, 128, 128) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) label = f"{cls} {conf:.2f}" cv2.putText(img, label, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) cv2.imwrite("alarm_scene.jpg", img) print(f"detected {len(detections)} target(s)")先说两个最容易忽视的细节。第一,OpenCV 默认读图是 BGR 顺序,而深度学习模型训练时几乎都用 RGB。不做通道转换,火焰的橙红色特征在模型眼里会错位,原本该检测出来的火苗直接漏掉。第二,画框用的坐标必须是原图坐标。如果预处理阶段把图缩放过,推理输出的框要先按缩放比例映射回原图,再传给 cv2.rectangle,否则叠框位置会偏。
置信度阈值这块,火焰和烟雾要分开设。火焰目标比较“实”,0.25 起步没问题;烟雾边缘模糊、透光性强,很多真烟帧的置信度只有 0.15 到 0.2,把全局阈值设到 0.3 以上,烟雾漏报会很严重。建议火焰 0.30、烟雾 0.20 作为第一版参数,跑完现场样本再微调。
3.2 mp4 逐帧检测:抽帧、画框与告警落盘
mp4 文件检测比图片多一层“时间维度”。一般做法是把视频按帧读出来,每隔 N 帧抽一帧做推理,命中烟火的帧立即画框保存。抽帧不是越密越好,火焰从出现到成势通常有几十秒过程,每秒抽 5 帧足够,推理负载却能降九成。
import cv2 cap = cv2.VideoCapture("camera_03_20250314_180000.mp4") fps = cap.get(cv2.CAP_PROP_FPS) frame_interval = max(1, int(fps // 5)) # 每秒抽 5 帧 frame_idx = 0 alarm_count = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break # 视频读完了或者解码出错 if frame_idx % frame_interval != 0: frame_idx += 1 continue # frame 直接是 BGR,推理接口内部会处理颜色转换 detections = model.detect(frame) if len(detections) > 0: # 用视频内时间戳命名,方便事后回看时定位 ts = cap.get(cv2.CAP_PROP_POS_MSEC) out_name = f"alarms/frame_{ts:.0f}ms.jpg" cv2.imwrite(out_name, draw_boxes(frame, detections)) alarm_count += 1 frame_idx += 1 cap.release() print(f"alarm frames: {alarm_count}")这份代码里,frame_interval 的计算是个常规操作:用视频原始帧率除以 5,得到“每 5 帧取 1 帧”的间隔,并保证最小为 1。时间戳取的是视频播放位置(毫秒),而不是系统当前时间,这样回放录像时能直接跳到对应时刻确认现场情况。
mp4 检测有一个隐藏前提:视频编码必须是 OpenCV 内置 FFmpeg 能解的格式。H.264 基本没压力,但对 H.265/HEVC 的支持要看编译版本,很多 Windows 下预编译的 OpenCV 对 10bit H.265 视频直接解出绿屏或报错。后面避坑章节会专门讲转码方案。
3.3 用时间戳与区域去重:把“每一帧告警”变成“每一起事件”
逐帧检测跑起来,马上会遇到一个实际问题:同一团烟雾持续两分钟,每帧都告警,一个事件能存几百张图。值班人员不可能挨个看,告警图片的意义就没了。
我一般会在检测之后加一层简单的去重逻辑。记录上一次告警的时间和位置,如果当前帧告警框与上一个告警框的中心距离小于画面宽度的 5%,且时间间隔小于 10 秒,就认为是同一事件,只更新“最近一次告警帧”,不保存新图。只有距离拉开或间隔超过阈值,才算新事件。
这里不要写复杂逻辑,一个字典按区域分组就够了:
last_alarm = {} # key: 区域编号, value: (last_ts, last_center) def is_dup(region, ts, center, width, interval=10): if region not in last_alarm: return False last_ts, last_center = last_alarm[region] if ts - last_ts > interval * 1000: return False dist = abs(center - last_center) return dist < width * 0.05去重之后再保存图片,一个真实烟火事件的告警图一般会控制在 3 到 5 张,既保留过程信息,又不至于把磁盘写爆。这个逻辑对图片输入不适用,但 mp4 和 RTSP 流一定要加,后面避坑章节还会从磁盘角度再提一次。
4. RTSP 实时流的烟火检测:拉流参数、断线重连与告警落盘
4.1 RTSP 拉流地址怎么找?海康、大华、萤石的常见路径格式
RTSP 是实时流传输协议,摄像头把 H.264/H.265 编码后的 RTP 包推给客户端。LNTON 羚通这类烟火识别工具接 RTSP 时,第一步是拿到正确的取流地址。不同厂商路径不一样,但结构都是rtsp://用户名:密码@IP:端口/流路径。
| 品牌 | 主码流路径示例 |
|---|---|
| 海康威视 | rtsp://admin:pass@192.168.1.64:554/Streaming/Channels/101 |
| 大华 | rtsp://admin:pass@192.168.1.65:554/cam/realmonitor?channel=1&subtype=0 |
| 萤石 | rtsp://admin:pass@192.168.1.66:554/h264/ch1/main/av_stream |
拉流时建议优先用子码流做检测。主码流一般是 4K 或 1080p 高码率,解码开销大,而烟火检测并不需要极高的细节,子码流分辨率 640×360 到 1280×720 就够用,帧率也稳定。等到报警瞬间再去回拉主码流存证据图,是常见的省资源做法。
刚才说的地址后面,还有个容易被忽略的传输参数。RTSP 底层 RTP 可以跑 UDP 或 TCP:UDP 延迟低但容易丢包,丢狠了画面全是马赛克,误报漏报跟着来;TCP 更稳但延迟略高。现场网络环境差时,我一般会把传输协议锁成 TCP,用 OpenCV 的 FFmpeg 后端加参数:
import os os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = "rtsp_transport;tcp|stimeout;5000000"stimeout 单位是微秒,设成 5000000 表示连接和读取超时 5 秒,避免摄像头掉线后程序卡死在 read 上。
4.2 拉流参数与断线重连:RTSP 实时流的稳定性配置
RTSP 和 mp4 最大的区别是:mp4 读错了可以从头再来,RTSP 流断了就没了。大部分摄像头设备在遇到网络抖动时,会自动断掉不活跃的会话,检测程序如果不做重连,第二天早上过去发现黑屏了一整夜,这种事在工地、园区项目里太常见了。
稳定拉流的代码骨架通常长这样:
import cv2 import time RTSP_URL = "rtsp://admin:pass@192.168.1.64:554/Streaming/Channels/102" cap = None while True: if cap is None: cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG) # 缓冲只留 1 帧,防止延迟堆积 cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) ret, frame = cap.read() if not ret: # 读不到帧了,先释放句柄,避免摄像头侧会话残留 cap.release() cap = None time.sleep(3) # 等 3 秒再重连,不要死循环空转 continue detections = model.detect(frame) if len(detections) > 0: ts = time.strftime("%Y%m%d_%H%M%S", time.localtime()) cv2.imwrite(f"rtsp_alarm_{ts}_{channel_id}.jpg", draw_boxes(frame, detections)) time.sleep(0.05) # 控制循环节奏,防止 CPU 空转CAP_PROP_BUFFERSIZE 设成 1 是经验值。FFmpeg 默认会缓冲多帧,推理速度跟不上拉流速度时,缓冲越堆越多,画面延迟越来越大,告警出来时火已经烧了半分钟。只保留最新一帧,相当于主动丢弃处理不过来的帧,对烟火这类慢速目标来说完全够用。
断线重连的节奏也要讲究。每 3 秒重试一次比较合理,连续失败 5 次后把间隔拉长到 10 秒,给摄像头和网络一个恢复时间。频繁重连不仅没有意义,还可能触发摄像头厂商的连接数限制,把其他客户端一起踢下线。
4.3 告警叠加通道号与时间戳:便于人工核实的落盘规范
RTSP 检测的告警叠框图,输出规范比算法本身更重要。现场值班人员看到一张图,第一反应是“这是哪个点位、什么时候发生的”,如果文件名只有一串自增数字,一个小时后想去复核现场录像都找不到对应的摄像头。
我习惯的命名规范是:点位ID_日期时间_目标类型_置信度.jpg,例如site_03_20250314_183025_fire_0.87.jpg。同时在图片左上角用 putText 叠上通道名和精确时间,这样即使图片被转发、改名,信息也不会丢。告警信息同步追加一行 JSON 或 CSV 日志,记录时间、通道、类型、置信度、目标坐标,为后面做误报分析和阈值调优留数据。
还有一个原则值得坚持:保存叠框图的同时,再存一张不带框的原图。叠框图给值班人员看效率高,原图留作证据,哪天有纠纷要追溯,原始画面才是最可信的。磁盘紧张时,原图可以压缩质量保存,但不能不存。
5. 烟火识别工具避坑指南:误报、漏报与性能的四个翻车现场
5.1 阳光把围墙照成橘色,误报率飙升
现象:晴天上午 10 点到下午 2 点,固定机位对着的砖墙或铁皮房被太阳晒成橘红色,系统持续输出“火焰告警”,一天能弹几十条。
原因:单帧检测模型主要靠颜色、纹理和形状特征。阳光下的橘色墙面、电焊火花、橙色工作服、红色卡车,在特征空间里都和火焰有重叠。火焰的形状又不像人车那么规整,模型本身对“疑似火焰”就宽容。
解决:加时序确认。单帧命中不算告警,连续 3 到 5 帧(大约 1 秒)都命中同一区域,才触发报警。同时叠加一个轻量帧间差分,计算告警区域的像素位移——火焰有抖动,墙壁是静止的,差分幅度小直接认为是静态热源干扰。再进一步,把火焰类的置信度阈值从 0.25 提高到 0.35 到 0.4,宁可晚报警 2 秒,也不要天天狼来了。
5.2 mp4 解码花屏、h265 视频检测不到,问题出在解码器
现象:同一个 mp4 文件,用播放器打开画面正常,烟火识别工具跑起来要么报错退出,要么输出的画框图全是绿块和马赛克。
原因:OpenCV 自带的 FFmpeg 对 H.265 的兼容性取决于编译选项。播放器通常内置了完整的商用解码器,而 OpenCV 在部分平台上只能用内置的软解,10bit H.265、高码率 4K 视频就解不动。这不是烟火算法的问题,是视频解码链路先崩了。
解决:先确认编码格式,再决定转码。用 FFprobe 查一眼最保险:
ffprobe -v error -show_entries stream=codec_name,pix_fmt -of default=noprint_wrappers=1 input.mp4输出显示codec_name=hevc或pix_fmt=yuv420p10le,就直接转成 H.264 8bit:
ffmpeg -i input.mp4 -c:v libx264 -pix_fmt yuv420p -c:a copy output.mp4转码会损失一点画质,但对烟火检测够用。日常处理历史录像时,我一般会写一个批处理脚本,先 ffprobe 过滤出 H.264 的文件直接检测,H.265 的先丢进转码队列,两个流程并行,省时间也省心。
5.3 RTSP 延迟越跑越高,告警比现场慢半分钟
现象:RTSP 流刚接入时延迟 1 到 2 秒,运行一个小时后告警图片对应的事件比现场慢了 30 多秒,重启程序后恢复,再跑一小时又复发。
原因:这是典型的拉流端处理不过来导致缓冲区堆积。FFmpeg 解码线程按固定速率从网络读包,推理线程如果跟不上,帧就积压在下游队列里。烟火识别一帧跑 100 毫秒,拉流端每秒进 25 帧,越积越多,延迟自然滚雪球。
解决:把 CAP_PROP_BUFFERSIZE 设成 1,只保留最新帧,主动丢帧;检测改成抽帧模式,每秒最多推理 5 帧,中间帧直接丢弃;传输协议用 TCP 减少因丢包重传引入的额外延迟。做完这三步,延迟基本能稳定在 1 到 3 秒内。还有个冷门原因:有些摄像头默认开了多路组播,客户端不去读,设备端也会持续推流,把带宽占满。检查交换机端口流量能发现这类问题。
5.4 告警叠框图把磁盘写满
现象:单点烟火检测跑了一个月,磁盘满了,系统告警中断,检查发现全是告警图片,一天几百张,一张 200KB,一个月轻松十几 GB。
原因:告警图片保存没有做数量上限和定期清理。叠框图是 JPEG,一张不大,但 7×24 小时跑下来,积累量非常可观。
解决:按天建目录,同时限制总文件数,超出后自动删除最早的目录。最粗暴但有效的办法是加一个定时清理任务:
find /data/alarms/*.jpg -mtime +7 -delete我一般会在程序启动时和每天零点各跑一次清理,保留最近 7 天告警图。有取证需求就再额外备份到对象存储或 NAS,本地不留全量。这个习惯看起来不起眼,但很多烟火检测项目交付后半年内的电话,有一半是“磁盘满了”打过来的。
6. 把烟火识别调成可用状态:置信度阈值与现场验证方法
6.1 先调两个参数:置信度阈值与 NMS IoU
烟火检测落地时,最值得花时间调的就是置信度阈值。火焰目标的置信度分布通常比较靠上,0.35 到 0.45 之间能过滤掉大部分阳光、车灯干扰;烟雾则相反,真烟的边缘模糊、透明度高,置信度常年徘徊在 0.15 到 0.25,阈值一高就漏报。
| 参数 | 火焰 | 烟雾 | 说明 |
|---|---|---|---|
| 置信度阈值 | 0.35~0.45 | 0.20~0.30 | 先按区间设,现场样本再微调 |
| NMS IoU | 0.50 | 0.45 | 烟雾框大且重叠多,IoU 太高会拆出多个框 |
NMS 的 IoU 参数很多人忽略。默认 0.5 在人车检测上没问题,但烟雾是一大片弥散区域,同一团烟可能会被输出多个互相重叠的框,IoU 阈值设太低会导致一个烟团拆成三四个告警,去重逻辑失效。我会把烟雾的 IoU 降到 0.45,让重叠框合并得更果断。
6.2 用一段现场录像回放来验收:算检出率和误报率
参数调完不能直接上线,先用现场录像回放验一遍。我的做法是:固定一路要部署的摄像头,录 2 小时白天时段和 1 小时傍晚/夜间时段,按每个 10 分钟切段,人工标记每段有没有烟火、烟火出现的起止时间,再用工具跑一遍,把算法输出和人工标记对比。
对比时只记三类:检出(算法告警且人工确认属实)、漏报(人工看到烟火但算法没报)、误报(算法告警但实际没有烟火)。统计完算一下检出率和误报率,达不到要求的回去调阈值再跑一轮。傍晚和夜间是重灾区,路灯、地灯、车灯和火焰特征高度相似,必须单独验证,不能只看白天数据。
我现在的习惯是每到一个新点位,先花半小时录一段现场视频,跑一次回放再上线。这个习惯帮我避开了无数个“白天没事、晚上狂叫”的售后电话。烟火识别是一个需要持续用现场样本养着的系统,没有一劳永逸的参数,但把验证流程固定下来,后面每次调整都有据可依。希望这些踩坑记录能帮你在部署时少走几趟弯路,也祝你的烟火告警系统上线后,安安静静只在真正需要的时候响。
本文还有配套的精品资源,点击获取