目标检测数据集制作实战:从采集标注到VOC/YOLO双格式转换

目标检测数据集制作实战:从采集标注到VOC/YOLO双格式转换 简介这是一份面向高尔夫运动场景的目标检测数据集适合希望在球类检测、运动动作分析或体育视频理解方向训练的开发者与研究者。数据集包含13134张经图像增强处理的JPEG图像覆盖高尔夫球、球杆手柄、球头三个类别分别标注了11029个、12193个和10191个边界框总计33413个有效检测框。所有标注均采用labelImg工具完成以标准矩形框定位目标同时提供Pascal VOC格式的XML与YOLO格式的TXT两种严格对齐的标注文件可直接用于YOLOv5/v8、Faster R-CNN、SSD等主流检测框架也方便做格式迁移或模型对比。资源包共3个文件压缩包大小约3KB内部以inscode页面、说明html及gitignore文件为主用于快速展示数据集结构与使用说明。目前已有73人学习浏览适合需要标准双格式标注、用于模型评估或算法验证的中级及以上目标检测学习者。 做高尔夫相关视觉项目这段时间最头疼的不是模型选型而是数据集。高尔夫球、球杆手柄、球头这三类目标在公开数据集里几乎没有现成可选项而项目里又确实需要同时把球和杆部部件检测出来才能继续做后续的挥杆动作分析。后来我按项目需求整理了一份三类别目标检测数据集最终包含13134张图含增强并同步导出VOC与YOLO双格式整个流程从原始图像到可训练格式全部跑通。这里把从采集、标注、增强到格式转换的完整经验写下来给正在做体育场景检测、或者想自己做一套目标检测数据集的朋友当个参考。1. 项目概述为什么是这三类目标1.1 场景需求与检测难点高尔夫挥杆分析、球场辅助拍摄、球童机器人这类项目目标检测的输入其实很固定画面里可能会出现一个或多个高尔夫球、球员手中握着球杆而球杆上真正需要识别的是手柄和球头两个部件。为什么不是检测整根球杆因为整根球杆在画面中是一条长长的线段轴对齐矩形框很难把它框住而且杆身对后续姿态判断没有直接贡献反而会引入大量无效框。只检测球杆手柄和球头配合关键点或者角度计算就能反推出球杆的朝向和挥杆轨迹这是更务实的方案。难点也集中在这三类目标上。高尔夫球直径很小在1080p画面里往往只有二三十个像素属于典型的小目标球杆手柄和球头则是长条状或异形物体随着挥杆动作在画面中倾斜、旋转框的宽高比变化剧烈再加上球场背景里的草地纹理、天空、树木、沙坑干扰信息非常多。这些问题如果不从数据层面解决后面换什么模型都很被动。1.2 数据集的构成与核心参数这份数据集最终包含13134张图像全部完成了三类目标的实例级标注类别为golf_ball、grip、head。原始有效标注图约3500张按照8:1:1切分为训练集、验证集、测试集其中对训练集进行数据增强扩充最终训练集约11700张加上约700张验证集和约700张测试集总计13134张。验证集和测试集保持原始样本分布不做增强这样评估出来的指标更接近真实场景。格式上同时提供VOC与YOLO双格式VOC格式是每个图像对应一个XML文件适合接入MMDetection、Detectron2这类框架YOLO格式是每个图像对应一个TXT文件YOLOv5、YOLOv8、YOLOv11等系列可以直接训练。两类格式由同一份标注数据转换而来不会出现两边坐标对不上的问题。2. 数据采集与标注数据集质量的地基2.1 采集筛选与样本分布数据来源主要是两类一类是人工拍摄的真实球场画面包括不同时段、不同球场、不同天气下的图像另一类是公开体育视频片段通过抽帧得到。采集阶段最容易犯的错是只顾数量不顾质量——画面模糊、球被严重遮挡、远景中球只有几个像素这类图像删掉比留着更有价值。我最终保留3500张左右的有效标注图实际采集和抽帧的量大概是这个数的三倍以上。筛选时还要看类别分布。如果一组画面全是静态摆拍球和球杆都很大很清晰训练出来的模型一到比赛场景就失灵。所以我手动控制了三类目标的分布球类实例尽量涵盖远距离小球、中距离清晰球、运动中略微模糊的球手柄和球头则涵盖不同握杆角度、不同挥杆阶段、正对镜头和侧对镜头的画面。标注完成后的实例总数大约为球类5200个、手柄类4100个、球头类4000个整体没有出现某一类碾压其他类的情况。2.2 三类目标的标注规范标注规范直接决定数据能不能用。我给这套数据定了三条硬性标准高尔夫球只要球体边缘可见超过50%就标注目标区域取能包含完整球体的最小矩形严重遮挡、运动模糊到看不出球体轮廓的不标。球杆手柄从握把最顶端开始到握把渐变纹理结束的位置结束只标握把区域不包含杆身。球头整个杆头区域包括杆面、杆底、杆颈但不包含杆身。杆身全程不标注避免制造大量重叠框。这套标准看起来简单实际执行时争议点非常多。比如球杆横在画面里、手柄和球头都在由于轴对齐矩形框的特性手柄的框很容易把一小段杆身也包进去球头在击球瞬间朝向镜头时投影面积变得很小此时按什么范围标注。我的处理原则是矩形框尽量贴合目标的实际外接矩形宁可框得略紧不要框得偏松让模型自己去学习目标边界。实测下来按这个规范标注的数据训练出的模型预测框的边界明显更干净NMS后误检也少。2.3 标注工具的选择与批量流程工具我试过LabelImg、X-AnyLabeling和Label Studio。LabelImg轻量、启动快适合个人小批量标注X-AnyLabeling内置了SAM和YOLO预标注模型可以用粗标注的框先跑一遍自动标注再人工修正效率提升非常明显Label Studio适合多人协同但配置稍重。最终我选择X-AnyLabeling走完整个标注流程导出时选VOC格式。批量标注时有一个关键细节先把所有图像做一次预处理统一方向、统一尺寸。手机相机拍出来的JPG经常带EXIF旋转信息如果不先把图像转正标注坐标和图像内容会对不上后面训练时模型看到的图和标注框不匹配活活把数据集做废。用PIL处理一行代码就能解决from PIL import Image, ImageOps img Image.open(src_path) img ImageOps.exif_transpose(img) # 根据EXIF信息转正 img img.convert(RGB)3. 数据增强从几千张到13134张的扩充过程3.1 增强策略与参数选择原始3500张图直接训练模型在小目标球类上的召回率很难看所以要对训练集做增强。我用的增强策略分四种水平翻转、HSV颜色扰动、随机旋转、随机裁剪缩放。其中水平翻转生成量和原图1:1HSV扰动选约40%的训练图随机旋转和随机裁剪缩放各占一部分叠加后把训练集扩充到约11700张。参数选择上有几个实测结论水平翻转对高尔夫场景安全因为球的对称性极强手柄和球头翻转后语义不变。随机旋转角度控制在±15度以内超过后手柄和球头的宽高比会失真而且球杆在画面中的自然倾斜角度本身就不会太多。HSV扰动建议H范围±5S和V范围±20拉太大会让球的颜色失真训练时模型会去学球面上的错误纹理。Mosaic增强我没有用。高尔夫球本身是小目标Mosaic把四张图拼起来后球变得更小标签类别分布也容易出现空图收益不稳定。3.2 增强的生成流程与注意事项增强不能只做图像变换标签必须跟着图一起变。确保增强后的标签不越界、不丢失是整个流程里最容易被忽略的地方。每次增强操作都做两件事一是对标注框做相同的几何变换二是旋转或裁剪后重新计算框的坐标并丢弃面积小于原框20%或者完全越界的框。生成流程总结为一个脚本循环读图、读标签、做随机变换、写增强图、写增强标签。跑完后直接统计每张增强图的实例数量把异常结果挑出来人工检查一遍。实测最常出现的问题是旋转后球的标注框中心点在图像外但框边缘还留在画面里导致这个框变成半裁切状态模型训练时会学到一个半个球也要完整框住的错误先验。所以我在脚本里对旋转后的框做了一次严格过滤中心点不在图内就丢弃该实例宁可样本量少一点不让脏标签混进去。4. VOC与YOLO双格式一份标注适配主流框架4.1 两种格式的结构差异VOC和YOLO格式差异很大但很多初学者会在这里绕弯子。VOC格式用XML存储信息结构大致是根节点annotation下包含folder、filename、size其中size里有图像的width和height每个object节点里是name和bndboxbndbox的四个值是xmin、ymin、xmax、ymax单位是像素坐标是绝对坐标。YOLO格式则是一个TXT文件每行表示一个目标格式为class_id x_center y_center width height其中class_id是整数从0开始x_center、y_center、width、height都是归一化浮点数范围0到1归一化的分母就是图像的宽和对应的像素宽高。一句话总结差异VOC是绝对像素、轴对齐矩形的四角坐标YOLO是相对归一化、矩形中心的宽高坐标。转换的本质就是把四角坐标换算成中心坐标和宽高再除以图像宽高。4.2 VOC转YOLO的转换实现我这里直接给一个可用的转换脚本输入是VOC格式XML文件夹输出是YOLO格式TXT文件夹import xml.etree.ElementTree as ET import os classes [golf_ball, grip, head] # 顺序决定class_id def voc_to_yolo(xml_path, output_dir): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) bbox obj.find(bndbox) xmin int(float(bbox.find(xmin).text)) ymin int(float(bbox.find(ymin).text)) xmax int(float(bbox.find(xmax).text)) ymax int(float(bbox.find(ymax).text)) # 边界保护防止越界 xmin max(0, min(xmin, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) xmax max(0, min(xmax, img_w - 1)) ymax max(0, min(ymax, img_h - 1)) if xmax xmin or ymax ymin: continue x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(output_dir, out_name), w) as f: f.write(\n.join(lines))这段代码里有两个容易踩的细节一是分母一定用当前图像的实际宽高不能统一用640或某个固定值二是坐标要做边界保护因为有些标注工具在图像边缘标注时坐标可能比图像尺寸大1像素或小1像素。4.3 转换后的校验与常见坑转换完成后一定不要直接拿去训练先做一轮校验。最有效的校验方式是可视化随机抽几十张图把TXT里的坐标画回到图上确认框和物体对得上。这一步能发现绝大多数问题。再配合一个手动检查清单检查是不是所有图像都有对应TXT漏掉的文件会导致训练时报found no labels。检查class_id是否在类别范围内常见错误是把grip写成3超出索引直接崩溃。检查归一化值是否在0到1之间出现大于1的值说明分母用错了。检查是否存在空TXT文件如果是无目标的图建议直接把对应图像移出数据集避免训练时影响batch分布。VOC格式转出后如果要用MMDetection还有一个额外坑MMDetection默认读取VOC时要求文件名、XML文件名和图像路径里的文件名保持一致而且XML中的folder字段最好和实际路径一致否则会报找不到图片。我第一版转换时没注意folder字段结果MMDetection加载数据时各种路径错乱后来写脚本统一重写folder和filename字段才解决。5. 基于YOLOv8的三类目标训练实战5.1 数据集目录组织与配置老规矩先看目录结构。YOLOv8要求的数据集目录应该是这样的datasets/ golf/ images/ train/ val/ test/ labels/ train/ val/ test/images和labels下的文件名要一一对应图片和标签的主文件名完全一致只要扩展名不同即可。然后写一个data.yamlpath: datasets/golf train: images/train val: images/val test: images/test names: 0: golf_ball 1: grip 2: head这里names的索引顺序必须和VOC转YOLO时classes列表的顺序一致否则类别就全乱了。我处理数据时一度调整过类别顺序忘记同步改TXT训练时发现球头被当成手柄整个模型的类别语义全错。5.2 训练参数与效果对比我用YOLOv8s作为基础模型输入分辨率640batch size 16训练100轮采用默认的SGD优化器设置workers8。如果显存不够batch可以降到8但学习率要同步调低一点否则收敛不稳定。关键参数还有一个close_mosaic。YOLOv8在最后10个epoch会自动关闭Mosaic增强因为我没有用Mosaic所以这个参数不用动。对于小目标建议把训练和推理的imgsz提到800或960效果提升比换模型还明显。训练完成后我对同一份测试集做了对比指标仅原始3500张增强后13134张mAP500.8610.918mAP50-950.7060.781球类召回率0.7930.905手柄召回率0.8420.911球头召回率0.8560.924增强对三类的提升都很明显尤其是球类小目标召回率从79.3%提升到90.5%说明扩充后的数据确实解决了小目标样本不足的问题。测试集是保持原始状态的350张图没有参与增强所以这个对比是可信的。训练完如果需要部署导出ONNX也很简单一条命令yolo export modelbest.pt formatonnx imgsz640导出后可以在C工程里用ONNX Runtime加载走标准的预处理、推理、后处理流程。这部分后续如果想聊可以专门展开一篇。6. 实操中的常见问题与排查记录6.1 小目标球类漏检怎么办漏检球的根源是训练样本里小尺寸实例不够。即便做了整体增强如果原图里球普遍比较大增强后依然大球偏多。我的处理方式是把增强集中倾斜到小目标样本对包含小球的图做随机裁剪并放大让小球变得更清晰、更大对不包含小球的图做整体下采样在图像层面制造小球效果。两种操作交替明显改善了球类的召回。另一个有效手段是加大推理分辨率。640分辨率下28像素的球只占特征图上的一个点附近特征信息很少换到960分辨率后同样的球在特征图上占据的区域变大漏检率显著下降。实测在960下球类mAP50又涨了约1.5个点。6.2 斜向杆件部件框不稳定手柄和球头在挥杆过程中角度变化大特别是球头在击球瞬间会发生快速旋转标注框的宽高比在0.3到3之间跳动。模型预测时容易在同一目标上产生多个重叠框或者框的方向不对导致IoU对不上。排查下来发现标注不统一是最大因素有些标注员习惯把手柄标成细长矩形有些习惯标得松一点包住握把周围的空间模型学到的目标边界就变成两边都行预测自然不稳定。解决方法是回头统一标注规范把所有手柄框改成紧贴握把边缘球头框统一包含杆颈但不包含杆身。重训后同一目标的多框现象明显减少。6.3 增强后样本分布失衡做增强时不能只盯着总张数还要看类别实例数。我有一次增强完发现总张数超过13000但手柄和球头的实例比例相较球类有所下降因为部分增强操作把细长柄部裁出了画面。后来我在增强脚本里加入类别数量统计每次生成一批后检查三类实例数的比例如果某类低于预期就针对性增加该类保留率高的增强操作比如对包含手柄的图少做随机裁剪多做HSV扰动。最后再分享一个小技巧数据集的命名和版本管理要趁早做。这套数据从第一批标注到现在经历了四轮修正每次修正都要重新生成TXT和可视化校验图。如果一开始就在文件名里带上版本号并且用目录区分原始图、增强图、导出格式后面排查问题会省很多时间。说到底目标检测项目里模型只是最后一个环节数据集的质量和格式规范才是决定上限的部分。希望这份实操记录能帮你少走一些数据层面的弯路。本文还有配套的精品资源点击获取