ARTICLE DETAIL

资讯详情

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

带标签YOLO数据集增强:bbox与mask同步扩图全链路

带标签YOLO数据集增强:bbox与mask同步扩图全链路 简介面向YOLOv5等目标检测与分割任务的数据增强工具包专注解决标注图片数量不足、模型泛化能力弱的问题尤其适合小样本项目快速扩充数据。通过随机组合翻转、剪切、仿射变换、高斯模糊、平移、自适应高斯噪声和亮度调整等策略能够在变换图片的同时同步更新对应标签免去手工重新标注的繁琐步骤可无缝适配LabelImg和LabelMe两种主流标注格式覆盖检测与分割两类场景有效提升样本多样性。压缩包内共包含23个文件核心为4个Python脚本重命名脚本用于批量整理文件两个增强脚本分别处理LabelImg与LabelMe标注的数据另有16张示例图片、xml/json标注文件及简要说明文档整体仅1.64MB轻量易部署。目前已有876人学习下载。实际使用中参考demo目录结构将图片和标注文件放入指定文件夹即可自动输出增强后的样本操作门槛低非常适合初学数据增强的开发者快速上手。1. 带标签的 yolo 数据集增强扩图的同时让 bbox 和 mask 跟着变标了十几张 Camera_1 的图就急着开训YOLO 的 mAP 大概率不给你面子。与其咬咬牙再标几百张不如先把这批带标注的数据做一遍带标签扩增图翻转变亮了标注框和分割多边形也同步变扩出来的数据直接进 yolov5/yolov8 的训练目录。这份资源里三个 python 脚本分别负责重命名、LabelImg 检测标注增强、LabelMe 分割标注增强支持模糊、亮度、裁剪、旋转、平移、镜像以及随机组合能同时处理目标检测和目标分割两类标注。适合刚标完一批数据发现样本量不足的入门玩家也适合想给现有数据集补充难度的老手。下文先把三个脚本的数据走向盘清楚再带你跑通两条增强链路。2. 先盘数据走向三个脚本的分工和带标签增强的原理2.1 检测与分割两条链路XML、JSON 各走各的这套工具把任务拆成两条独立链路目标检测走 LabelImg 的 Pascal VOC XML目标分割走 LabelMe 的 JSON。之所以不统一格式是因为两个标注工具的存储结构完全不同增强时要改的坐标对象也不一样。检测改的是 bbox 的(xmin, ymin, xmax, ymax)四个数分割改的是 shape 里一串 polygon 点集。脚本配对标注格式增强方式适配任务rename_file.py任意批量重命名数据整理DataAugmentforLabelImg.pyLabelImg 导出的 XML模糊、亮度、裁剪、旋转、平移、镜像YOLO 系目标检测DataAugmentforLabelMe.pyLabelMe 导出的 JSON模糊、亮度、平移、镜像实例分割 / 语义分割跑之前先把依赖装齐opencv_python、numpy、Pillow这三个基本跑不掉。README 里也专门提醒了 opencv 的问题这步偷懒后面所有图像变换直接给你报module not found。我个人习惯用 conda 建一个干净环境再装避免和别的项目的 opencv 版本互相踩。提示LabelImg 版增强只吃 Pascal VOC 格式的 XMLYOLO 的 txt 格式不是它的输入。你需要先有 XML增强完再转成 YOLO 训练用的 txt这个后面细说。2.2 bbox 和 polygon 为什么能跟着图变坐标换算与随机组合带标签增强的可行性建立在“图像变换和坐标变换用同一组参数”这个前提上。检测侧最典型的是旋转VOC 的(xmin, ymin, xmax, ymax)要先换成中心坐标加宽高旋转后再取外接矩形换回xyxy。平移简单直接给四个坐标各加偏移量水平镜像则是交换xmin和xmax。模糊、亮度这类像素级操作根本不碰坐标所以最简单。分割侧的 polygon 是一串 N×2 的坐标数组。平移就是每个点加同一个偏移镜像对 x 坐标取反旋转、剪切、仿射变换则是对点集做矩阵乘法和图像用同一个变换矩阵M。这里很容易出 bug 的地方是有人只对图像做了warpAffine忘了对 polygon 点集做同样的cv2.transform结果就是增强图上 poly 还躺在老位置。下面是几个增强原语直观感受一下哪些动像素、哪些动坐标import cv2 import numpy as np img cv2.imread(Imgs/Camera_1.jpg) h, w img.shape[:2] # 亮度alpha 是增益beta 是加常数只改像素不影响 bbox bright cv2.convertScaleAbs(img, alpha1.2, beta10) # 高斯模糊ksize 核越大越糊同样不碰标注坐标 blur cv2.GaussianBlur(img, (7, 7), 0) # 水平镜像图左右互换xml 里 xmin 和 xmax 必须同步交换 flip cv2.flip(img, 1)前面两个增强不碰坐标第三个镜像改完图必须改 box。这就是带标签增强的核心逻辑每动一次几何变换都要找到对应坐标结构并同步更新。摘要里提到的“随机组合”也建立在这个基础上本质是给每项增强一个随机概率满足条件才执行所以同一张图每次跑出来的结果都可能不同这正是数据增强想要的多样性。2.3 随机组合的默认逻辑和你要留意的概率变量这套工具的增强策略按“随机引入不同方向翻转、剪切、仿射变换、高斯模糊、平移、自适应高斯噪声、亮度改变”设计然后随机组合。实际源码里一般会有一排概率阈值比如if random.random() p_flip:、if random.random() p_blur:这种结构。每项一个独立判断命中了才执行所以增强后文件数量会按组合数成倍膨胀。这里要提醒一个常见误用直接把所有概率都调到 1.0结果每张图同时叠加了旋转、噪声、亮度、平移样本难度陡增训练 loss 直接不收敛。我一般会把几何类增强控制在两张叠加以内像素类增强控制在三张以内。先看一遍两个 DataAugment 脚本顶部或函数里的概率变量再决定要不要调别盲目改。3. 跑通检测增强LabelImg 的 XML 怎么跟着图片一起扩3.1 目录准备demo 的图、xml 和输出路径怎么摆先照着 demo 的把目录搭出来。下载解压后你会看到Imgs目录放演示原图DataAugForObjectDetection下有个data目录你的图和你标好的 XML 就按下面这样放Data-enhancement/ ├── rename_file.py ├── DataAugForObjectDetection/ │ ├── DataAugmentforLabelImg.py │ └── data/ │ ├── Annotations/ # 放 LabelImg 导出的 xml │ └── JPEGImages/ # 放原图 └── Imgs/ # demo 原图示例注意几点xml 文件名必须和图片文件名一一对应Camera_1.jpg配Camera_1.xml对不上脚本会直接跳过或者报找不到文件原始标注目录提前备份一份增强脚本跑完会往输出目录写新图和新 xml没有后悔药。我一般先把 data 整个复制一份叫data_raw再拿data目录给脚本当输出这样原始标注始终保底。3.2 运行脚本与六种增强的参数设置在哪儿改路径摆好之后进DataAugForObjectDetection目录直接跑cd DataAugForObjectDetection python DataAugmentforLabelImg.py如果脚本里写死了路径就先打开文件把头部那几个目录变量改成你自己的路径。常见做法是把输入输出指到上面那个data目录下输出建议单独开一个augmented子目录别覆盖原图。这个脚本支持的增强类型比较多我把对照关系整理如下增强项涉及函数我一般建议的区间模糊cv2.GaussianBlur核大小 3~13概率 0.3亮度cv2.convertScaleAbsalpha 0.7~1.3beta 5~20裁剪cv2.Crop / 数组切片裁剪比例 0.7~0.9旋转cv2.warpAffine角度 -10°~10°超过 15° 外接框膨胀明显平移cv2.warpAffine偏移量 0.05~0.1 倍图宽高镜像cv2.flip水平镜像居多垂直慎用不同版本的脚本命名可能不一样但变量名的意思基本都能看出来。改参数的时候认准两个东西概率阈值和幅度区间。概率决定这张图会不会被增强幅度决定增强得有多狠。新手最容易翻车的是把旋转角度拉到 30°检测框会变得非常大且不贴合目标这个我在第 5 章详细说。3.3 增强结果自检先数文件再抽看图片跑完脚本之后先做两个最基础的自检。第一数输出目录里 jpg 和 xml 文件数量是否一致第二检查是否出现只有图没有 xml 的孤儿文件。如果数量对不上多半是某张图的 xml 没有配对成功。这一步查文件比查坐标快得多我建议养成习惯每次跑完先执行一次ls data/augmented/*.jpg | wc -l ls data/augmented/*.xml | wc -l两个数字一致才算过了第一关。接下来才是抽三四张图把 xml 框画上去看位置对不对。这套检查流程放到第 6 章统一写成脚本这里先做到能判断“有没有生成、有没有配对”。4. 跑通分割增强LabelMe 的 JSON 点集同步与重命名4.1 LabelMe 增强脚本polygon 点集跟着变换走分割增强走DataAugForObjectSegmentation目录下的DataAugmentforLabelMe.py配对的是 LabelMe 标注的 JSON。LabelMe 的 JSON 里有个shapes数组每个元素代表一个标注对象里面的points就是 polygon 的关键点坐标数组。脚本要做的核心工作就是读取每个 shape 的 points做完几何变换后把新坐标写回 JSON。import json import numpy as np with open(Camera_1.json, r, encodingutf-8) as f: ann json.load(f) pts np.array(ann[shapes][0][points]) # 平移 (10, 5)每个点的 x 10, y 5偏移量和图像平移必须一致 pts_shift pts np.array([10, 5]) print(pts_shift)这段代码演示了最简的平移实际脚本里还包含模糊、亮度、镜像这些变换。关键在于图像和 polygon 必须用同一组参数比如图像做了cv2.flip(img, 1)那 polygon 的 x 坐标就必须做w - x的换算否则扩出来的 mask 就是错位的。实例分割任务对坐标比检测更敏感polygon 有一两个点没同步好扩出来的 mask 就是缺角的。进入分割脚本目录直接运行同理cd DataAugForObjectSegmentation python DataAugmentforLabelMe.py4.2 rename_file.py先统一命名再跑增强分割和检测两条链路都会遇到同一个问题手工标注时的文件名往往是乱的。demo 里Camera_1_1.jpg、Camera_1_10.jpg这种命名如果原图、xml、json 之间命名不统一增强脚本跑一半就断了。rename_file.py就是干这个事的批量统一文件名改成等宽编号避免乱序和同名覆盖。python rename_file.py跑之前务必先打开脚本看头部路径变量确认它指向的是你要重命名的目录。这个脚本是原地改名的路径填错会把文件改成看不懂的名字。我一般建议在标注完成后、增强之前先跑一遍重命名把Camera_1_1.jpg这种改成img_0001.jpg风格xml 和 json 同步改名增强出来的文件名才可控后面转 YOLO txt 时也不会因为文件名错位找不到对应标注。4.3 两个增强脚本的边界它们不直接输出 YOLO 训练格式有一个边界必须提前说清楚这个资源增强完输出的是 Pascal VOC XML 和 LabelMe JSON不是 yolov5/yolov8 直接能用的labels/*.txt。你增强完之后还得把 XML 转成 YOLO 格式的归一化 txt。常见做法是写个转换脚本遍历增强后的 XML读 bbox换算成(cx, cy, w, h)再除以图宽高。这一步最好放在增强之后做避免同一套转换逻辑跑两遍。如果你后面打算往旋转框检测方向走比如用 mmrotate 去训 DOTA 这类数据集那要注意这个工具生成的是水平外接框旋转框数据集的theta角需要你自己重新标注或另行转换工具本身不产旋转框标注。5. 带标签扩增避坑标注漂移、坐标越界、文件错位一次排查5.1 先看现象再定位高频坑速查跑这套工具翻车点比较集中我先给一张速查表按“现象 → 原因 → 涉及环节”定位下面再逐条展开。现象原因涉及环节增强后框和物体错开一截图像变换和坐标变换参数不一致或旋转后外接矩形未重算旋转 / 仿射训练时报坐标越界、NaN裁剪或平移后坐标超出图宽高没有 clip裁剪 / 平移只有图没有 xml或 xml 对不上号文件名配对错位重命名顺序没统一rename_file / 配对增强完 loss 不降反升概率全开每张图叠加了太多增强噪声过大随机组合opencv 读不出图程序跑完结果全空中文路径或特殊字符导致 imread 失败环境 / 路径5.2 五个高频坑的展开排查坑一是“xml 没跟着图走”。现象很直接图翻转了框还留在老位置或者训练时 YOLO 坐标直接报越界。原因基本有两种要么增强函数里只处理了 image 忘记处理 bbox要么 opencv 读图失败但程序没报错继续往下跑把原始坐标原样写进了输出 XML。解决方法是跑之前先备份原始标注跑完挑一张做了镜像的图看输出 XML 的xmin是否大于原xmin水平镜像后这个值必须反过来。YOLO 训练目录里如果出现 0~1 范围外的坐标值先回来查这一步。坑二是旋转增强后框大面积包住背景。现象旋转 15°框把四个角全包进去了目标只占框的一小块。原因是旋转后用外接矩形重算坐标必然膨胀角度越大越严重。解决方法是把旋转角度上限控制在 10° 以内或者旋转后对框做一次收缩校正。我自己的习惯是超过 10° 的旋转增强单独评估检测小目标的场景直接不开旋转。坑三是裁剪或平移后坐标越界。现象转 YOLO txt 时出现负坐标或者画框时框线跑到图外面。原因是裁剪后坐标没有限制在图宽高范围内。解决方法是给所有输出坐标做一次 clip并把面积小于阈值的过小框过滤掉。下面的修复片段我一般会插在转换脚本里xmin max(0, xmin) ymin max(0, ymin) xmax min(w, xmax) ymax min(h, ymax) if (xmax - xmin) 10 or (ymax - ymin) 10: print(过滤过小框, xmin, ymin, xmax, ymax) continue参数说明10 是像素阈值小于 10×10 的框在增强后基本失去训练意义continue 表示跳过这个框不写进输出标注。这个过滤逻辑对检测和分割都适用polygon 也可以按新坐标围成的面积做同样过滤。坑四是增强后模型 loss 不降。现象数据量翻了几倍训练反而比只用原始数据还差。原因基本是概率全开旋转加噪声加亮度加平移同时叠加样本难度超出模型初始学习能力。解决方法是控制单图增强叠加数量几何增强最多两张叠加像素增强最多三张或者先只开镜像和亮度跑一轮基线再逐步加难度。坑五是 opencv 读不出中文路径图片。现象脚本不报错但增强结果文件数量明显偏少输出里全是空白图。原因是cv2.imread对中文路径和中文文件名支持不好Windows 下最容易踩。解决方法是把工程路径全部改成英文图片文件名统一改成英文字符rename_file.py 在这个场景下正好派上用场。6. 扩增完的验收习惯坐标检查与可视化一次过增强完不是直接扔进训练目录就完事我建议每次扩增后固定走一遍验收脚本。这个习惯是从一次血泪经验里养成的当时赶进度扩了五千张图没抽查训练到第二个 epoch 才发现坐标漂移整个数据集白扩。从那以后我每次跑完增强都强制自己先抽 10 张图做可视化验收再进训练目录。下面的脚本读增强后的 XML把框画到图上逐张看import cv2 import xml.etree.ElementTree as ET def draw_xml(img_path, xml_path): img cv2.imread(img_path) h, w img.shape[:2] root ET.parse(xml_path).getroot() for obj in root.iter(object): box obj.find(bndbox) xmin int(float(box.find(xmin).text)) ymin int(float(box.find(ymin).text)) xmax int(float(box.find(xmax).text)) ymax int(float(box.find(ymax).text)) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) cv2.imshow(check, img) cv2.waitKey(0) # 随机抽 10 张增强后的图和对应的 xml 验证 draw_xml(data/augmented/Camera_1_1.jpg, data/augmented/Camera_1_1.xml)rectangle的第三个参数是 BGR 颜色(0, 255, 0)是绿色第四个参数是线宽2 像素在 640 分辨率图上比较明显。waitKey(0)表示按任意键看下一张不填会直接黑屏闪退。抽看的时候重点看两类问题框是否贴住目标主体、镜像图上框是否在对称位置。可视化过了之后再做一步数值检查XML 转 YOLO txt 时顺手输出归一化中心的比值如果cx / w或cy / h出现负数或大于 1说明坐标没同步好直接毙掉这批增强结果重新跑。分割数据同理把 JSON 的 polygon 画成 mask和原图叠加看边缘是否贴合。这两步加起来不到十分钟却能拦住绝大多数静默错误。从那以后我每次扩完数据都强制走一遍上面的流程先看框再看 mask最后过数值检查确认没问题才往训练集里放。别嫌这几分钟麻烦扩标错了数据后面赔进去的是整个调参周期的精力。希望帮到你。本文还有配套的精品资源点击获取
返回列表