免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

行人检测数据集构建:VOC与YOLO标签格式转换实战指南

行人检测数据集构建:VOC与YOLO标签格式转换实战指南 简介一套面向行人检测任务的数据集从COCO2017中提取person类样本整理为VOC与YOLO两种格式标签分别对应xml和txt文件可直接用于YOLO系列模型训练与评估。当前压缩包为四部分中的第一部分覆盖16703个person标注目标包内文件总数2000主要包含jpg图像、xml标签与txt标签三类文件整体约834MB。txt为YOLO格式的归一化坐标xml为VOC格式的边界框信息两种标签便于在不同检测框架间切换。已有676人学习下载。数据聚焦单一类别person场景多样适合目标检测算法对比、模型微调或相关课程实验图像与标签一一对应可直接接入YOLOv5、YOLOv8等常见项目的数据加载流程省去自行转换格式与整理样本的麻烦解压后即可按需划分训练、验证集。该数据集源自COCO2017官方标注类别纯净适合作为行人检测方向的基准数据如需更大规模可继续获取后续部分组合使用。 做行人检测模型的人十有八九第一步就卡在数据集上。不是没有数据而是数据拿回来后发现标签格式五花八门有人给的是VOC的XML有人给的是YOLO的txt还有人直接甩给你一个JSON只留下一句“格式自己转换”。我最早踩这个坑的时候一个晚上全耗在写格式转换脚本上后来干脆自己整理了一套从数据集构建到格式规范的完整流程这次就先从行人检测数据集和VOC、YOLO两种主流标签格式聊起。这篇文章适合谁看准备自己标注行人检测数据集的新手、被公共数据集各种格式搞晕的调包侠、或者想系统梳理标签格式转换逻辑的老手。我会把VOC和YOLO两种格式的细节拆开揉碎给出一份可以直接复制跑通的转换脚本顺带把我在实际标注和训练过程中踩过的坑一并记录下来。1. 数据集整体设计思路先定格式再谈训练很多人拿到训练任务的第一反应是“赶紧找数据”但实际上最先应该定下来的不是数据量而是标签格式。因为标签格式决定了后续所有代码的写法和数据流走向等标注完几千张图再换格式那才是真正的折磨。1.1 为什么行人检测数据集偏爱VOC和YOLO格式行人检测本质上是一个目标检测的子任务而目标检测项目中最常见的两种标注格式恰好就是VOC格式和YOLO格式。VOC格式源自Pascal VOC挑战赛用XML文件存信息YOLO格式则是随着YOLO系列算法流行起来的纯文本标注。很多公开数据集比如INRIA行人数据集、Caltech行人检测数据集、CrowdHuman、KITTI的一部分标注都会提供VOC风格或YOLO风格的标签你在做完格式选型后才能决定这些数据集拿到手之后要做什么样的预处理。从我的个人经验来看这里有一个选型逻辑如果你打算用Faster R-CNN、SSD这类模型或者习惯阅读XML里的详细属性比如truncated、difficult、occludedVOC格式更友好如果你打算直接用YOLOv5、YOLOv8训练那YOLO格式是默认选项。而且YOLO格式文件小、读取快数据加载管线不需要XML解析训练时IO压力小很多。我自己现在的新项目基本上都是以YOLO格式作为标准存储格式需要VOC时再反向生成。1.2 构建数据集时的核心思考质量优于数量自建行人检测数据集时我经常看到有人热衷于凑数量什么图都往里面塞结果训练出来的模型在光线稍暗的场景里就“失灵”。真正决定模型上限的往往是数据质量和标注规范而不是单纯的数量。采集图像时至少要覆盖几个维度不同光照条件白天、黄昏、夜晚、不同距离近景全身、中景半身、远景小目标、不同姿态正面、背面、侧面、不同场景街道、商场、校园、停车场。此外标注时有一个容易被忽略的细节边界框的框定标准。行人检测数据集通常要求框住整个人体但遇到遮挡物怎么办一般做法是框可见部分还是完整人体不同公开数据的标准不太一样我建议在项目初期就把标注规范写死例如“必须包含头部和至少一个肩部遮挡超过50%的实例标记为difficult但仍要标注完整人体边界框”。这个标准不提前约定两个标注员标出来的框会有肉眼可见的偏差。2. VOC格式与YOLO格式的深度拆解从标签结构到转换逻辑两种格式看起来都是“画框”但存储方式完全不同。我先把它们的底层结构讲清楚后面转换脚本才能写得明白。2.1 VOC格式一份结构严谨的XML“简历”VOC格式的每个图像对应一个同名XML文件典型内容是这个样子annotation folderJPEGImages/folder filenamestreet_001.jpg/filename source databaseMyPedestrianDataset/database /source size width1920/width height1080/height depth3/depth /size segmented0/segmented object nameperson/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin345/xmin ymin256/ymin xmax478/xmax ymax689/ymax /bndbox /object /annotation逐字段拆解一下。folder和filename主要用于溯源告诉别人这个标注对应哪个目录下的哪张图片。size里记录的是图片原始宽度、高度和通道数这个字段在格式转换时非常关键很多新手转换出错就是因为这里和实际图片尺寸不一致。object是核心每个object对应一个行人目标name是类别名truncated表示目标是否被图像边界截断difficult表示这个目标是否因为太小、太模糊等原因被标记为难例。最后bndbox里的xmin、ymin、xmax、ymax是边界框的左上角和右下角坐标注意这里的单位是像素而且是整数基于原始图片尺寸。VOC格式最大的优点是信息完整不仅可以记录框的位置还能额外记录目标的姿态、遮挡程度、难易程度等属性。但缺点也很明显一个文件要写一大堆标签信息解析慢而且如果一张图里有几十个行人XML文件会长到夸张。2.2 YOLO格式一图一txt的极简标注YOLO格式的标注同样和图片同名但扩展名是txt文件内容每一行代表一个目标格式如下0 0.2148 0.4365 0.0693 0.4019 0 0.7315 0.5821 0.0812 0.3556第一个数字是类别ID从0开始后面四个数字分别是归一化后的中心点x坐标、中心点y坐标、边界框宽度和边界框高度。归一化的含义是用边界框的实际像素位置除以图片的宽或高让所有坐标值都在0到1之间这样无论图片分辨率是640乘640还是1920乘1080标注数据都不会失真。具体换算公式是x_center ((xmin xmax) / 2) / image_width y_center ((ymin ymax) / 2) / image_height width (xmax - xmin) / image_width height (ymax - ymin) / image_height如果把VOC格式比作一份详细简历那YOLO格式就是一张极简名片——只保留最关键的信息没一句废话。2.3 两种格式的关键差异对照我用一张表把两者的核心区别列出来方便对照对比维度VOC格式YOLO格式文件类型XML纯文本txt坐标类型绝对像素值整数归一化浮点数0~1类别标识类别名称字符串person类别ID数字0附加信息含truncated、difficult、pose等无任何附加信息可读性人和机器都好读机器友好人读起来费劲文件大小较大较小适用框架Faster R-CNN、SSD等YOLO系列、部分检测框架这里有一个关键点需注意同样一张图VOC里同一个object可以有多个name而YOLO格式里必须通过classes.txt文件来映射类别ID和类别名的对应关系。如果你的数据集只有“行人”一个类别那类别ID永远是0如果后续加入了“骑行者”“车辆”等类别顺序一旦定好就不能随便调整否则之前转换好的所有标签都会错位。3. 实操记录从原始数据到可训练数据集的完整流程理论说清楚了下面进入动手环节。我以自采的行人图片为例完整走一遍“图像整理—标注—格式转换—划分数据集—配置训练文件”的流程。3.1 图像采集与预处理图像的来源可以是自拍、公共数据集抽取也可以是公司内部积累的监控截图。不管来源是什么第一步都是统一文件名和图片格式。我习惯的命名规则是类别_日期_序号.jpg例如person_20250105_001.jpg。这样可以避免不同批次数据出现文件名重复也方便追溯。预处理阶段有几件事要做统一图片格式建议全部转为JPGPNG虽然无损但体积偏大。统一色彩空间如果数据集里混有灰度图和彩色图建议全部转成RGB三通道。删除重复图片尤其是从公开数据集不同批次里拼出来的数据极易出现重复样本我自己用哈希去重过滤掉了一大批。图片尺寸过小比如小于300像素的短边建议删除或单独归类因为行人太小的情况下即便标注了模型学到的东西也很有限。3.2 使用LabelImg完成标注标注工具我推荐开源的LabelImg界面简洁支持PascalVOC和YOLO两种输出格式。安装后打开一张图片在视图菜单里打开自动保存模式然后用快捷键W画框、A/D切换上一张/下一张、CtrlS保存。整个标注过程很像PS里框选选区只是这里框出来的每个区域都要填一个类别名。如果数据集只有一个类别建议把默认标签直接设为person能省不少事。标注时画框的原则是尽量贴合行人轮廓上下左右不要留太多空白也不要截掉头部或脚部。我曾见过不少新手标出来的框只框到躯干头部在框外这类框在后续训练里会严重干扰模型对行人整体特征的学习。3.3 VOC格式转YOLO格式的完整脚本实际项目中LabelImg直接标注成YOLO格式很方便但如果你从公开数据集下载到的是VOC格式的标注就必须要转换。这里我给出一个自己一直在用的转换脚本它做的事情很简单读取XML文件解析出每个目标的坐标和类别再通过classes.txt把类别名映射为ID最后写出YOLO格式的txt文件。import os import xml.etree.ElementTree as ET from pathlib import Path # 注意classes文件中每一行对应一个类别顺序不能乱 with open(classes.txt, r, encodingutf-8) as f: classes [line.strip() for line in f if line.strip()] class_to_id {name: idx for idx, name in enumerate(classes)} def voc_to_yolo(xml_file, output_dir): tree ET.parse(xml_file) root tree.getroot() # 读取图片宽高这是归一化计算的基础 size root.find(size) img_width int(size.find(width).text) img_height int(size.find(height).text) # 获取不含扩展名的文件名用于生成同名txt filename Path(xml_file).stem yolo_lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_to_id: # 遇到未在classes.txt中定义的类别直接跳过并打印警告 print(f[警告] {xml_file} 中存在未定义类别: {name}) continue class_id class_to_id[name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # VOC中xmax、ymax是实际最大坐标YOLO需要的是中心点和宽高 x_center ((xmin xmax) / 2) / img_width y_center ((ymin ymax) / 2) / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height # 归一化后的值必须限制在0~1之间偶尔会出现由于标注越界导致的负值 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) width min(max(width, 0.0), 1.0) height min(max(height, 0.0), 1.0) yolo_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if not yolo_lines: print(f[警告] {xml_file} 转换后没有有效目标已跳过) return out_path os.path.join(output_dir, filename .txt) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(yolo_lines)) if __name__ __main__: voc_dir Annotations yolo_dir labels os.makedirs(yolo_dir, exist_okTrue) for xml_file in Path(voc_dir).glob(*.xml): voc_to_yolo(str(xml_file), yolo_dir) print(转换完成)这段脚本里包含了几个我刻意加进去的防护逻辑。第一万一XML里有未在classes.txt中定义的类别不要直接报错中断程序而是打印警告后跳过这样即使某个文件坏了也不会影响整个批量的转换。第二归一化后对坐标值做了裁剪因为有些标注工具有时候会画出超出图片边界的框导致计算出来的宽度和高度出现负值这会在训练时报一个非常难排查的维度错误。第三转换脚本只输出有效目标的txt如果一个XML里没有任何行人目标我会直接把空的txt也省略省得后面训练时加载一堆空标签文件。转换完成后一定要做一次反向抽检。我自己有个笨办法随机挑选10张图把txt里的坐标换算回像素值然后直接用OpenCV在图上画框保存肉眼对比看框的位置是否和原图目标一致。只有抽检全部通过这批标签才算真正合格。3.4 数据集目录组织与训练集划分YOLO系列框架对数据集目录有约定俗成的结构要求虽然不是强制的但遵循官方习惯能少写很多代码。我常用的目录结构是pedestrian_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── classes.txt └── pedestrian.yaml这里有个特别容易犯的错误images/train里放了哪些图片labels/train里必须存在对应的同名txt文件一个都不能少。训练时框架会通过图片路径去找同名标签找不到就跳过错失标注而且不报错。结果就是模型训了半天loss下降得也挺好看但实际很多图根本没参与监督训练。划分数据集时我用的是train_test_split比例通常是7:2:1但划分前要做一个关键操作按场景而不是按文件名随机划分。举例来说同一路段同一时间连拍的100张图如果一部分进训练集一部分进测试集那测试集和训练集高度相似评估结果会虚高。我习惯在采集时就把不同场景的视频抽帧结果分组按组划分这样才更接近真实场景的泛化效果。3.5 YOLO训练配置示例目录和标签都准备好后训练配置文件yaml内容如下path: /path/to/pedestrian_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: personnc表示类别数量names是类别名和ID的映射这里只检测行人一个类别所以是0: person。配置完成后就可以启动训练了。如果你用的是YOLOv5命令行大致是python train.py --data pedestrian.yaml --weights yolov5s.pt --img 640 --epochs 100 --batch-size 16如果你用的是YOLOv8则命令略有变化yolo train datapedestrian.yaml modelyolov8s.pt imgsz640 epochs100 batch16训练过程中的监控指标可以关注mAP0.5和mAP0.5:0.95。对于单一类别的行人检测如果数据质量没问题mAP0.5跑到0.9以上不算夸张但mAP0.5:0.95通常会在0.6到0.8之间浮动这个指标更严格也更考验框的回归精度。4. 常见问题与排查技巧实录格式转换和训练中的真实坑我把自己和身边同事在这些环节里实际踩过的坑做了个整理这里面每一个都很典型不夸张地说我第一次独立做行人检测数据集时至少踩了其中一半。4.1 格式转换阶段的典型问题问题现象可能原因解决办法训练时提示标签为空图片和txt没有同名或XML转换后输出到了错误目录编写脚本检查同名文件缺失情况批量修复检测结果里框的位置偏了归一化时分母写错比如用了某个目标自己的宽高而不是图片宽高回到转换公式检查计算逻辑类别ID彻底错乱classes.txt类别顺序和转换脚本映射不一致统一固定classes.txt一旦确定不再改动训练loss曲线不下降标注框和物体边缘偏差过大或大量难例未标记返工复检标注质量重点看小目标和遮挡目标验证集map很低但训练集很高数据集按文件名随机划分场景重叠度太高按视频序列或场景分组重新划分这里要特别强调一下difficult标签的处理。在VOC格式里difficult1表示这个目标非常难识别通常在VOC的评估里会被忽略掉。但如果你直接转换成YOLO格式YOLO并没有difficult的概念。我的处理方式是如果数据集里difficult目标占比不大就直接当作普通目标转换如果占比很高就先单独抽出来分析确保不是标注质量问题再决定是否剔除。4.2 行人检测数据集的质量隐患除了格式问题数据本身的质量隐患更让人头疼。我遇到过的最隐蔽的问题是重复帧。用视频抽帧方式做数据集时相邻帧非常相似如果训练集、验证集、测试集都包含了同一段视频的连续帧模型就等于“开卷考试”指标很漂亮但放到新场景立刻变差。解决思路我在前面提过按视频段划分数据集是最直接有效的方案。还有一个问题是样本分布不均衡。行人检测数据里远处的行人往往只占几十个像素而近处的行人可能占据半个画面。如果小目标比例过低模型会对小目标非常不敏感。我建议在数据层面补充一些“远景小目标”的样本如果实在找不到可以在训练时对图片做随机裁剪和缩放让模型多看到不同尺度的目标。再有就是漏标。一张人流密集的图片里标注员很容易漏掉一两个被遮挡的行人。漏标对模型的影响比错标更隐蔽因为错标最多让模型学偏漏标则等于告诉模型“这里没有行人”属于一种隐性错误标签。我自己的兜底方法是标注完成后用已经训练好的模型对训练集做一次批量推理把置信度较高的检测框和人工标注框做IoU匹配凡是IoU低于0.3的高置信度框多半就是漏标区域人工复核后补标。这个方法很土但实测非常有效能抓出不少肉眼漏掉的目标。4.3 关于显卡和训练环境的一点说明有不少朋友问过我没有NVIDIA显卡能不能跑YOLO行人检测训练。这个问题我在实战里拆开看过YOLOv5和YOLOv8都支持CPU训练但速度会慢到让人怀疑人生。我自己用笔记本CPU训练一个几百张图片的小数据集一个epoch都要好几分钟更别提完整训练上百个epoch。如果你手头只有核显或AMD显卡短期内可以先做小规模实验和格式验证真正完整的训练还是建议用带NVIDIA CUDA的显卡或者直接用云GPU平台。环境配置方面YOLO本身对依赖版本极其敏感Python版本、PyTorch版本、CUDA工具包版本只要有一处不匹配就会冒出各种奇奇怪怪的报错遇到这种问题优先查看官方仓库的requirements文件按指定版本安装。最后再分享一点我自己的心得体会数据标注这件事看着简单真正做起来特别磨人。我最早做行人检测数据集的时候觉得“不就是框个人嘛”结果标注了500张图之后回看发现第一天的标注质量明显不如后面几天的——手不稳、框不准、漏检多。所以如果你准备做一批自建数据集我强烈建议先拿几十张图试标统一标准后再大规模铺开。格式转换的脚本一定要保留好因为不管是VOC转YOLO还是YOLO转VOC这类需求在项目里几乎必然会反复出现。每当你拿到一个新的公开数据集第一件事就是先跑一遍转换脚本把格式统一到项目内部标准这一步偷懒的话后面每一次训练都要为当时的偷懒买单。希望这些经验能让你少走点弯路。本文还有配套的精品资源点击获取
返回列表