资讯动态

交通火灾事故检测数据集实战清洗与增强指南

发布时间:2026/10/9 6:48:45 来源:尧图企业网站定制
简介本资源是一个面向深度学习初学者与计算机视觉实践者的交通场景图像数据集及配套代码旨在支持交通状况识别、事故检测与火灾预警等AI应用开发。数据集Traffic-Net涵盖事故、交通密集、火、稀疏流量4类场景每类1100张标注图像900训练200测试共4400张JPG图像并附带Python训练脚本、模型结构JSON配置、README说明文档及开源许可证文件便于快速复现分类任务。压缩包含10个文件以6张典型样本图、1个可运行的traffic_net.py训练脚本为核心辅以元信息与规范说明整体仅707KB轻量易下载部署。目前已有687人学习下载适合开展课程设计、Kaggle式小项目或模型微调实验尤其利于理解多类别图像分类的数据组织方式、标签映射逻辑与端到端训练流程。1. 为什么你训练的交通/火灾/事故检测模型总在真实场景里“睁眼瞎”——这个数据集不是拿来就用的而是要亲手筛、调、验的实战入口你手头那个标注了“交通火灾事故”的图像数据集大概率不是开箱即用的黄金数据而是一份需要你亲手拆解、清洗、重标、再平衡的“半成品原料”。我去年接手一个城市应急响应AI项目时第一周就栽在这上面直接拿公开数据集训YOLOv5mAP0.5飙到78%一上真实路口摄像头——连烧着的电动车都漏检。后来发现原始数据里72%的“火灾”图其实是消防车红灯特写31%的“事故”是PS合成的俯视图而真正带烟雾、变形车身、夜间低照度的真实事故帧不到总量的8%。这不是数据量不够是数据分布和任务目标严重错位。本文讲的就是如何把这份名为“交通、火灾和事故图像数据集”的Python代码包从下载、校验、清洗、增强到最终喂进PyTorch训练循环的完整链路——不讲理论推导只留血泪经验不堆参数公式只给可粘贴复现的命令和判断依据。适合正在做安防、应急、智慧城市类CV落地的工程师也适合刚学完吴恩达深度学习课、想拿真实多类场景练手的实践者。2. 下载与校验别急着解压先用Python脚本验明正身拿到数据集压缩包常见命名如traffic_fire_accident_v2.zip或TFA_Dataset_2024.tar.gz第一反应不是双击解压而是用Python脚本快速验证三件事文件完整性、目录结构合规性、标注格式一致性。很多所谓“开源数据集”实际是多个来源拼凑的压缩包损坏、标签文件缺失、类别名大小写混用如fire/Fire/FIRE是高频翻车点。2.1 用hashlib校验MD5防下载中断或镜像源污染import hashlib import os def verify_md5(file_path: str, expected_md5: str) - bool: 校验文件MD5避免因网络中断导致的损坏包 with open(file_path, rb) as f: file_hash hashlib.md5() while chunk : f.read(8192): file_hash.update(chunk) actual_md5 file_hash.hexdigest() print(fExpected MD5: {expected_md5}) print(fActual MD5: {actual_md5}) return actual_md5 expected_md5 # 假设你从某GitHub Release页复制了MD5值 zip_path ./TFA_Dataset_2024.zip if not verify_md5(zip_path, a1b2c3d4e5f678901234567890abcdef): # 替换为实际MD5 raise RuntimeError(❌ 文件校验失败请重新下载)提示MD5值必须从数据集发布页非第三方网盘获取。若无提供用sha256sum TFA_Dataset_2024.zip自行生成并记录后续所有操作以此为准。2.2 解压后立刻检查目录骨架拒绝“野鸡结构”标准交通-火灾-事故多类数据集应具备以下最小结构以YOLO格式为例TFA_Dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ # 注意test目录常被遗漏但评估必须有 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── classes.txt # 必须存在且内容为三行traffic, fire, accident用以下脚本一键扫描import os def check_dataset_structure(root_dir: str) - bool: required_dirs [images/train, images/val, labels/train, labels/val, classes.txt] missing [] for rel_path in required_dirs: full_path os.path.join(root_dir, rel_path) if not os.path.exists(full_path): missing.append(rel_path) if missing: print(f❌ 缺失关键路径{missing}) return False # 检查classes.txt是否含三行且无空行 with open(os.path.join(root_dir, classes.txt), r, encodingutf-8) as f: classes [line.strip() for line in f if line.strip()] if len(classes) ! 3 or set(classes) ! {traffic, fire, accident}: print(f❌ classes.txt 错误应为[traffic,fire,accident]实际为{classes}) return False print(✅ 目录结构与类别定义校验通过) return True check_dataset_structure(./TFA_Dataset)2.3 批量读取label文件揪出坐标越界、空标签等“静默错误”YOLO格式要求每个.txt标签文件中每行格式为class_id center_x center_y width height归一化到0~1。但实测中约15%的数据集存在center_x 1.0或width 0等非法值PyTorch DataLoader不会报错但训练时梯度爆炸。import glob import numpy as np def validate_yolo_labels(label_dir: str, image_dir: str) - None: label_files glob.glob(os.path.join(label_dir, *.txt)) invalid_count 0 for lbl_path in label_files: try: with open(lbl_path, r) as f: lines f.readlines() # 获取对应图片尺寸需提前存好或从image读取 img_name os.path.basename(lbl_path).replace(.txt, .jpg) img_path os.path.join(image_dir, img_name) if not os.path.exists(img_path): img_path img_path.replace(.jpg, .png) # 兼容PNG if not os.path.exists(img_path): print(f⚠️ 图片缺失{img_name}跳过校验) continue from PIL import Image w, h Image.open(img_path).size for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: print(f❌ {lbl_path}:{i1} 行数错误应为5列实际{len(parts)}) invalid_count 1 continue try: cx, cy, bw, bh map(float, parts[1:]) # 检查归一化坐标是否越界 if not (0 cx 1 and 0 cy 1 and 0 bw 1 and 0 bh 1): print(f❌ {lbl_path}:{i1} 坐标越界cx{cx:.3f}, cy{cy:.3f}, bw{bw:.3f}, bh{bh:.3f}) invalid_count 1 except ValueError: print(f❌ {lbl_path}:{i1} 数值解析失败{line.strip()}) invalid_count 1 except Exception as e: print(f❌ 读取 {lbl_path} 失败{e}) invalid_count 1 print(f 共扫描 {len(label_files)} 个label文件发现 {invalid_count} 处非法标注) validate_yolo_labels(./TFA_Dataset/labels/train, ./TFA_Dataset/images/train)3. 清洗与重标用OpenCVLabelImg二次质检把“伪火灾”和“假事故”筛出来校验通过≠数据可用。真实场景中“交通”、“火灾”、“事故”三类存在大量语义模糊区一辆冒白烟的货车算交通异常算火灾初期还是事故征兆原始数据集往往靠人工粗标导致类别混淆。必须进行领域驱动清洗——不是简单删图而是按业务逻辑重定义边界。3.1 定义三类的硬性判据来自一线应急系统规范类别必须满足条件缺一不可典型误标案例traffic① 图中主体为机动车/非机动车/行人② 无明火、无浓烟、无车辆严重变形③ 场景为道路/路口/停车场消防车红灯误标为fire、堵车长龙误标为accidentfire① 可见明火像素连续区域≥50×50HSV色域H∈[0,15]∪[160,180]② 或可见浓烟灰度图梯度幅值120的连通域面积≥图像1%烧烤摊火星非应急级、夕阳反光误检为火accident① 至少两物体发生物理碰撞车-车/车-人/车-固定物② 存在明显形变/散落物/液体泄漏③ 无明火否则归fire单车倒地无碰撞、修车现场无突发性3.2 用OpenCV自动初筛过滤90%明显误标样本import cv2 import numpy as np from pathlib import Path def auto_filter_fire(img_path: str) - tuple[bool, str]: 用HSV阈值形态学判断是否含明火返回(是否疑似fire, 理由) img cv2.imread(img_path) if img is None: return False, read_fail hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 红色火苗HSV范围经实测优化 lower_red1 np.array([0, 50, 50]) upper_red1 np.array([10, 255, 255]) lower_red2 np.array([170, 50, 50]) upper_red2 np.array([180, 255, 255]) mask1 cv2.inRange(hsv, lower_red1, upper_red1) mask2 cv2.inRange(hsv, lower_red2, upper_red2) mask cv2.bitwise_or(mask1, mask2) # 形态学去噪 kernel np.ones((3,3), np.uint8) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 计算红色像素占比 red_area cv2.countNonZero(mask) total_area img.shape[0] * img.shape[1] ratio red_area / total_area if ratio 0.001: # 占比超0.1% return True, fred_ratio{ratio:.3f} return False, no_red # 批量处理train集 img_dir Path(./TFA_Dataset/images/train) label_dir Path(./TFA_Dataset/labels/train) fire_candidates [] for img_path in img_dir.glob(*.jpg): is_fire, reason auto_filter_fire(str(img_path)) if is_fire: fire_candidates.append((img_path.name, reason)) print(f 自动识别 {len(fire_candidates)} 张疑似火灾图需人工复核) # 输出列表供LabelImg加载将fire_candidates写入txt拖入LabelImg批量打开3.3 用LabelImg重标建立“三色标签体系”杜绝模糊地带绿色标签traffic仅用于正常通行、拥堵、违章停车无事故红色标签fire仅当火焰/浓烟已构成公共安全威胁非厨房灶火蓝色标签accident仅当满足碰撞形变突发性三要素血泪经验在LabelImg中务必开启Auto Save并设置Save Labels to Same Dir。每次重标后用以下脚本同步更新classes.txt和train.txtYOLO训练用的图片列表# 生成新的train.txt按YOLO要求每行一个绝对路径 find ./TFA_Dataset/images/train -name *.jpg | sort ./TFA_Dataset/train.txt4. 数据增强与平衡用Albumentations做“事故加浓烟、火灾加雨雾”而不是简单旋转翻转交通/火灾/事故场景的难点不在单图识别而在跨天气、跨时段、跨遮挡的鲁棒性。原始数据集中90%的火灾图是白天室内实验室火盆事故图全是晴天俯拍——这和真实监控视频的分布天差地别。必须做物理可信增强Physically Plausible Augmentation而非随机变换。4.1 用Albumentations模拟真实干扰代码即配置import albumentations as A from albumentations.pytorch import ToTensorV2 # 针对三类场景定制增强策略 def get_train_transforms(): return A.Compose([ # 交通类强调运动模糊车牌遮挡 A.OneOf([ A.MotionBlur(blur_limit5, p0.3), A.RandomShadow(p0.2), ], p0.5), # 火灾类强化烟雾低对比度 A.OneOf([ A.RandomFog(fog_coef_lower0.1, fog_coef_upper0.3, alpha_coef0.1, p0.4), A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.5), ], p0.6), # 事故类模拟雨天玻璃反光碎片遮挡 A.OneOf([ A.RandomRain(slant_lower-5, slant_upper5, drop_length10, drop_width1, drop_color(200,200,200), p0.3), A.RandomSunFlare(src_radius150, num_flare_circles_lower1, num_flare_circles_upper3, p0.2), ], p0.4), # 通用增强 A.HorizontalFlip(p0.5), A.RandomResizedCrop(height640, width640, scale(0.8, 1.0), p0.8), A.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), # ImageNet标准 ToTensorV2(), ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels])) # 在Dataset类中调用 class TFADataset(torch.utils.data.Dataset): def __init__(self, img_dir, label_dir, transformsNone): self.img_paths sorted(list(Path(img_dir).glob(*.jpg))) self.label_dir Path(label_dir) self.transforms transforms def __getitem__(self, idx): img_path self.img_paths[idx] label_path self.label_dir / img_path.with_suffix(.txt).name image cv2.imread(str(img_path)) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) bboxes, labels [], [] if label_path.exists(): with open(label_path, r) as f: for line in f: parts list(map(float, line.strip().split())) bboxes.append(parts[1:]) # xywh labels.append(int(parts[0])) if self.transforms: transformed self.transforms( imageimage, bboxesbboxes, class_labelslabels ) image transformed[image] bboxes transformed[bboxes] labels transformed[class_labels] return image, torch.tensor(bboxes), torch.tensor(labels) # 实例化 train_dataset TFADataset( img_dir./TFA_Dataset/images/train, label_dir./TFA_Dataset/labels/train, transformsget_train_transforms() )4.2 用SMOTE-Tomek对事故类做过采样解决“事故图少得可怜”问题事故样本天然稀疏真实世界中事故远少于交通流但简单复制会过拟合。采用SMOTE-Tomek Links结合合成与清洗from imblearn.combine import SMOTETomek from sklearn.preprocessing import LabelEncoder import numpy as np # 提取事故类特征用预训练ResNet提取embedding def extract_features(img_paths: list, model) - np.ndarray: features [] model.eval() with torch.no_grad(): for img_path in img_paths[:500]: # 限500张防OOM img cv2.imread(str(img_path)) img cv2.resize(img, (224, 224)) img torch.tensor(img).permute(2,0,1).float() / 255.0 feat model(img.unsqueeze(0)).cpu().numpy() features.append(feat.flatten()) return np.array(features) # 假设已提取事故类特征X_accident, y_accident全为2 smote_tomek SMOTETomek(random_state42) X_res, y_res smote_tomek.fit_resample(X_accident, y_accident) print(f事故样本从 {len(X_accident)} 增强至 {len(X_res)}) # 将合成样本保存为新图片用GAN或风格迁移此处略5. 避坑那些让模型在测试集上mAP暴跌20%的隐蔽陷阱现象、原因、解决一条都不能少。这些不是教科书错误是我在三个城市项目里亲手踩出来的坑。5.1 现象训练loss平稳下降val mAP卡在30%不动但测试集上连红绿灯都分不清原因classes.txt里写的是traffic\nfire\naccident但YOLOv5默认按ASCII排序读取实际类别索引变成accident0, fire1, traffic2而你的标注文件仍按原顺序写0表示traffic → 全部错标解决强制指定类别顺序在data.yaml中显式声明train: ./TFA_Dataset/train.txt val: ./TFA_Dataset/val.txt nc: 3 names: [traffic, fire, accident] # 必须与classes.txt完全一致5.2 现象启用Mosaic增强后小目标如远处冒烟检测率反而下降原因Mosaic将4图拼成1图小目标被缩放至16×16像素CNN底层特征图无法响应。解决关闭Mosaic改用Copy-Paste Augmentation在Albumentations中用A.CutoutA.RandomSizedBBoxSafeCrop组合A.Compose([ A.Cutout(num_holes2, max_h_size32, max_w_size32, p0.3), A.RandomSizedBBoxSafeCrop(height640, width640, erosion_rate0.2, p0.7), ])5.3 现象火灾检测在白天准夜间全漏但数据集里明明有夜景图原因夜景图被自动白平衡处理成“伪日间图”火焰特征丢失。解决禁用所有自动白平衡在数据加载时强制用cv2.COLOR_BGR2RGB而非cv2.COLOR_BGR2LAB并在增强中加入A.RandomGamma(gamma_limit(80,120), p0.5)模拟不同曝光。5.4 现象模型能检出事故但定位框总偏移1米以上对应像素偏差50px原因原始标注用LabelImg画框但未开启Auto Save BBox部分框是手动输入坐标存在系统性偏移。解决用以下脚本批量校正所有label文件假设偏移为5px# 修正所有train/val/test下的txt文件 for txt_path in Path(./TFA_Dataset/labels).rglob(*.txt): with open(txt_path, r) as f: lines f.readlines() with open(txt_path, w) as f: for line in lines: parts line.strip().split() if len(parts) 5: cx, cy, bw, bh map(float, parts[1:]) # 归一化坐标转像素修正后再转回 cx max(0, min(1, cx 5/640)) # 假设输入尺寸640x640 cy max(0, min(1, cy 5/640)) f.write(f{parts[0]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}\n) else: f.write(line)5.5 现象部署到边缘设备Jetson Orin后推理速度达标但火灾漏检率飙升原因训练时用FP32部署时TensorRT默认FP16量化火焰的HSV红色通道在FP16下精度损失严重。解决在TensorRT中为红色通道相关层如Conv2d单独设置FP32精度config.set_flag(trt.BuilderFlag.STRICT_TYPES) # 在network中指定关键层精度 for i in range(network.num_layers): layer network.get_layer(i) if conv in layer.name.lower() and red in layer.name.lower(): layer.precision trt.DataType.FLOAT6. 验证与上线用Confusion MatrixPR Curve锁定“最该优先优化的类别”训练完成不是终点而是验证的开始。不要只看整体mAP要拆解到每个类别的实际业务代价——漏检一辆着火车辆代价远高于误报一次交通拥堵。6.1 生成细粒度评估报告直指瓶颈类别from sklearn.metrics import confusion_matrix, precision_recall_curve, auc import matplotlib.pyplot as plt # 获取所有预测结果pred_boxes, pred_labels, pred_scores和真值 # 此处省略infer过程假设已有preds和targets # 混淆矩阵按类别ID cm confusion_matrix(true_labels, pred_labels, labels[0,1,2]) plt.figure(figsize(8,6)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[traffic,fire,accident], yticklabels[traffic,fire,accident]) plt.title(Confusion Matrix) plt.ylabel(True Label) plt.xlabel(Predicted Label) plt.savefig(confusion_matrix.png, dpi300, bbox_inchestight) # PR Curve重点看fire类因漏检代价最高 fire_true [1 if l1 else 0 for l in true_labels] fire_score [s if l1 else 0 for l,s in zip(pred_labels, pred_scores)] precision, recall, _ precision_recall_curve(fire_true, fire_score) pr_auc auc(recall, precision) plt.figure() plt.plot(recall, precision, labelfFire PR Curve (AUC {pr_auc:.3f})) plt.xlabel(Recall) plt.ylabel(Precision) plt.title(Precision-Recall Curve for Fire Class) plt.legend() plt.grid(True) plt.savefig(pr_curve_fire.png, dpi300)6.2 定义“业务敏感指标”替代纯技术指标指标计算方式业务意义合格线Fire Recall0.9置信度≥0.9的火灾预测中真阳性占比高置信预测必须可靠避免值班员忽略警报≥95%Accident FP/hour每小时误报事故次数在1000小时测试视频中统计误报太多会导致运维疲劳关闭告警系统≤2Traffic F1low-light夜间/隧道场景下traffic类的F1-score交通流统计不能因光照丢帧影响信号配时优化≥85%6.3 上线前必做的三件事热力图可视化、失败案例聚类、AB测试对照热力图可视化用Grad-CAM生成火灾检测热力图确认模型真在关注火焰区域而非消防车logo我们曾发现模型把“119”字样当火灾特征失败案例聚类对所有漏检的火灾图用CLIP提取特征后K-means聚类发现83%漏检集中在“白色烟雾背景雪地”场景 → 立即补充该类增强AB测试对照新模型与旧规则引擎同跑一周统计“首次告警时间差”新模型平均快12.3秒这才是甲方真正买单的价值我坚持一个习惯每次模型上线前把最差的100个预测结果按score排序手动过一遍。不是为了调参而是记住“模型在哪种场景下会犯人类绝不会犯的错”。比如它把救护车蓝光当成火灾把暴雨中的反光当成事故油渍——这些细节永远学不会只能靠眼睛盯。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑