ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VOC与YOLO标注转换指南:634张猪数据集训练YOLOv8全流程

VOC与YOLO标注转换指南:634张猪数据集训练YOLOv8全流程 简介猪只目标检测数据集面向计算机视觉学习者与目标检测算法开发者适用于目标检测、目标识别等模型的训练、验证与效果评估。数据包含634张左右jpg格式猪只图片单张体积约1-500KB画面覆盖多种姿态与场景数据多样。每张图片均配套VOC格式xml标注与YOLO格式txt标注类别统一为pig基于labelImg标注完成解压后无需格式转换即可接入主流检测框架。资源压缩包总大小约229.59MB共1903个文件含634张jpg图片、634份xml标注、634份txt标注及少量说明文件目录按图片、xml与txt分三类存放取用对照方便。已有111人学习下载适合需要现成猪只数据集入门目标检测、进行模型微调或评估精度的开发者可显著节省数据采集与标注时间成本。1. 猪目标检测数据集634张VOCYOLO双格式标注能解决什么问题养猪场智能化改造里猪只检测算法是所有上层应用的底座。存栏盘点要数猪、健康监测要定位猪体、分娩看护要跟踪母猪位置这些都依赖一个先决条件检测模型能从圈舍背景里把猪找出来。模型从哪里来从有标注的数据来。这份约634张的猪数据集同时提供VOC和YOLO两种格式的目标标注XML适合人工查看修改txt适合直接喂给yolov8训练。634张这个规模不够工业级几万张的量但足够跑通yolov8训练自己的数据集的完整流程也足够做方案验证、课程设计和PoC。对新手双格式标注省掉格式迁移的理解成本对熟手小数据集的敏感性能逼你把划分、标注校验这些基本功做扎实。后面的每一章都围绕怎么把这份数据用出最大价值展开。2. 看懂两种目标标注格式VOC的XML与YOLO的txt在定义上的四个不同处理634张猪数据集的第一步不是急着训模型而是先把VOC标注和YOLO标注在结构上的差异搞清楚。两者描述的是同一个目标框但存储方式、坐标基准、类别编码和读取逻辑完全不同。这章把四个差异逐一看透后续转换脚本才不会写错。2.1 VOC的XMLobject节点、bndbox与difficult标记VOC格式以XML文件存储标注信息。解开一个标注文件结构大致是这样的annotation filenamepig_001.jpg/filename size width1280/width height720/height depth3/depth /size object namepig/name difficult0/difficult bndbox xmin256/xmin ymin144/ymin xmax1024/xmax ymax576/ymax /bndbox /object /annotationfilename、size、object、bndbox这四个层级最关键。size下的width和height是后续坐标归一化的分母一旦和实际图片尺寸不一致转出来的YOLO标注就会系统性出错。object节点可以出现多次每头猪对应一个objectbndbox里的xmin、ymin、xmax、ymax是绝对像素坐标数值范围直接受图片分辨率影响。difficult标记在VOC竞赛里表示目标是否难以识别取值0或1。训练时通常会把difficult等于1的框过滤掉因为难样本会影响初版模型的收敛。这份数据里的difficult多为0但转换脚本里保留读取逻辑是更稳的做法万一遇到手工标注时误标成1的框还能在转换阶段决定要不要丢弃。2.2 YOLO的txt归一化坐标与class_id从0开始的约定YOLO格式的标注是纯文本不需要首行说明每行直接对应一个目标框规范格式是五个数值用空格分隔class_id x_center y_center width heightclass_id从0开始。数据集中如果只有pig一个类别那它就该是0如果还分small_pig和big_pig那么classes列表里排第一的类别为0第二个为1。这个顺序跟XML里写的类别名没有必然关系完全取决于你在转换脚本里给classes列表排的顺序。yolov8训练时data.yaml的names顺序必须和这里一致class id才会对应上。四个坐标全部是归一化浮点数。归一化的分母是图片的宽度和高度不是某个固定的目标尺寸。中心点坐标的计算方式是先算像素中心再除以图片尺寸。这样做的好处是标注与图片分辨率解耦换用不同输入尺寸训练时不用重新标注。2.3 从像素坐标到归一化坐标为什么除法顺序很重要一次完整的坐标换算涉及三个步骤算中心点、算宽高、除以图片尺寸。顺序反掉的结果是数值完全不同。正确的递推式是先加后除。拿一个具体例子。1280x720的图片里某头猪的框是xmin320ymin180xmax960ymax540。中心点x(320960)/2640y(180540)/2360框宽960-320640框高540-180360。除以图片宽度1280x_center归一化为0.5除以图片高度720y_center归一化为0.5。宽度、高度也都是0.5。这里翻车的点有三个。第一有人误把x_center直接写成(xmax-xmin)/2再除以width那算出来的是半宽不是中心点。第二width和height在归一化时分母弄反把框的横纵比例搞畸形。第三转换脚本读取img_w和img_h时从size节点取值但xml里size记录的尺寸与实际图片不符时分母错误会扩散到这张图片的所有标注。转换前后的数值边界也值得留心。一个完全落在图片内的框其x_center、y_center、width、height的取值都在0到1之间。如果标注时手滑把框拖出图片边缘换算出的值会超过1训练时yolo损失函数会把这个框当异常样本处理造成loss波动。第5章会专门讲如何把这些脏框提前过滤掉。3. 动手转换把VOC标注改成YOLO格式的脚本与目录对齐了解了格式差异之后进入实际动手环节。转换脚本是这份数据集从VOC走向yolov8的关键桥梁。这章给出一个可以直接运行的python脚本同时说明目录结构怎么布局、转换完如何确认结果正确。3.1 转换脚本解析XML并写出归一化txt常见做法是写一个独立的python脚本放在数据集根目录下运行。脚本核心逻辑分五步读XML、取图片尺寸、遍历object、换算坐标、写txt。以下是可用版本import xml.etree.ElementTree as ET import os CLASSES [pig] def voc_to_yolo(xml_path, out_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) if img_w 0 or img_h 0: raise ValueError(fbad image size in {xml_path}) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: continue cls_id CLASSES.index(name) 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) # clamp避免框超出图片边界 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) # 过滤宽高为0或反向的无效框 if xmax xmin or ymax ymin: print(f[skip invalid box] {xml_path} {name}) continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h x_center round(x_center, 6) y_center round(y_center, 6) box_w round(box_w, 6) box_h round(box_h, 6) lines.append(f{cls_id} {x_center} {y_center} {box_w} {box_h}) if not lines: print(f[no valid objects] {xml_path}) return os.makedirs(out_dir, exist_okTrue) base os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, base .txt), w) as f: f.write(\n.join(lines) \n)代码里CLASSES列表是最重要的开关顺序就是类别ID。当前只有pig一类所以所有框的class_id都是0。后续如果要把猪分成大猪小猪直接改CLASSES列表并保证data.yaml里的names顺序完全一致。坐标精度保留6位小数对yolov8训练来说足够。yolov8默认把输入缩放到640x6406位小数对应的误差远小于一个像素再多保留几位也没有实际意义。clamp这一步不能省略它负责兜住标注拖拽出界的脏框避免脏数据一路流到训练阶段。3.2 目录结构images和labels必须保持同名前缀yolov8训练时读取数据集的默认方式不是靠配置文件里的索引而是靠目录结构。常见做法是这样布局datasets/ images/ train/ train_001.jpg val/ val_001.jpg labels/ train/ train_001.txt val/ val_001.txtimages和labels是两个根目录下面的train和val子目录名称必须完全一致。图片文件和标注文件只靠文件名关联——train_001.jpg对应train_001.txt中间没有任何辅助索引。这就是该数据存储格式的约定简单但容错率低。从平铺的Annotations目录转成上面的两层结构需要做目录移动和划分。保守做法是让转换脚本输出到一个raw_labels目录完成校验后再按后续的划分结果分别移动到labels/train和labels/val下。移动之前一定先跑一遍校验脚本确认每个txt都存在且内容合法避免把脏数据挪进正式目录。3.3 转换结果抽样校验画框检查最直观脚本跑完之后不要急着删VOC标注。先抽三五个txt对照原图画框检查。画框用OpenCV几行代码就能完成import cv2 def draw_yolo_boxes(image_path, label_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path) as f: for line in f: parts line.split() cls_id int(parts[0]) xc, yc, bw, bh map(float, parts[1:]) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imshow(check, img) cv2.waitKey(0)抽样逻辑建议覆盖三类图片单猪图、多猪密集图、暗光环境下拍摄的图。三类都画框正常说明转换脚本在这个数据集的代表性场景上没有系统性偏差。如果只在密集图中发现框错位多半是某个XML里多个object的顺序或者坐标字段读反了。用OpenCV画框只是为了校验不要求跟标注工具长得一样重点看框与猪身体的贴合度。框紧贴身体、不出图片边界、类别名正确这条VOC到YOLO的链路就可以放心交给训练脚本了。4. 634张猪数据的体检与划分类别统计、分布分析和训练验证分裂转换完成后会得到一份干净的、yolov8能直接消费的数据集。但634张这个量级还存在两个隐患类别样本不平衡、划分不当导致验证结果失真。这章先做统计体检再讲划分策略最后给出增强参数的取舍。4.1 用脚本统计框数了解每张图的平均猪头数先别急着训练。用脚本扫一遍所有YOLO格式的txt统计每张图的框数和总框数。这一步虽然简单却能回答一个重要问题634张图片里到底有多少个猪目标这直接决定模型容量和预期精度。import os import glob def stat_labels(label_dir): total_boxes 0 empty_files 0 file_count 0 per_class {} for path in glob.glob(os.path.join(label_dir, *.txt)): file_count 1 with open(path) as f: lines [ln for ln in f if ln.strip()] if not lines: empty_files 1 total_boxes len(lines) for ln in lines: cls_id int(ln.split()[0]) per_class[cls_id] per_class.get(cls_id, 0) 1 print(ffiles: {file_count}) print(ftotal boxes: {total_boxes}) print(fempty files: {empty_files}) print(fper class: {per_class})如果总框数在3000到4000之间说明平均每张图有5到6头猪属于密度适中的圈舍场景。如果总框数不足1000模型在密集场景下漏检会比较严重评估时就该把漏检单独统计而不是只盯mAP。如果文件数接近634但empty_files很多说明部分XML里object节点为空或在转换时被过滤掉了需要回到VOC原标注确认这些空图要不要保留。4.2 训练/验证/测试划分按场景分组避免数据泄漏634张图片直接用random split会带来数据泄漏问题。猪舍监控视频抽帧数据里相邻帧的背景几乎相同猪的姿态变化也小。如果同一视频的帧同时进入训练集和验证集模型相当于开卷考试验证mAP会虚高到不真实。常见做法是按拍摄批次或视频来源分组。假设这批图片来自若干个不同的视频文件或拍摄时间片断先按来源分组再决定归属。如果来源信息不完整退一步用文件名前缀分组。原则是同一场景的图片宁可全进训练集也不要被拆散到两边。划分比例上634张我会按75%/15%/10%来切约475张训练、95张验证、64张测试。验证集不需要太大但每一类都要有代表性样本。划分脚本里加一个按class_id的统计检查确保验证集里每个类至少出现5个框否则这一类的验证loss会成为一个无法解释的黑匣子你不知道是模型没学会还是压根没样本。4.3 数据增强mosaic、HSV抖动和letterbox的取舍634张原图训练yolov8必须开增强但增强参数要跟着场景调整。猪舍场景中猪只常有局部遮挡yolov8默认开启的mosaic增强把四张图拼成一张能模拟更多遮挡排列对小数据集很友好。但mosaic也会让小目标变得更小如果数据集中存在较多远距离小猪建议把mosaic比例从默认的1.0降到0.5左右。颜色增强方面建议保守。hsv_h、hsv_s、hsv_v在yolov8的默认值分别是0.015、0.7、0.4。猪舍图片本身就是暖色调灯光hsv_v加得太大容易让颜色失真模型学到的颜色特征偏离真实场景。可以先只把hsv_v降到0.2观察两三个epoch的验证loss趋势再决定要不要继续调。letterbox处理在训练和推理时都要留意。yolov8默认把输入缩放到640x640并保持原始宽高比多余部分填充灰边。如果猪圈图片多为1280x720宽幅缩放后猪在整体画面中的占比较小框的相对尺寸也被压缩。此时可以尝试imgsz960把小猪目标放大后再训。显存占用会上升但漏检率往往能明显下降这笔交换在少样本场景下通常是划算的。5. 常见问题排查标注校验与yolov8训练踩坑记录这一章专门记录我在使用634张左右规模的猪数据集时实际遇见的坑。它们不会在训练启动时报错但会以各种隐蔽的方式降低模型性能。每条都按现象、原因、解决来写你可以直接对照排查。5.1 现象xml和jpg数量对不上转换前做统计发现Annotations里有621个xmlJPEGImages里有634张jpg。差的13个是数据采集过程中的历史遗留某几张jpg因文件损坏被删除但xml没有同步清理或者另一些图片拍糊了被标注人员跳过没生成对应的VOC文件。解决用脚本以文件名为基准做集合差异运算找出多出来的XML和缺标注的JPG分别处理。ls JPEGImages | sed s/.jpg$// | sort imgs.txt ls Annotations | sed s/.xml$// | sort anns.txt echo missing xml:; comm -23 imgs.txt anns.txt echo missing jpg:; comm -13 imgs.txt anns.txt两个comm命令分别得到两类差异。缺xml的jpg从数据集里移出缺jpg的xml也删掉因为训练时没有图片可配。更稳妥的做法是在转换脚本里增加一个数量校验阶段发现不一致就直接报错停住不要带病继续。5.2 现象坐标越界与宽高为0的bboxyolov8训练过程中loss出现异常尖峰或者某个epoch的验证mAP突然掉到接近0。用脚本扫描labels目录发现某些txt里出现了大于1或小于0的归一化坐标甚至width或height为0。这些脏框来自标注工具拖拽出界的误操作或者图片在预处理时被裁剪但标注没同步更新。解决在转换脚本里加clamp和有效性判断坐标值被限制在图片尺寸范围内xmax不大于xmin、ymax不大于ymin的框直接跳过。如果训练前才发现脏框写一个过滤脚本扫描并删除非法行def clean_label(path): valid [] with open(path) as f: for line in f: parts line.split() if len(parts) ! 5: continue vals [float(v) for v in parts[1:]] if all(0 v 1 for v in vals): valid.append(line.strip()) with open(path, w) as f: f.write(\n.join(valid))5.3 现象类别ID错位导致预测全错训练结束后验证发现框的位置都正确但每头猪都被预测成同一个错误类别。原因十有八九是转换脚本里的CLASSES顺序和data.yaml的names顺序不一致。比如classes列表里pig排第0而data.yaml里写的是names: [background, pig]于是id 0被模型当成了背景类。解决统一classes的排列并在训练前打印yaml内容逐一核对。yolov8训练日志每个epoch会输出每个类别的AP如果某个类的AP一直为0且其他类数值异常优先怀疑ID错位而不是模型结构。5.4 现象文件名大小写不一致导致图片不加载在Windows上用LabelImg标注时文件名是Pig_001.JPG放到Linux服务器上训练脚本按jpg后缀去索引Pig_001.JPG被漏掉。或者txt文件名是pig_001.txt图片是Pig_001.jpg大小写不一致导致yolov8读不到对应的标注文件训练样本数悄悄减少。解决转换前把所有文件名统一为小写bash一行完成for f in images/* labels/*; do mv $f $(echo $f | tr A-Z a-z); done统一后再跑一次文件名校验脚本确保同名前缀的数量与图片数一致。这个坑在Windows标注、Linux训练的工作流里出现频率极高值得养成习惯。5.5 现象灰度图像混入RGB训练集部分监控设备在夜间输出灰度视频抽帧后混进了数据集。yolov8的输入层是3通道训练时遇到单通道图片虽然不一定报错但模型特征提取会丢失颜色信息导致白天数据的泛化明显变差。解决在数据准备阶段扫描所有图片的通道数把灰度图转换为三通道RGB后再加入训练集。也可以直接丢弃灰度图前提是丢弃的数量不多且不影响类别分布。转换脚本可以这样写from PIL import Image import os def convert_gray_to_rgb(img_dir): for f in os.listdir(img_dir): path os.path.join(img_dir, f) img Image.open(path) if img.mode ! RGB: img.convert(RGB).save(path)5.6 现象yolo损失函数波动剧烈但mAP停滞小数据集训练常见的另一个现象是训练loss在下降val loss却上下波动mAP50也没明显增长。这往往是增强参数过强、batch size过小、初始学习率偏高三者叠加的结果不一定是指标体系出了问题。解决先把hsv增强参数调低再看batch size是否小于8最后检查初始学习率是否超过0.01。逐一排查之后通常能稳住训练曲线。小数据集的训练问题大多出在标注和增强上而不是模型结构本身玄学。6. 用634张跑通yolov8训练全流程命令、参数与最终核验数据干净、划分完成后接下来就是用yolov8把模型跑出来。634张这个规模撑不起大模型所以优先用yolov8n跑通后再决定要不要升级到yolov8s。6.1 初版训练命令与参数设定yolov8训练参数要围绕小数据、少epoch、重验证来设。以下是我在这个数据集上的开局命令yolo detect train \ datadatasets/pig.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ save_period10 \ device0 \ projectruns/pig_exppatience20表示20个epoch内val指标没有进步就提前停止避免小数据集过拟合后继续空转。save_period10定期保存中间权重防止某个epoch崩溃丢失进度。batch在显存允许范围内尽量取大batch16时BN层的统计更稳定loss曲线更平滑。6.2 可视化核验与阈值调节训练结束后用best.pt做预测保存带框图片。我习惯把conf从0.25降到0.1再看一遍区分哪些漏检是阈值过滤掉的、哪些是模型真正找不到的。如果降阈值后能找回大量正确框说明模型本身有检出能力只是默认阈值偏严部署时把conf调到0.15即可。如果降阈值后找回的框大多为重复框或误检说明模型存在过拟合需要从增强幅度和正则化方面往回调。634张规模训出的模型在真实猪舍里跑漏检比误检更值得关注——漏掉的猪影响存栏数统计而误检通常能在后处理里滤掉。6.3 进阶做法预训练权重finetune与部署验证634张数据从头训不是最优选择。yolov8n.pt自带的COCO预训练权重已经包含通用物体特征在猪数据上finetune比随机初始化收敛快得多同样epoch数下mAP50能高出5到8个百分点。训练完需要部署时用yolo export导出onnx格式再结合推理引擎测单张耗时就能评估整条链路的实时性。这套从格式转换、数据划分、训练调参到可视化核验的流程是我做过几轮猪数据项目后固定下来的工作流。先把标注格式吃透、划分做干净再用小模型跑通全流程最后才考虑模型规模和部署优化。634张不算大但这条链路踩过的坑在几万张数据上还会以相似的方式再来一遍。希望帮到你。本文还有配套的精品资源点击获取
返回列表