首页 / 资讯中心 / 文章详情

智慧城市市容检测数据集:VOC转YOLO格式与训练避坑指南

智慧城市市容检测数据集:VOC转YOLO格式与训练避坑指南 ★ FEATURED ARTICLE
简介面向智慧城市市容巡检场景的街道涂鸦、垃圾、故障路灯等目标检测数据集已按VOC与YOLO两种格式整理适合训练目标检测模型。资源涵盖19263张图片对应的标注共11个类别覆盖涂鸦、垃圾堆放、故障路灯等常见市容问题可用于街道环境卫生监测、市容问题自动发现等应用。需特别留意的是数据集中超过一半为增强图片由4张拼接而成下载前请先查看预览图确认符合需求。压缩包共2000个文件以XML标注文件为主含1999个XML和1个使用说明TXT其中XML对应Pascal VOC格式、TXT对应YOLO格式整体约978.91MB。目前已有225人学习下载。资源内标注组织清晰可直接用于相应检测框架训练免去格式转换时间适合从事城市管理、AI巡检算法开发的工程师与学生使用。1. 智慧城市街道涂鸦垃圾故障路灯市容检测数据集为什么19263张双格式数据值得动手市政巡检员每天处理大量巡街照片问题对象很杂墙上涂鸦、街边成堆垃圾、故障路灯、破损井盖……每一项都单独建模型成本高且难以维护。智慧城市街道涂鸦垃圾故障路灯市容检测数据集这类方案就是把常见市容问题收进一个11类别、19263张的监督训练集合同时给出Pascal VOC和YOLO两种标注格式。它解决的是智慧城市视觉落地里最现实的问题有没有现成、干净、数量够的数据让你把检测模型先跑起来。适合谁用做市政AI的算法工程师、搞智慧城市视觉终端的开发者、做城市管理课题的学生都能拿这份数据直接练手不用自己在街景图上慢慢画框。全文围绕“怎么打开它、怎么转格式、怎么避坑、怎么训练验证”展开中间会有可直接复制的脚本和相关参数说明。2. VOC与YOLO双格式到底存了什么目录结构、XML字段与归一化坐标换算拿到这类数据集压缩包很多人的第一反应是直接解压扔进训练脚本。但市容检测数据里Pascal VOC和YOLO格式的标注互不相同训练框架的读取方式也不一样先花十分钟把两种格式看清楚后面能省半天排错时间。2.1 两种格式并存的取舍Pascal VOC的老牌标注与YOLO的现代训练Pascal VOC是目标检测领域的老牌标注协议。它用一个XML文件描述每张图片里的对象文件名、图片来源、图像宽高以及每个目标的类别和边界框坐标左上角xmin、ymin右下角xmax、ymax。公开数据集像早期的VOC2007、VOC2012都是这套结构很多老工具链也默认读它。YOLO格式则完全不同每个标注是一行文本“类别id x_center y_center width height”坐标全部对图片宽高做了归一化取值在0到1之间。YOLO系列训练脚本用的是这种紧凑格式。双格式并存的现实意义在于兼容两种工作流你习惯了LabelImg的VOC交互就用XML做二次修正你要用YOLOv5、YOLOv8直接训练就拿TXT格式省掉转换。另外YOLO格式损失了目标属性信息VOC格式则便于人工阅读和审核。对市容场景来说涂鸦位置是否标全、垃圾袋是否漏标用XML肉眼复查更直观所以发布到开源社区的数据集往往保留VOC为“母版”再生成YOLO副产物。2.2 解压后先看目录结构确认图片与标注的对应关系拿到“.7z”压缩包第一步是解压并列出目录树。Windows下用7-ZipLinux下用p7zip命令很简单。解压后应能看到类似下面的组织方式7z x 智慧城市街道涂鸦垃圾故障路灯市容检测数据集VOCYOLO格式19263张11类别.7z -o./street_dataset cd ./street_dataset tree -L 2典型数据布局会是三个平级区域JPEGImages或images放图片Annotations或VOC/Annotations放VOC的XML文件YOLOLabels或labels放YOLO的TXT文件。再往下可能还有ImageSets/Main存训练验证划分的txt索引里每行写不带扩展名的图片名。看到这个结构后请先做一个动作统计三种文件数量是否对得上。这里有个常见问题有些压缩包只给图片和标注不给划分文件需要自己按比例随机分出训练集、验证集。我一般不建议直接拿全部数据训练因为智慧城市场景里同一街道多次拍摄会产生相似样本不划分就评估mAP会虚高后面做部署对比时容易被质疑。2.3 YOLO标注归一化坐标换算从XML里的像素到txt里的比例VOC和YOLO之间最关键的技术点是坐标从“像素绝对坐标”换算成“归一化相对坐标”。比如一张1920×1080的街景图XML里记录一个涂鸦框是“xmin520, ymin300, xmax980, ymax760”对应YOLO的四个数必须这样算x_center(520980)/2/1920≈0.3906y_center(300760)/2/1080≈0.4907width(980-520)/1920≈0.2396height(760-300)/1080≈0.4259。很多人在自己写的转换脚本里翻车就是忽略了宽高必须除以原始图片尺寸而不是除以缩放后的尺寸。市容照片来自不同设备有可能是4032×3024的手机原图也有可能是1920×1080的监控截图若脚本不读每张图的真实宽高而统一用某个固定值生成的TXT全部错位。所以下一步的转换脚本里第一步永远是打开XML读size节点的width和height而不是对图片文件做假设。import xml.etree.ElementTree as ET xml_path Annotations/street_001.xml tree ET.parse(xml_path) root tree.getroot() # 从XML的size节点获取真实宽高这是归一化的分母 img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) for obj in root.findall(object): class_name obj.find(name).text bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化中心点坐标除以宽高宽高同样除以宽高 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height box_width (xmax - xmin) / img_width box_height (ymax - ymin) / img_height print(class_name, round(x_center, 6), round(y_center, 6), round(box_width, 6), round(box_height, 6))这段代码验证了VOC到YOLO的换算过程。注意所有除法都用了浮点数Python 3里单个斜杠就是真除法不会取整。如果是在Python 2环境或不小心用了整数除法1/2会得到0坐标全部失常这是踩坑重灾区。另外要说明的是归一化后的坐标允许略超出0到1范围。当目标被图片边缘截断时xmin为负或xmax超出图片宽度计算后会出现负值或大于1的值。YOLO训练脚本通常能容忍轻微越界但当框严重越出画面比如目标只有5%在图像内我一般建议直接过滤掉因为这类残缺样本会让模型学到奇怪的纹理对市容场景的涂鸦检测收益很低。3. 从VOC转YOLO转换脚本与四个边界坑很多框架只吃YOLO格式所以拿到VOC标注后最常做的就是批量转格式。这个过程看起来是把上面的单条逻辑包一层循环但实际落地时至少有四个边界坑会让人折腾一晚上。这一章直接给出我常用的转换脚本和参数细节。3.1 转换前先体检统计11个类别的框数量与图片数量写转换脚本之前先对数据集整体做一次体检。市容检测数据集的难点常在类别极端不均衡有些类别可能有一万多个框有的类别只有一两百个框。不做统计直接转换训练后稀有类别几乎不可用。import os import xml.etree.ElementTree as ET from collections import Counter anno_dir Annotations counter Counter() image_names [] for xml_file in os.listdir(anno_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, xml_file)) root tree.getroot() image_names.append(root.find(filename).text) for obj in root.findall(object): counter[obj.find(name).text] 1 print(统计到图片数:, len(image_names)) print(各类别框数:, counter.most_common())统计结果出来后做两件事第一确认类别名是否和你的训练配置一致。比如某个类叫“Litter_Trash”另一个叫“litter_trash”在OCR里看着正常但YOLO会把它们当成两个类id错位训练指标直接失真。市容数据因为采集周期长、标注人员不同大小写不一致、下划线和空格混用的情况很常见建议转换时统一做一次类别映射。第二观察类别分布。若发现严重长尾分布后面训练阶段就要考虑样本均衡策略这一点我会在第四章展开。3.2 VOC XML转YOLO TXT的核心脚本体检通过之后按下述脚本批量转换。脚本设计成一个函数输入VOC目录、YOLO输出目录和类别映射表输出每张图片对应的TXT文件。import os import cv2 import xml.etree.ElementTree as ET # 类别映射表按训练需求固定id顺序不要依赖字母序 CLASS_MAP { Graffiti: 0, Litter: 1, TrashBag: 2, GarbageBinFull: 3, FaultyStreetlight: 4, DamagedManholeCover: 5, IllegalPoster: 6, RoadSpillage: 7, OccupiedSidewalk: 8, OverflowingDustbin: 9, VendorStall: 10, } def voc_to_yolo(anno_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(anno_dir): if not xml_file.endswith(.xml): continue xml_path os.path.join(anno_dir, xml_file) tree ET.parse(xml_path) root tree.getroot() img_path os.path.join(img_dir, root.find(filename).text) # 用OpenCV读取图片尺寸XML里的宽高如果缺失时可以兜底 h, w cv2.imread(img_path).shape[:2] lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAP: print(f跳过未映射类别: {name} in {xml_file}) continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 过滤严重越界框完全在画面外或宽高为负 if xmax xmin or ymax ymin: continue cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{CLASS_MAP[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_name os.path.splitext(xml_file)[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines)) voc_to_yolo(Annotations, JPEGImages, YOLOLabels)这套脚本有几个关键点。第一类别映射表是手工写的不是自动从XML里收集的目的是保证id顺序稳定。YOLO训练时类别id从0开始连续编号class文件里第几行就对应id几。如果你用自动收集然后排序下次换一台机器或者数据集更新类id可能全变模型就得重新训练。第二用OpenCV读图片尺寸比直接读XML里的size字段更可靠因为部分标注工具生成的XML里宽高是错的甚至缺失。第三过滤了宽高异常的框。市容数据里特别远的垃圾袋经常被标成几个像素的小框这种框既没意义又干扰损失函数。转换完成后务必抽检。随机打开几个TXT文件用可视化脚本把框画回图上检查不要只看数值。标注框偏移一到两个像素肉眼看不出来但归一化到小分辨率训练时偏移可能被放大成明显的错位。3.3 脚本参数说明类别id、图片尺寸与输出目录上述脚本有四个参数需要重点关注。类别映射表CLASS_MAP必须与训练用data.yaml的顺序完全一致。如果data.yaml写“Graffiti”在第二个位置而映射表把Graffiti编为0训练出来的类别就全部错位。图片尺寸读取方式脚本用了OpenCV的imread它会按原图分辨率读取。但注意如果图片有EXIF旋转信息OpenCV默认不会自动矫正方向某些手机拍摄的街景图会被倒置导致框错位。解决方式是用cv2.imread(..., cv2.IMREAD_IGNORE_ORIENTATION)强制不应用旋转并在标注阶段就确认方向一致。输出目录out_dir建议转换到独立的labels目录不要直接在原始目录上改动。保留VOC原文件出现问题有“后悔药”可吃这也是我多年做数据集的基本习惯。文本精度{:.6f}保留了6位小数。对1920宽的图片6位小数对应约0.002像素精度完全够用。有人习惯保留8位没必要反而让TXT文件变大训练读取时字符串解析更慢。3.4 四个边界坑空标注、截断目标、非矩形框、中文路径转换不是跑通就完事边界情况才是数据质量的分水岭。第一个边界坑是空标注。市容巡检中经常出现首尾帧图片里没有任何问题目标但XML文件依然存在只是没有object节点。转换脚本会产生一个空TXT文件YOLO训练时遇空标签默认跳过该图。问题在于若验证集里空图过多评估时会引入偏差建议单独把它们挑出来要么从训练集剔除要么标注成背景类别。第二个边界坑是截断目标。街景图片里涂鸦经常被电线杆、车辆遮挡标注框会延伸到画面外。处理方式有两种一是将越界框裁剪回图像边界二是直接过滤。我一直用过滤加保留混合策略保留越界比例低于10%的框并且让脚本把坐标clip到[0,1]区间保证训练不崩。第三个边界坑是非矩形框。涂鸦形状不规则标注工具若输出polygon格式很多转换脚本会直接报错或强行取外接矩形。取外接矩形会引入大量背景涂鸦本来就细长背景一多模型容易误检。做法是把多边形顶点放进XML的polygon节点转换时按最小外接矩形处理同时记住该框是复杂目标后续训练可配合高IOU阈值。第四个边界坑是中文路径。开源数据集的标注文件里嵌着中文文件名Windows下用脚本遍历时编码如果不对cv2.imread会返回None紧接着取shape时就抛异常。解决办法是在脚本开头设置os.environ[PYTHONIOENCODING] utf-8或者统一先重命名文件为拼音加编号。这条属于经典“玄学Bug”排查半小时后才意识到是编码问题。4. 11类别数据怎么用类别体系、样本均衡与训练集划分市容检测数据集和通用目标检测数据集有个显著区别类别语义高度重叠。垃圾桶满溢和路边垃圾袋在视觉上边界模糊涂鸦和非法小广告在颜色、纹理上也很接近。这些重叠会让模型在验证集上表现尚可一上真实街道就露馅。所以动手训练前必须明确类别体系并做好样本均衡处理。4.1 类别体系设计涂鸦、垃圾、故障路灯与市容杂项先按市容问题类型把11个类别归组。第一类是涂鸦类包含墙体喷绘涂鸦特点是颜色鲜艳、边缘锐利、形状不规则第二类是垃圾类包含散落垃圾、袋装垃圾、垃圾桶满溢特点是纹理杂乱、尺度跨度大远看一个像素点近看一大片第三类是设施故障类包含故障路灯、破损井盖特点是暗光环境下对比度低第四类是市容杂项类包含违规小广告、占道经营、道路遗撒等特点是类别间视觉特征极易混淆。我在实操时第一步不是急着写训练脚本而是把每张图片的类别标签打印出来逐类做语义边界定义。比如“Litter”散落垃圾和“RoadSpillage”道路遗撒前者强调人为丢弃的废弃物后者强调车辆运输中掉落的物品。边界定义好之后写进一个CLASS.md文件将来新增标注数据或换标注外包时这份文件就是标准答案。4.2 训练/验证集划分随机划分的脚本与种子固定没有现成划分文件时按8:2随机划分训练集和验证集。但必须固定随机种子否则每次运行结果不同同一张图忽而在训练集忽而在验证集没法对比实验。import os import random from sklearn.model_selection import train_test_split random.seed(42) image_files [f for f in os.listdir(JPEGImages) if f.endswith(.jpg)] train_files, val_files train_test_split(image_files, test_size0.2, random_state42) with open(train.txt, w) as f: for name in train_files: f.write(os.path.join(JPEGImages, name) \n) with open(val.txt, w) as f: for name in val_files: f.write(os.path.join(JPEGImages, name) \n) print(f训练集 {len(train_files)} 张验证集 {len(val_files)} 张)划分之后还要做一次类别分布校验分别统计训练集和验证集里各类别框的数量尽量让比例接近。如果验证集里“故障路灯”只有两个框验证mAP的置信区间就会过宽偶然性太大。发现比例不对时不要硬改随机种子直接把稀有类别的样本手动放进训练集再从其他类别里随机补齐验证集。4.3 样本均衡策略欠采样与数据增强的边界长尾分布是市容数据集的常态。涂鸦、垃圾袋这类高频类别动辄几千框破损井盖可能只有一两百框。直接训练会让模型倾向于把未知目标预测成高频类别这属于目标检测里最常见的“翻车”模式。常用做法分两步。第一步是图像的欠采样控制高频类别的图片数量让它不超过稀有类别图片数量的五倍。第二步是稀有类别的针对性增强包括随机旋转、亮度扰动、Mosaic拼接。但注意市容场景有强先验方向涂鸦和垃圾不被倒置旋转增强的角度范围应控制在正负30度以内全角度旋转会违背“街道方向固定”的物理常识反而降低真实场景精度。如果评估下来模型对稀有类别还是无感可以降低损失函数里正样本权重或者在损失函数设计上给稀有类别更高的loss权重。YOLOv8的默认配置里没有直接的类别权重参数但可以修改loss.py或者用采样器调整图片权重。这条要根据框架版本灵活处理不要照抄网上的教条。5. 避坑清单训练这个数据集最容易翻车的5个检查点把19263张数据真正跑起来之后回看整个过程坑其实不在模型结构而在数据集本身的细节。下面五条是我做市容检测时实打实踩过的坑按“现象→原因→解决”列出来希望能帮你少走弯路。现象一训练loss正常下降但验证集mAP始终在0.3附近徘徊。原因类别映射表顺序与data.yaml不一致导致验证时预测框类别id对不上计算mAP全是错配。解决训练前打印前10张图片的标签TXT内容和data.yaml里class的顺序逐行校对。现象二训练卡死在某个epoch报错提示“Image not found”。原因XML里filename字段写了相对路径但实际图片文件名带有中文或空格Windows下路径拼接失败。解决统一重命名图片为纯数字编号同时更新XML里的filename节点。现象三某些类别检测精度极高某些类别完全检不出。原因训练时没做类别均衡稀有类别样本数不足。解决参考第4.3节做欠采样加定向增强并检查数据增强是否破坏图像语义。现象四验证集mAP很高但部署到现场摄像头效果很差。原因训练集和验证集来自同一批次街道照片时间接近、光线相似模型学的是场景匹配而不是目标特征。解决用不同时间段、不同街道的照片做外部验证集把内部验证集mAP当作参考值不当作上线指标。现象五转换后的YOLO标签大量出现“nan”。原因图片尺寸读取异常cv2.imread返回None宽高变成None除法产生nan。解决在转换脚本里加一个检查读不到图片就直接打印文件名单独排查不要通过批量逻辑掩盖。这五条几乎覆盖了从数据预处理到模型评估的完整链路。很多时候不是模型调参不够而是数据管线的某个细节在捣鬼。保持怀疑态度遇到底层指标异常优先怀疑数据链路。6. 跑通收尾YOLOv8最小训练命令与mAP验证技巧最后一步用YOLOv8把转换好的数据集跑起来。这里给出最小可行的训练方案并对关键参数做说明适合快速验证数据集质量。pip install ultralytics yolo detect train \ datastreet.yaml \ modelyolov8s.pt \ epochs50 \ imgsz640 \ batch16 \ lr00.01 \ seed42其中street.yaml需要指向你的图片目录和标签目录train: ./train.txt val: ./val.txt nc: 11 names: [Graffiti, Litter, TrashBag, GarbageBinFull, FaultyStreetlight, DamagedManholeCover, IllegalPoster, RoadSpillage, OccupiedSidewalk, OverflowingDustbin, VendorStall]跑完50个epoch后保存最优权重用验证集评估。命令行会输出mAP50和mAP50-95这两个数字不是终点。我更习惯翻出验证集的混淆矩阵看看哪些类别在互相“打架”。如果涂鸦和非法小广告高度混淆说明类别定义过细或标注本身有分歧这时候改模型结构没用回去统一标准才是正道。最后一个血泪经验训练完在测试集上可视化预测结果时不要只看检测出目标的图多翻翻漏检图。YOLO在明亮光照下的涂鸦识别效果通常不错但晚上昏暗街道上的故障路灯、被树叶遮挡的垃圾是模型最容易漏掉的两个场景。如果这两类在你的业务里重要我建议对训练集做亮度扰动增强并单独收集夜间数据进行微调。这个方向的“验证”也不该停在mAP数字上。把模型接上GStreamer管道推流测试在真实街道视频上的稳定性和误报率才是最终标准。项目做到这里你会深刻体会到数据集质量对最终效果的决定作用——这也是我做智慧城市项目最深的教训数据标注的歧义和类别边界模糊比模型结构带来的影响大得多。希望这些经验对你有用按这个路径走一遍你也能在这个方向上少踩几个坑。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站