ARTICLE DETAIL

资讯详情

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

熊猫数据集标注指南:VOC与YOLO格式转换避坑全流程

熊猫数据集标注指南:VOC与YOLO格式转换避坑全流程 简介面向目标检测入门与实战的熊猫检测数据集采用VOC与YOLO两种主流标注格式共217张左右jpg图片及对应xml、txt标注文件适合训练YOLO系列或Faster R-CNN等检测模型也可用于课程设计与算法验证。压缩包内包含jpg图片、xml标注、txt标注三个文件夹解压后即可直接配合labelImg查看标注或开始训练标注类别统一为panda遵循框选准确、目标完整、一致校验原则数据质量较有保障。资源共652个文件其中txt标注218个、xml标注217个、jpg图片217个整体90.97MB体量轻便便于快速下载与迭代实验。已有99人学习使用文件夹结构清晰可作为小规模目标检测教学、迁移学习或检测算法对比测试的基础数据集。1. 217张熊猫图的双格式标注做目标检测的第一道坎不在模型拿到一批“熊猫数据集 VOC和yolo格式目标标注217张左右”的素材第一反应别急着找模型先把格式理清楚。VOC格式用XML记录目标框YOLO格式用txt记录归一化坐标两种格式各有生态而主流训练脚本只认YOLO的txt。这217张图说多不多说少不少正好能验证一套“选图→整理→标注→转格式→训练前检查”的流程能不能跑通同时它又足够小任何格式错误都会被放大成训练事故。这篇文章就把这套熊猫小数据集从零到训练前检查完整过一遍顺手把坐标越界、空标签、图片旋转这些坑全部填平。适合正在攒自定义数据集的工程师和研究生照着复现。2. 先把217张图收拢成标准目录选图标准与训练验证划分2.1 选图标准别让烂图浪费你的标注时间目标标注这件事图的底子决定了后续所有工作的上限。217张熊猫图如果里面有大量模糊、严重遮挡、目标占比过小的图标注出来也是废标签。我一般会先按三个维度筛一遍。分辨率维度单边低于640像素的图直接淘汰或者统一resize到640以上。YOLO训练时会把输入resize到640x640原图如果只有300x300缩放后熊猫的纹理细节基本丢光小目标更是一个都检不出来。目标占比维度熊猫主体在画面里占的比例最好在5%到60%之间。小于5%意味着目标只有几十个像素标注框又小又容易偏训练时模型很难学到有效特征大于60%则会让模型对“大熊猫”过拟合换一张带场景的图就翻车。背景多样性维度纯棚拍、单色背景的图不要超过三分之一。熊猫检测的难点是“在环境里找到熊猫”如果训练集全是白底大头照验证集放到竹林雪地场景里模型基本是睁眼瞎。遮挡和运动模糊也要单独过一遍。217张图规模不大宁可砍到180张全是干净的也不要为了凑数把模糊图塞进去。标注一个糊框比不标更坑因为模型会拿这个错误框当学习样本。2.2 目录结构VOC和YOLO两种布局一次建好格式转换的前提是目录规范。VOC格式的标准布局是JPEGImages放原图、Annotations放XML、ImageSets/Main放训练验证文件名列表YOLO格式则是images和labels分训练验证两份。两种布局在同一个数据集根目录下并列存在转换脚本只管读和写互不干扰。# 数据根目录 mkdir -p dataset/VOC2007/{JPEGImages,Annotations,ImageSets/Main} mkdir -p dataset/yolo/{images/{train,val},labels/{train,val}}这段命令创建两套目录骨架。VOC2007是历史习惯沿用JPEGImages里统一放jpg后缀的图片Annotations里每个xml同名前缀对应一张图ImageSets/Main下的train.txt和val.txt只写文件名不带后缀。yolo目录下再按train/val切一次labels里每张图对应一个txt文件。目录分开的好处是转换脚本可以只在两个根之间搬数据不会把原图路径搞乱。图片文件名建议统一成“类别_序号”形式比如panda_001.jpg。不要用微信截图那种乱七八糟的命名也不要夹带中文和空格。YOLO的标签文件和图片严格同前缀只要命名规范后面检查对应关系能省一大半功夫。2.3 训练验证划分217张怎么切不肉疼小数据集的划分要讲究。常规8:2切分217张里留43张验证训练174张对单类检测其实够用但前提是两个集合的难度要均衡。import os import random from glob import glob random.seed(42) img_paths glob(dataset/VOC2007/JPEGImages/*.jpg) img_paths.sort() random.shuffle(img_paths) val_count int(len(img_paths) * 0.2) val_list img_paths[:val_count] train_list img_paths[val_count:] def write_name_list(path, paths): with open(path, w) as f: for p in paths: name os.path.splitext(os.path.basename(p))[0] f.write(name \n) write_name_list(dataset/VOC2007/ImageSets/Main/train.txt, train_list) write_name_list(dataset/VOC2007/ImageSets/Main/val.txt, val_list) print(ftrain: {len(train_list)}, val: {len(val_list)})用固定随机种子保证每次划分结果一致这是能复现的第一前提。val_count取整后是43张train有174张。写出来的txt只有文件名不带后缀VOC的train.txt就是给转换脚本做遍历用的不直接给YOLO训练用。这里有个实战习惯先洗牌再切而不是按文件名字典序硬切。否则前217张如果是同一个拍摄批次训练集和验证集的场景分布就偏了。切完之后肉眼扫一遍验证集清单确认里面既有近距离大熊猫也有远景小目标分布合理再往下走。提示如果某张图里同时出现两只熊猫不要为了省事只标一只。217张小数据集里多目标样本是黄金数据能帮模型学会区分个体和重叠。3. VOC和YOLO格式互转归一化坐标转换脚本与边界坑3.1 两种格式到底差在哪VOC的XML把目标信息写得非常啰嗦它记录文件名、路径、图片宽高、每个目标的类别名和四个绝对像素坐标坐标原点在左上角x轴向右y轴向下。这种格式的好处是可读性强一个框是错是正打开XML一眼就能看出来。YOLO的txt则极简每行五个数字第一个是类别索引后四个是归一化后的中心点x、中心点y、框宽、框高全部除以图片宽高值域在0到1之间。为什么训练要用YOLO格式因为YOLO系列模型的输入是一张定尺寸的图损失函数计算时对目标框做的是相对坐标的回归归一化坐标天然适配。而VOC的XML是标注工具常用的输出格式人工复核和修改更方便。所以常见做法是先用labelImg标成VOC XML再用脚本统一转成YOLO txt。XML留底txt训练两边都不耽误。转换逻辑的核心就是四步解析XML拿到图片宽高和bndbox坐标把(xmin, ymin, xmax, ymax)换成中心点加宽高四分量全部除以图片宽高按“类别索引 归一化坐标”的格式写进txt。看起来简单实际转换时有一堆边界情况要处理。3.2 转换脚本一次跑通这套熊猫数据import xml.etree.ElementTree as ET import os from glob import glob classes [panda] # 类别表索引从0开始 def convert_annotation(xml_path, out_txt): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size).find(width).text) img_h int(root.find(size).find(height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue cls_id classes.index(cls) 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) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 兜底坐标超界时裁剪到[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) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines) (\n if lines else )) # 按VOC的train/val划分文件遍历 for split in [train, val]: txt_path fdataset/VOC2007/ImageSets/Main/{split}.txt if not os.path.exists(txt_path): continue with open(txt_path) as f: names [line.strip() for line in f if line.strip()] os.makedirs(fdataset/yolo/labels/{split}, exist_okTrue) for name in names: xml_file fdataset/VOC2007/Annotations/{name}.xml out_file fdataset/yolo/labels/{split}/{name}.txt if os.path.exists(xml_file): convert_annotation(xml_file, out_file) # 同步拷图片 import shutil for split in [train, val]: with open(fdataset/VOC2007/ImageSets/Main/{split}.txt) as f: names [line.strip() for line in f if line.strip()] for name in names: for ext in [.jpg, .jpeg, .png]: src fdataset/VOC2007/JPEGImages/{name}{ext} if os.path.exists(src): shutil.copy(src, fdataset/yolo/images/{split}/{name}{ext}) break print(convert done)classes列表的顺序决定了YOLO的类别索引。单类数据集只有一个panda索引固定为0后续训练配置里的nc参数写1就行。如果以后要加“品种”之类的第二类一定先扩类别表再重新转换顺序一乱索引全对不上。bndbox里的四个值是字符串必须先转float再做除法。保留6位小数足够YOLO使用写太多位反而让txt体积变大。空目标文件也要写入一个空文件YOLO训练时会跳过空文件但缺失文件会被视为数据异常。图片拷贝那里做了扩展名兜底jpg和png混存的目录也能一次处理干净。转换完成后用wc -l dataset/yolo/labels/train/*.txt抽查几个文件行数应该和XML里的object数量一致这是最直接的验收方法。3.3 转换时的四个边界坑第一个坑是坐标越界。标注时手滑把xmax拉出图片边界归一化后会出现大于1的值训练时yolo损失函数计算会直接产出错误结果甚至NaN。上面的脚本已经做了裁剪兜底但兜底只是不报错更合理的做法是回头改XML把越界的框拉回边界内。第二个坑是XML里存在不参与训练的类别。比如素材里有一两张图带说明标签XML里混了name为caption的object转换脚本会直接跳过但框还在原图上。要确认跳过的是不是误标的有效框不要等到训练完才发现某个熊猫没被学习。第三个坑是图片和XML文件名不同。有的标注工具会把xml存成时间戳和原图前缀对不上。转换结果里会出现某张图有xml但原图不在JPEGImages或者反过来。我用一个简单脚本兜底遍历labels目录下所有txt反向检查对应图片是否存在一行不差才放行。第四个坑是坐标原点不统一。VOC坐标从左上角开始中心点计算是(xmin xmax) / 2这个公式是像素坐标系下的中点没问题但如果你从其他标注工具导出的坐标是“以y向下为正、以右下角为原点”这个公式就错了。转换前先打印一张图的前三个框用可视化脚本画出来看位置对不对比信任何工具都靠谱。4. 标注工具与复核流程用labelImg把217张图标成可训练数据4.1 为什么选labelImg而不是更高阶的工具目标检测常用标注工具有labelImg、labelme、CVAT、Roboflow等。labelImg最老牌但对单类矩形框标注它是效率最高的那一个。labelme适合多边形和分割标注标矩形反而绕CVAT功能全但要起服务一个人标217张图犯不上上服务器。labelImg的安装很简单pip一行就能装依赖的是系统里的Qt和yolo环境配置不冲突不需要额外折腾。pip install labelImg labelImg启动后左侧打开JPEGImages目录右侧指定XML保存目录为Annotations。画框模式选Rectangle快捷键W开始画A和D切换上一张下一张CtrlS保存。这些快捷键能背下来标注速度能快一倍。标注之前把默认类别设置好在data/predefined_classes.txt里只留一行panda。这样画框时按键盘上的类别快捷键直接选中不用每次下拉选择。4.2 先标VOC再转YOLO的工作流217张图一个人标按一张图半分钟到一分钟算连标带休息半天多能完成。我建议按这个顺序走先标VOC XML标完批量转YOLO txt再随机抽30%的图可视化检查一遍。为什么不是直接标YOLO格式因为txt文件人眼根本没法复核一个坐标错位肉眼很难发现XML里至少能看清每个框的像素坐标对错一眼可见。宁可多花十分钟转换也不要抱着黑匣子一样的txt进训练。标注框的规则要统一框紧贴熊猫主体包括头和身体尾巴如果明显伸出可以包进去但不要留大块空白背景。两只熊猫挨在一起时各自独立画框允许框重叠不要合并成一个框。遮挡严重的熊猫可以不标但要记住是哪几张后面用负样本策略处理。有一个经验第1张到第50张是标注质量最不稳定的阶段手还不熟框容易偏。我习惯标完前50张就停下来把已标注的图全部可视化复查一遍修正手误再继续。否则积累到第217张再复查看到错误框的返工成本会让人崩溃。4.3 复核清单和负样本策略全部标注完成后按清单过一遍第一每张图都有对应的xml数量等于217第二随机打开30张左右肉眼确认框和熊猫贴合第三用转换脚本把xml全量转成txt然后跑第6章的可视化脚本把images/labels叠加显示重点看验证集43张。这个小数据集只有一类熊猫容易忽视一类问题模型会把所有带“黑白纹理”的东西都当熊猫。这时候负样本就非常重要。所谓负样本就是完全不含熊猫的图对应txt是空文件模型要从这些图里学会“这里什么都没有输出”。我从素材里挑出10张背景干净但没有熊猫的竹林、山石图直接拷进train和val目录的images里labels下放空txt文件。别小看这10张图没有它们模型特别容易在背景纹理上误检。提示不要在标注阶段开Mosaic等数据增强那是训练时的事。标注数据要保持原图原框增强留给训练脚本做否则一旦标注和增强叠在一起排查错误时根本分不清是哪一层出了问题。5. 避坑指南217张小数据集训练前必须确认的5类异常5.1 训练Loss频繁跳NaN第一反应查空标签现象YOLO训练跑起来前几十个batch的box_loss还能正常下降突然某一步直接变成nan后面全部是nan。原因217张里有一张图对应txt文件为空或者没有配对的txt。yolo训练时如果加载到0字节标签或者缺失标签数据管线会报错或把loss算崩。这类问题在验证集里尤其隐蔽因为验证集的loss不进权重更新直接显示成nan也不会中断训练。解决训练前写个检查脚本遍历labels目录找出所有0字节文件再反向找到对应的原图把这张图要么补标要么从训练列表里剔掉。最稳的办法是训练配置里把exist_ok相关的检查开关打开但不如自己在数据侧提前清理乾净。5.2 验证集mAP一直是0但训练Loss正常现象训练损失一路下降看起来一切正常到最终验证时mAP打印出来是0召回率也是0。原因最常见的是训练集和验证集中的同一张图被重复命中了或者验证集txt里写了一个不存在的文件名。另一种可能是验证集里的坐标标注本身是错的框画到了空白区域真值框和预测框完全没有重合度。解决先做集合交集检查用脚本比对train.txt和val.txt两个文件名列表确认没有重叠。再做真值框检查统计验证集所有标注框的面积占比如果某张图的框宽高都小于10像素模型几乎不可能正确回归这种图建议从验证集挪到训练集。5.3 手机拍摄的熊猫图坐标错位现象标注时在电脑上看是正框训练时模型输出框整体偏了一个固定方向比如所有框向右平移了一截。原因原图自带EXIF旋转信息。有些手机竖拍的jpg预览程序会自动旋转显示但YOLO读图用的是OpenCV不会读取EXIF里的旋转字段于是图被横着读标注坐标和像素位置错位90度或180度。这不是标注手误是读图方式不一致。解决标注前统一用PIL把图片按EXIF转正后另存新文件去掉所有EXIF信息再进标注流程。这一步非常建议放在选图阶段做否则标注完再发现所有XML里的坐标都得重标。5.4 单类数据集误检频发黑白纹理样样都像熊猫现象训练Loss收敛得很好测试集上每张图都画出一堆框框的全是石头、树木阴影之类有黑白对比的区域。原因训练负样本不足模型没见过“没有熊猫的图”于是一直倾向于输出目标存在。217张全部是有熊猫的图模型学到的先验是“画面里总有熊猫”后果就是在没有熊猫的场景里强行找熊猫。解决每15张正样本配1到3张负样本。负样本图的场景要和正样本接近包含竹林、岩石、雪地、水面等容易误检的背景。负样本的txt为空文件单独放进一个负样本目录并写进数据文件中。加了负样本之后置信度阈值可以从0.25上调到0.45误检能明显压下来。5.5 训练集和验证集的难度分配失衡现象训练Loss不再下降验证集mAP停在0.3附近上不去但训练集mAP已经接近0.9。原因划验证集时随机切分恰好把远景小目标、遮挡严重的图全部分到了验证集导致训练集全部是大目标近景模型学到的尺度分布和验证集不匹配。小数据集这种翻车特别常见因为随机划分在小样本量下方差很大。解决切验证集时手动指定难度分布。把217张图按“近景大目标”“中景”“远景小目标”分成三堆每堆按8:2比例分别抽到训练验证最后合并。三堆划分可以肉眼完成不需要额外工具但这步能保证训练集和验证集的难度结构一致。6. 训练前检查与首轮训练用可视化脚本和yolo损失函数判断数据是否可用6.1 把YOLO标签可视化到原图上转换完的txt直接看数字没有任何意义我习惯先跑一个可视化检查脚本把每张训练图的框画出来。import cv2 import os img_dir dataset/yolo/images/train label_dir dataset/yolo/labels/train out_dir check_vis os.makedirs(out_dir, exist_okTrue) for name in os.listdir(img_dir): if not name.endswith(.jpg): continue stem name[:-4] img cv2.imread(os.path.join(img_dir, name)) h, w img.shape[:2] # OpenCV读图后height在前 txt_path os.path.join(label_dir, stem .txt) if not os.path.exists(txt_path): print(fmissing label: {name}) continue with open(txt_path) as f: for line in f: parts line.split() if len(parts) ! 5: print(fbad format line in {stem}.txt: {line.strip()}) continue 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.imwrite(os.path.join(out_dir, name), img) print(visual check saved to check_vis/)脚本里有两处容易踩坑OpenCV的img.shape返回的是(height, width, channels)先取height后取width顺序反了画出来的框会偏归一化坐标乘以宽高时xc乘以wyc乘以h不要写成两个都乘w或者都乘h。检查输出图时重点关注框的边和熊猫轮廓之间留白是否均匀如果某个框明显只框住一半身体回源头把XML改掉再重新转换。6.2 用小数据集跑通首轮训练并观察yolo损失函数数据和标签都确认无误后配置训练单类nc1预训练权重用yolo预训练模型下载下来的官方权重初始化。输入尺寸不要一上来就拉640217张数据量小先用416跑通batch_size设8到16epoch设100。打开训练日志重点看box_loss、cls_loss和dfl_loss三条曲线。正常收敛的pattern是前10个epoch三条loss快速下降20到50个epoch降幅放缓之后在低位小幅抖动。如果box_loss从第一个epoch开始就在0.1上下徘徊不下降先检查标签可视化结果大概率是框和物体没对齐。如果三条loss都下降但验证mAP不涨回看第5章的5.5节检查划分。6.3 小数据集的三条常用增强手段217张图直接训练容易过拟合可以在yolo配置里把Mosaic增强打开但关掉旋转和透视变换因为熊猫是直立生物旋转90度会让模型学到错误姿态。Saturation和Hue扰动开到0.3左右模拟不同光照下的毛色差异Scale扰动控制在0.2以内模拟远近变化。增强不是为了凑数量是为了让模型对亮度、尺度、背景这些真实变化产生鲁棒性过头了反而失真。我的习惯是训练完第一轮之后单独统计验证集上预测框的置信度分布如果多数正确框置信度在0.5以上但还是被漏检考虑把置信度阈值下调如果是误检框置信度偏高回去补负样本比调参更管用。这套流程在217张熊猫数据上跑完第一轮mAP通常不会太高但能稳定过0.7已经足够支撑你继续扩大数据量或者换更强骨干。希望这套目录规范、转换脚本和检查手段能帮你在自己的数据集上少走弯路希望帮到你。本文还有配套的精品资源点击获取
返回列表