火灾检测CNN模型实战:FireNet与InceptionOnFire源码解析
发布时间:2026/10/1 4:25:19来源:尧图网络
简介面向计算机视觉开发者、消防安全领域研究人员以及需要快速部署图像火灾识别方案的工程人员这套基于Python的火灾检测卷积神经网络模型源码提供图像火焰识别的完整训练与推理示例便于在监控场景中快速分析火灾特征。压缩包共23个文件、约1.9MB以8个Python脚本模型定义、转换、验证及超像素处理、6张结果示意图、Markdown说明、Shell下载脚本、YAML工作流配置等为主覆盖数据准备、模型训练、验证到模型导出的主要流程目录结构清晰。源码包含FireNet与Inception V1/V3/V4迁移学习模型superpixel脚本可结合SLIC示例图观察预处理效果download-dataset.sh和download-models.sh便于一键获取数据集与权重pythonapp.yml提供自动化CI验证思路。已有444人学习下载适合希望复现火灾检测实验或继续改进模型的中高级Python工程师。1. 火灾检测CNN模型最容易踩的坑先看这份Python源码怎么拆火灾检测CNN模型最容易踩的坑是把网络堆得又大又深以为靠模型拟合就能解决小火苗漏检。我拆完这套基于Python的火灾检测CNN模型设计源码之后反而觉得它最有价值的地方是给出了完整的对比链路轻量的FireNet和InceptionV1/V3/V4的OnFire变体放在同一套验证脚本下数据、预处理、评估口径完全一致差多少一目了然。里面除了模型定义还带数据集下载脚本、模型转换脚本、验证脚本和SLIC超像素预处理脚本不是那种只有一个py文件的半成品。适合正在复现火灾检测实验、要把模型部署到树莓派这类边缘设备或者想拿现成Python源码快速跑通CNN识别流程的开发者。先把这23个文件按用途分清楚后面复现才不会迷路。2. FireNet与InceptionVxOnFire小模型与大骨干的取舍怎么选2.1 火焰特征决定了CNN的基本结构火灾检测里真正可用的视觉信号集中在颜色分布、边缘纹理和局部亮度梯度上。火焰的颜色让R/G通道比值在局部区域明显偏高边缘轮廓不规则而且没有固定几何形状和背景之间的对比度通常很高。CNN在火灾检测里能做对是因为卷积层用局部感受野滑过整张图第一层学到颜色块和简单边缘后续层组合出不规则纹理特征这个过程比传统颜色阈值分割稳定得多。这套源码同时放了FireNet和InceptionV1/V3/V4的OnFire变体设计意图是给不同算力场景留选择余地。FireNet是几条卷积加池化堆出来的小模型参数量小推理延迟低适合树莓派、监控盒子这类边缘设备做实时粗筛InceptionVxOnFire走的是Inception模块多尺度卷积路线1x1、3x3、5x5的卷积核并行提取不同感受野的信息对复杂背景和远处小火苗的召回能力更强但计算量也明显上去了。它俩不是“谁比谁强”的关系而是精度、速度、部署成本的取舍。模型系列特征提取策略部署倾向典型应用场景FireNet常规卷积块池化堆叠参数少边缘设备、低延迟实时视频流逐帧粗筛InceptionV1/V3/V4 OnFireInception多尺度并行卷积服务器GPU、离线批量高精度复核、论文基准对比仓库里的InceptionV1-OnFire.png、InceptionV3-OnFire.png、InceptionV4-OnFire.png和FireNet.png就是这四个模型的架构图读代码之前建议先把这几张图对照着看一遍至少把卷积层和分类头的边界在图上找出来再进源码会顺很多。2.2 从readme.txt开始读源码入口顺序和核心文件职责这套源码的阅读顺序我建议是先打开readme.txt看作者给的复现说明再看requirements.txt确认依赖然后看firenet.py和inceptionVxOnFire.py里的Input shape和类别数接着看validation脚本确认模型加载方式最后看conversion脚本和superpixel脚本。千万别一上来就翻download-dataset.sh那个脚本下载的数据集不一定匹配你本地目录结构后面对不上会非常折腾。firenet.py这种文件核心结构通常是标准的小型CNN可以概括成下面这种写法实际代码里会有具体的卷积核和尺寸配置但骨架大同小异# firenet.py 的典型结构对应仓库里的同名文件 from tensorflow.keras import layers, models def build_firenet(input_shape(64, 64, 3), num_classes2): inputs layers.Input(shapeinput_shape) # 第一层卷积提颜色块和粗略边缘 x layers.Conv2D(16, (3, 3), activationrelu, paddingsame)(inputs) x layers.MaxPooling2D((2, 2))(x) # 第二层卷积组合出不规则火焰纹理 x layers.Conv2D(32, (3, 3), activationrelu, paddingsame)(x) x layers.MaxPooling2D((2, 2))(x) # 全局平均池化替代FlattenDense大幅压参数 x layers.GlobalAveragePooling2D()(x) outputs layers.Dense(num_classes, activationsoftmax)(x) return models.Model(inputs, outputs)这里要注意几个细节。第一Input层的shape里不写batch size实际训练时由数据决定所以改输入尺寸只需要动这里。第二Conv2D的paddingsame是为了让特征图尺寸在卷积后不缩水小模型普遍这么设置。第三GlobalAveragePooling2D是嵌入式部署的关键姿势它把每个通道压成一个值再接一个Dense比Flatten再接几百个神经元的全连接层省下大量参数。如果你想换成自己的数据集只需要改num_classes和输入高宽但改完必须重新训练不能直接加载这套预训练权重否则最后会报shape不匹配。2.3 训练态与部署态分离validation和conversion为什么要拆开这组源码里最值得学的是把模型生命周期拆成了两个阶段。h5文件是训练态产物firenet-validation.py和inceptionVxOnFire-validation.py负责加载h5在测试集上算准确率、召回率、F1属于验证阶段inceptionVxOnFire-conversion.py和firenet-conversion.py负责把训练好的h5导出成SavedModel或TFLite属于部署阶段。两者拆开的原因很简单h5格式严重依赖Keras运行环境TensorFlow版本一升级旧h5可能直接加载失败这在后面避坑章会具体说而SavedModel和TFLite是标准交换格式TensorFlow Serving、TFLite Runtime、甚至不少嵌入式推理框架都能直接消费。不要拿h5文件直接上线这是个血泪教训。另外仓库里的pythonapp.yml是GitHub Actions的CI配置它只能做语法检查、依赖安装和轻量冒烟测试跑不了全量数据集。你要复现精度数字必须自己拉数据和权重CI不是干这个的。我一般会先把validation结果跑出来存档再做conversion转完再对一遍输出这样每一步都有独立产物出了问题能快速定位到底在哪个环节。3. 数据准备与SLIC超像素预处理搭一条能反复跑的流水线3.1 环境准备Python版本、虚拟环境与requirements.txt这套源码是基于Python的CNN项目代码本身不挑系统但依赖需要装对。先建虚拟环境别把TensorFlow这种重依赖直接洒到系统Python里。如果你刚入门Python这一步尤其重要虚拟环境隔离了项目依赖后面不管你装opencv还是scikit-image都不会污染其他项目。VSCode里配置Python环境时直接选.venv这个解释器路径即可终端里也要先激活再跑脚本。# 建议用Python 3.8先建虚拟环境再装依赖 python -m venv .venv source .venv/bin/activate # Windows下用 .venv\Scripts\activate pip install --upgrade pip pip install -r requirements.txtrequirements.txt里通常会锁住TensorFlow、numpy、opencv-contrib-python、scikit-image这些核心库的大版本。注意opencv-contrib-python不是普通的opencv-pythonSLIC超像素的ximgproc模块在contrib包里装错会导致后面导入报错。装完依赖后用pip list核对关键包的版本别跳到下一步才发现TensorFlow和Keras对不上。3.2 download-dataset.sh与download-models.sh先让下载脚本可复跑数据准备是整个复现流程里最容易被低估的一步。下载脚本一般需要先给执行权限再运行里面用wget或者curl拉取数据集压缩包和预训练权重。脚本本身逻辑不复杂但实际运行中有个很现实的坑如果下载源服务器离得远或网络波动大wget默认失败就退出进度条卡在0字节是很常见的。# 先给执行权限再跑数据集下载 chmod x download-dataset.sh download-models.sh ./download-dataset.sh ./download-models.sh如果脚本跑不动我一般会改成curl的断点续传写法把URL换成可用的镜像或自己内网的对象存储地址# 网络不稳定时的续传下载写法 curl -L -C - -o FireDataset.zip https://your-mirror.example.com/FireDataset.zip unzip -q FireDataset.zip -d datasets/这里的-C -是断点续传下载中断后可以重跑同一个命令接着下-L是跟随重定向-o指定保存文件名。下载完数据集和预训练权重后先解压并确认目录结构再看readme.txt里的数据目录约定很多报错都源于数据集路径和脚本里写死的路径不一致。3.3 superpixel-inceptionVxOnFire.pySLIC超像素解决小火苗漏检为什么要上SLIC超像素因为火灾检测的场景里火焰区域在整张图中往往占比很小。如果把整图直接resize到64x64或224x224远处的小火苗可能只剩几个像素卷积层根本提不出有效特征。SLIC先把图像分割成感知均匀的超像素小块每一块内部颜色和纹理高度一致然后逐块判断有没有火焰最后按块投票决定整图是否报警。仓库里的slic-stages.png和slic-ex.png就是这类分割阶段的可视化结果跑代码之前先看图能更快理解它在干什么。# superpixel-inceptionVxOnFire.py 的推理骨架以opencv ximgproc实现为例 import cv2 def slic_segments(image_path, n_segments250, compactness15): img cv2.imread(image_path) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 生成超像素分割SLICO模式不要求预设超像素个数 slic cv2.ximgproc.createSuperpixelSLIC( img_rgb, algorithmcv2.ximgproc.SLICO, region_size20, ruler15 ) slic.iterate(10) # 迭代次数越多分割越稳定但耗时越高 labels slic.getLabels() segments slic.getNumberOfSuperpixels() return img_rgb, labels, segments这里的region_size控制超像素的初始尺寸值越大超像素块越大、数量越少ruler控制分割的规整度值越大越接近规则网格iterate(10)是迭代次数一般10次左右在速度和稳定性之间比较平衡。超像素个数直接决定后续推理开销每个segment都要单独喂给模型一次n_segments拉到上千会让整个验证慢到怀疑人生。我的经验是先在全图上跑一次FireNet粗检只有命中fire的局部区域才做SLIC细筛这样重计算只发生在该发生的地方而不是每帧无脑全图分割。4. 模型转换与验证从h5到SavedModel/TFLite的完整落地路径4.1 conversion脚本的职责把训练态模型变成部署态产物inceptionVxOnFire-conversion.py和firenet-conversion.py干的是同一件事加载训练好的h5权重导出成TensorFlow Serving和边缘端能直接用的格式。常见做法是同时导出SavedModel和TFLite两个版本前者给服务器端的TensorFlow Serving后者给树莓派、Android这类移动和嵌入式场景。核心代码一般长这样# conversion脚本的常见姿势h5 - SavedModel - TFLite import tensorflow as tf model tf.keras.models.load_model(models/inceptionV1OnFire.h5) # 导出SavedModel格式目录里包含变量和推理图 model.save(saved_model/inceptionV1OnFire) # 从SavedModel继续导出TFLite converter tf.lite.TFLiteConverter.from_saved_model(saved_model/inceptionV1OnFire) converter.target_spec.supported_ops [tf.lite.OpsSet.TFLITE_BUILTINS] tflite_model converter.convert() with open(models/inceptionV1OnFire.tflite, wb) as f: f.write(tflite_model)model.save会把模型结构、权重和推理图打包成一个目录而不是单个文件这是SavedModel和h5的重要区别。TFLiteConverter的target_spec.supported_ops用来约束算子集合如果模型里有不支持的算子转换时会直接报错这时可以稍后把supported_ops扩展到SELECT_TF_OPS再试。转换产物按用途落地见下表产物格式消费方典型部署位置原始h5单文件Keras/TF训练环境训练机、实验机SavedModel目录TensorFlow Serving、TFLite转换服务器、云环境TFLite单文件TFLite Runtime、OpenCV边缘设备、嵌入式设备4.2 validation脚本的正确打开方式指标比Loss更可信跑验证前先搞清楚这个脚本在加载什么。它加载的是原始h5不是转换后的TFLite所以验证结果只代表训练态模型的精度。命令行入口一般是这种结构# 跑一次验证得到准确率、召回率、F1和误检样例 python inceptionVxOnFire-validation.py \ --model models/inceptionV1OnFire.h5 \ --data datasets/FireDataset/test验证脚本里通常会输出一整套评估指标除了准确率还会包含召回率和F1这对火灾检测特别重要。火灾检测里的正类是fire样本漏检的代价远比误报高所以只看准确率会有欺骗性比如测试集里95%是no-fire样本模型全猜no-fire也能拿到95%准确率但这个模型没有任何实用价值。验证脚本一般还会把预测错误的图片路径打印出来这部分信息一定要保留它能告诉你模型是在哪些场景下翻车的是光线暗的室内、还是浓烟遮挡的初期火情。4.3 推荐复现顺序先验证、再转换、最后对比logits复现这套源码时强烈建议按三步走不要跳过任何一步# 第一步验证原始h5精度 python firenet-validation.py --model models/firenet.h5 --data datasets/FireDataset/test # 第二步转换为SavedModel/TFLite python firenet-conversion.py # 第三步用同一批测试图对比h5和TFLite的输出 python compare_outputs.py --h5 models/firenet.h5 --tflite models/firenet.tflite第三步很多人会忽略。转换过程不是无损的尤其是TFLite的量化可能会让输出概率偏移所以必须在同一批图片上对比原始h5和导出模型的输出。常见做法是允许1e-3量级的浮点误差如果偏差超过这个范围大概率是预处理不一致或者量化丢了太多精度需要回到转换步骤检查。这个顺序的价值在于每一步都有独立产物出问题能快速分割责任边界h5验证不达标是数据或训练的问题转换后对不上是转换参数或预处理的问题。不要为了省时间把验证和转换揉在一起跑否则精度下降时你会陷入两边都像有问题的排查泥潭。5. 避坑排查火灾检测项目里最常见的五个翻车现场这套源码文件虽然不多但踩坑点集中在数据下载、版本兼容、格式转换和预处理一致性上。下面这五条是我复盘下来最需要提前知道的。5.1 下载脚本卡在0字节现象运行download-dataset.sh后压缩包大小一直是0脚本半天不结束。原因脚本写死的下载地址响应不稳定wget默认没有重试和续传逻辑连接超时后不会自动恢复。解决换成curl续传写法curl -L -C - -o FireDataset.zip 下载地址中断后重跑同一个命令会从断点继续。如果数据集很大建议先下载到本地再手动放进datasets目录别依赖脚本每次都重新拉全量。5.2 h5加载报Unknown layer或shape mismatch现象validation脚本在load_model处直接抛ValueError: Unknown layer或者加载成功后前向推理报shape不一致。原因TensorFlow版本升级后Keras的h5序列化内容发生了变化旧模型里的自定义层或旧算子在新版本里找不到对应实现。解决严格按requirements.txt安装依赖不要用自己的全局TensorFlow环境跑。如果项目没给版本就用TensorFlow 2.10之前的版本搭配对应Keras加载时如果报Unknown layer在load_model调用里加custom_objects参数把模型定义里的自定义层类和名称传进去。改这个文件之前先确认Input shape和训练时一致。5.3 OpenCV的dnn模块加载SavedModel失败现象用cv2.dnn.readNetFromTensorflow加载conversion导出的模型报错说找不到某些算子或者图形状对不上。原因OpenCV的dnn模块只支持TensorFlow算子的一个子集Keras模型导出时有些算子没有映射到OpenCV能识别的格式尤其是带自定义层的模型几乎必翻车。解决换路径。最省事的是直接用TensorFlow Serving的REST接口做推理或者把SavedModel用tf2onnx转成ONNX再用OpenCV的dnn模块读取ONNX文件。这个小模型走ONNX中转通常很顺但要注意ONNX导出时输入输出的节点名要记下来OpenCV读的时候要用正确的blob命名。5.4 TFLite输出和h5原始输出差异大现象同一张测试图h5输出fire概率0.87TFLite输出只有0.31差异远超浮点误差范围。原因TFLite默认可能启用动态范围量化权重从float32压到int8精度会有损失更常见的是输入预处理不一致h5推理时做了归一化到[0,1]而TFLite推理时忘了做或者resize插值算法从双三次换成了最近邻。解决先用converter.optimizations [tf.lite.Optimize.DEFAULT]配合FP16保留更高精度如果必须用INT8量化需要准备校准集让转换器统计激活值范围。预处理部分把两边的resize插值算法和归一化写法逐一比对这一步最容易藏雷。5.5 SLIC批量验证慢到怀疑人生现象superpixel脚本对单张图片处理还算正常但跑完整测试集要几个小时CPU占用还拉不满。原因脚本在循环里对每张图重新创建SLIC实例而且每张图被切成几百个超像素块每块都要单独过一遍模型计算量成倍放大。图片分辨率越高问题越严重。解决先降薪再干活。第一把输入图片统一resize到脚本预期的尺寸别让SLIC在高分辨率原图上跑第二用FireNet先做整图粗检只有命中fire的区域才切成超像素细筛第三n_segments控制在200附近就好盲目拉大既增加耗时又不显著提召回。5.6 真正出问题时先查哪三个地方排查顺序比技巧更重要。我的习惯是先看输入预处理再看模型输入shape最后看依赖版本。预处理包括resize插值算法和归一化范围90%的推理结果异常都出在这输入shape对不上会导致报错或静默的广播错误这个用model.summary()一眼就能确认依赖版本问题就回到第5.2条重装requirements.txt后再跑一次。按这个顺序查能避免在转换参数和网络结构上浪费时间。6. 进阶用法把验证过的FireNet接进摄像头实时推理6.1 滑动窗口多数投票压掉单帧误报验证通过之后最顺手的进阶就是把FireNet接到摄像头实时流上。火灾检测误报很常见因为闪光、灯光反射、甚至橙色衣服都可能让模型给出高概率。单帧判决不稳是常态解决办法是维护一个滑动窗口做时域投票# 实时推理每5帧抽1帧用多数投票跳过单帧误报 import cv2 import numpy as np import tensorflow as tf model tf.keras.models.load_model(models/firenet.h5) cap cv2.VideoCapture(0) window [] while True: ret, frame cap.read() if not ret: break if len(window) 5: # 预处理必须与训练保持一致resize到模型输入高宽 resized cv2.resize(frame, (64, 64), interpolationcv2.INTER_LINEAR) inp np.expand_dims(resized.astype(float32) / 255.0, axis0) prob model.predict(inp, verbose0)[0, 1] window.append(prob) else: fire_score float(np.mean(window)) if fire_score 0.5: cv2.putText(frame, FIRE, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imshow(firenet-live, frame) window window[1:] if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这里的关键参数是窗口长度window和判断阈值0.5。窗口越长越抗噪但报警延迟也越高5帧在摄像头30fps下大约是0.17秒延迟视觉上可以接受。fire_score 0.5这个阈值可以调宁可让误报多出现几次也不要让漏报吞掉早期火情。INTER_LINEAR是双线性插值如果训练时用的是其他插值算法这里必须保持一致否则输出会出现系统性偏移。6.2 两级检测粗筛加超像素细筛把实时推理和超像素结合起来才真正用上这套源码里那个superpixel脚本。我的做法是FireNet先对整帧做粗筛概率大于0.3但不高于0.7的模糊帧才对检测框内的区域跑SLIC细筛概率已经高于0.7的直接报警低于0.3的直接放过。这样既避免了每帧全图SLIC的性能灾难又保住了对小火焰的召回。模糊帧的数量通常很少细筛带来的额外算力压力完全可控。那次在部署现场我发现TFLite在一张小火苗图上输出fire概率只有0.3排查到最后是保存模型前忘了把归一化方式同步过去。从那以后我每次做完转换都强制把原始h5和导出模型在同一批测试图上对一遍输出再上线先保正确再谈优化。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网