ARTICLE DETAIL

资讯详情

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

红花目标检测数据集实战:VOC/COCO/YOLO格式转换与YOLOv8训练全流程

红花目标检测数据集实战:VOC/COCO/YOLO格式转换与YOLOv8训练全流程 简介面向YOLO系列模型训练的红花目标检测数据集包精选1000张真实场景红花图片覆盖多种拍摄环境与角度经LabelImg仔细标注提供VOCxml、COCOjson和YOLOtxt三种通用格式标签分别存放可直接接入常用目标检测训练流程适合教学实训、课程设计和科研竞赛等场景。配套Linux/Windows双平台YOLO环境搭建与训练教程、按实际案例修改训练自有数据集的说明以及训练集/验证集/测试集划分脚本方便用户按需拆分数据并生成ImageSets所需txt文件。整个资源共2000个文件主体为约千份xml标注与990个txt标签文件另含6个HTML说明文档、3个Python脚本和1个模型配置文件yaml压缩包大小103.51MB目录按用途分装结构清晰。已有356人学习浏览适合刚接触目标检测的学生与开发者作为红花检测入门实战资料下载后可按教程从环境搭建到训练调参完整跑通整个流程。1. 红花目标检测要解决的不只是“找得到花”做农业视觉的人多半会被两类问题反复折磨一类是目标小、遮挡重、光照变来变去另一类是标注数据贵、格式乱、训练跑不起来。红花菊科红花属在无人机低空影像和田间定点摄像里刚好把这两类问题撞在一起——花朵直径通常只有几厘米到十几厘米一株上可能同时有未开放的花苞、盛开的红花和凋谢的花头加上叶片颜色相近普通检测模型很容易把花苞漏掉。标题里这个数据集的思路很清楚用1000张图片先把数据量撑起来再同时给出VOC、COCO和YOLO三种标注格式省掉格式互转这一步让不管是跑老版YOLOv5还是新版YOLOv8的人拿到就能开始训练。适合谁看正在做作物表型统计、田间花量估算的算法工程师或者想拿一份现成数据集练手目标检测全流程的初学者。接下来的内容以一个一线工程师的视角把从解包到出模型的完整路径走一遍。2. 三种标签格式的字段差异决定了你的数据管线怎么设计2.1 VOC、COCO、YOLO 的核心字段对比同一批标注换了格式就要重读一遍解析代码这是本地跑数据集最常见的卡点。VOCPascal VOC格式本质上是一个XML文件每张图片对应一个标注文件里面记录着对象的类别名和边界框坐标坐标是像素绝对值左上角为原点。COCO格式则是把所有图片和标注塞进一个JSON文件里图片用ID关联标注里存的也是像素坐标而且类别ID是从1开始编号的0通常保留给背景。YOLO格式最简单一个txt文件一行一个目标内容是“类别ID 中心点x 中心点y 宽度 高度”全部归一化到0到1之间不存在“图片宽度”这种概念。三种格式各有各的历史包袱但对这个数据集来说最需要留意的是类别ID的对应关系——同样是“红花”这个类别voc的XML里叫red_safflowercoco的JSON里可能分配了ID 1而yolo的txt里第一列写的可能是0。如果直接拿VOC标注脚本去训练YOLO模型加载的标签值错一位模型就会把背景当花来学。格式存储单位坐标类型类别定义加载开销VOC每图一个XML像素绝对坐标(左上右下)标签名字符串中解析XML较慢COCO全集一个JSON像素绝对坐标(左上宽高)数字ID类别名映射表大一次加载全量标注YOLO每图一个txt归一化相对坐标(中心点宽高)数字ID(从0开始)低文本直接读入2.2 VOC 转 YOLO 的坐标换算别把归一化顺序搞错手头只有VOC标注又不想依赖在线转换工具时我一般会写一个临时脚本做转换。核心就一个公式把xmin, ymin, xmax, ymax的左上右下坐标换成中心点x_center, y_center和宽高w, h再分别除以图片的宽度img_w和高度img_h。这里最容易出错的不是除法而是忘记把xmax - xmin的结果做一次上限截断——坐标偶尔会超出图片边界不处理YOLO训练时会被视为“无效标签”直接丢弃。import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_file, class_names, out_txt): tree ET.parse(xml_file) root tree.getroot() # 从XML里读取图片宽高用于归一化 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) with open(out_txt, w) as f: for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) 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 w (xmax - xmin) / img_w h (ymax - ymin) / img_h f.write(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) if __name__ __main__: classes [red_safflower, flower_bud] voc_to_yolo( annotations/0001.xml, classes, labels/0001.txt )这段代码里class_names的顺序必须固定下来它就是之后YOLO训练时的类别编号顺序。很多人在这一步是“意会”的——VOC里叫flower_budYOLO里是ID 1看起来没毛病但训练时的data.yaml里类别顺序写反了模型输出和标注就错位。另外注意归一化保留6位小数太少会在训练时产生边界误差尤其在像素较小的花苞上一两像素的偏移就可能让IoU直接从0.9掉到0.7。2.3 COCO 格式的两个隐藏坑categories 首项为0和超像素坐标COCO转YOLO比VOC多一层麻烦。COCO标注里每个标注的bbox字段是[x, y, width, height]但这四个数全是浮点数像素坐标转换时同样要做中心点归一化。真正需要提防的是categories列表——数据集为了保持和YOLO的ID一致可能让ID从0开始也可能从1开始。从1开始时转换脚本里要用class_id cat_id - 1否则YOLO会把你所有目标的类别都学成“减一位的错类”。另一个坑是COCO的area和bbox有时和图片尺寸不匹配个别标注框的x width会超出图片宽度。转换时如果直接除以img_w得到的结果可能大于1YOLO训练时会静默丢弃。我在做COCO转YOLO时会额外截断一次把超过1的坐标值拉回0.999并记录到一个日志文件里回查原标注。处理这类边界值的基本逻辑是宁可截断也不要跳过跳过的样本会成为训练集里的“隐形空洞”。3. 划分脚本的真正作用别让同一株红花同时出现在训练集和验证集3.1 为什么随机划分不靠谱1000张图中如果存在同一株红花的连续多帧影像简单的shuf随机划分会把高度相似的画面同时分进训练集和验证集。验证集loss会好看很多但去田间采集新的设备角度一拍跌回原形。划分脚本的职责不是把文件排列一下而是保证“信息不泄漏”。常见做法是先按图片名排序把连拍的序列视为一个分组再按分组划分。这个数据集同时给出了VOC、COCO、YOLO三种格式意味着划分脚本必须能同步操作三个目录。只做图片划分不够标签也得跟着走一个images/train对应一个labels/train缺一个目录YOLO训练直接报错。我见过有人用现成库split-folders只复制了图片训练时报train.txt里的图片找不到标注文件回查才发现标签目录没建。3.2 一个同时处理三种格式的划分脚本下面是按“以图为中心”的思路写的Python脚本核心是把所有图片路径读出来按比例打乱后分别把图片和对应的三种标签复制到对应子集目录。脚本不care你到底用哪种格式它只做文件级的搬运。import os import random import shutil from glob import glob # 项目根目录下应该有的原始目录结构 IMG_DIR images_orig # 1000张jpg YOLO_DIR labels_yolo_orig # 1000个txt VOC_DIR annotations_voc_orig # 1000个xml文件名与图片一样后缀xml COCO_JSON annotations_coco.json # 整个数据集的coco标注 TRAIN_RATIO, VAL_RATIO 0.8, 0.1 # 剩下的0.1自动归为test def build_split(): imgs sorted(glob(os.path.join(IMG_DIR, *.jpg))) names [os.path.splitext(os.path.basename(p))[0] for p in imgs] random.seed(42) # 固定随机种子多次划分结果可复现 random.shuffle(names) n_train int(len(names) * TRAIN_RATIO) n_val int(len(names) * VAL_RATIO) split { train: names[:n_train], val: names[n_train:n_train n_val], test: names[n_train n_val:] } # 定义每个子集对应的目录 subsets [train, val, test] for sub in subsets: os.makedirs(fimages/{sub}, exist_okTrue) os.makedirs(flabels/{sub}, exist_okTrue) os.makedirs(fannotations_voc/{sub}, exist_okTrue) for sub, name_list in split.items(): for name in name_list: # 图片、YOLO标签、VOC标签同步复制 shutil.copy(f{IMG_DIR}/{name}.jpg, fimages/{sub}/{name}.jpg) shutil.copy(f{YOLO_DIR}/{name}.txt, flabels/{sub}/{name}.txt) shutil.copy(f{VOC_DIR}/{name}.xml, fannotations_voc/{sub}/{name}.xml) print(ftrain{len(split[train])}, val{len(split[val])}, test{len(split[test])})random.seed(42)这行值得多说一句不固定种子每一次运行脚本划分出的图片集合都不同。训练一次模型后想复盘结果可能对不上。固定种子后同一份数据集永远产生同样的train/val/test划分问题复现和结果对比才成为可能。使用shutil.copy而不是move是刻意为之——保留原始目录划分搞砸了随时能重来。3.3 COCO 文件按子集切分上述脚本只处理了VOC和YOLO的独立文件COCO是单个JSON没办法按文件复制。常见的做法是用官方提供的coco_split逻辑处理或者写几行过滤逻辑解析JSON里每个图片的file_name如果在某个子集的名单里就把它的图片记录和对应的标注记录提取出来组成新的JSON。这里有个容易忽略的ID关联问题COCO的标注通过image_id关联图片切分后如果直接保留原始ID后面做评估时可能出现ID冲突。我习惯在切分时保留原ID同时单独写一张id_remap表映射到新的连续ID训练时只在TensorBoard里显示用不影响模型结构。对新手而言最稳妥的方案是把images数组和annotations数组分别过滤保持原文件结构不变。4. YOLOv5/v8 训练教程的关键参数与踩坑点4.1 数据集的目录布局和YAML配置拿到划分好的数据时第一件事不是跑训练而是核对目录结构。YOLOv5和YOLOv8对数据集目录的约定基本一致images/train、images/val、images/test以及对应的labels/train等。标签的txt文件名要和图片文件名严格一致包括后缀前的部分。接下来写data.yamlYOLO家族用这个文件描述数据集。最基本的字段是train、val、test的路径以及nc类别数和names类别名列表。一个常见的错误是train路径写成绝对路径换一台机器就要改一次。保守做法是直接用./images/train这样的相对路径前提是YAML文件放在当前工作目录下。# redflower.yaml train: ./images/train val: ./images/val test: ./images/test # 类别数 nc: 2 # 类别名——顺序必须和标签txt里的数字ID一一对应 names: [red_safflower, flower_bud]4.2 用 YOLOv8 起训练的命令与参数解释对这类中小型数据集YOLOv8s是性价比较高的起点模型参数量适中单卡就能带得动。启动训练的命令如下yolo detect train \ dataredflower.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience15 \ projectredflower_runs \ namebaseline命令行每个参数都有明确意义但也有常见误用。epochs100并非越多越好红花目标小训练后期容易过拟合配合patience15验证集指标连续15轮没提升就提前停止更稳妥。batch16取决于显卡显存如果24GB显存可以上调到32反之8GB显存降到8否则会OOM。imgsz640是目前精度和速度的平衡点但如果花苞在图片里不足20像素建议尝试imgsz960代价是训练时间约翻倍。训练完成后模型权重保存在redflower_runs/baseline/weights/best.pt。注意best.pt依据的是验证集指标不是训练集loss。对红花这种类内差异大的目标我一般直接看mAP50-95。4.3 训练日志里需要盯的4个指标很多人只看最终的mAP就下结论但中间环节的信息量更大。训练时终端会实时打印一组字段我通常重点看四项box_loss、cls_loss、dfl_loss和mAP50-95。box_loss在训练前10个epoch里如果降不下去大概率不是模型问题而是标签框和实际目标对不齐回到数据层面去看标注吧。cls_loss如果出现异常波动先查是不是类别ID错位——数据集的names顺序变了或者某些类别的样本数太少都会让分类头学不扎实。mAP50-95比mAP50更有参考价值它衡量的是模型对框的精确定位能力对红花这种密集小目标很重要。如果mAP50已经到0.9但mAP50-95只有0.5说明你的框位置不够准下一步该考虑调高imgsz而不是加大epochs。4.4 推理测试时最容易忽略的细节训练完做推理最常见的错误是没加source路径。模型不知道你要预测哪张图所以在redflower_runs/baseline目录下运行yolo detect predict \ modelredflower_runs/baseline/weights/best.pt \ sourceredflower_runs/baseline/val_batch0_pred.jpg \ conf0.25推理时还常遇到结果全是nms 0的情况——拿训练时的modelyolov8s.pt加载了预训练模型而不是best.pt权重不对得到的自然是空列表。另外conf0.25是默认置信度阈值如果红花在远距离拍摄下置信度普遍偏低把它降到0.1能跑出更多候选框但代价是误检增多此时先进框架框看结果再做后处理。5. 一个能省半天时间的验证技巧跑训练前先做标签可视化核对在做任何训练之前用一个脚本把所有训练集图片上的真实标注框画出来存成新的图片人眼过一遍。这一招能发现大量标注问题框错位、漏标、类别错乱、坐标越界。很多工程师跳过了这一步训练到一半才发现数据集质量不行白折腾十几个小时。下面这个脚本读YOLO格式的标签用OpenCV把归一化坐标还原成像素坐标再在原图上画出框和类别名。import cv2 import os import glob IMG_DIR images/train LBL_DIR labels/train # 类别名和颜色对应颜色纯属可视化用不影响训练 CLASS_COLORS { 0: (0, 0, 255), # 红花-红色 1: (0, 255, 255) # 花苞-黄色 } CLASS_NAMES {0: red_safflower, 1: flower_bud} def draw_yolo_boxes(img_path, txt_path, out_path): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path, r) as f: for line in f.readlines(): parts line.strip().split() cls_id int(parts[0]) x_c, y_c, bw, bh map(float, parts[1:]) # 归一化坐标还原为像素坐标 cx int(x_c * w) cy int(y_c * h) bw_px int(bw * w) bh_px int(bh * h) x1 max(0, cx - bw_px // 2) y1 max(0, cy - bh_px // 2) x2 min(w, cx bw_px // 2) y2 min(h, cy bh_px // 2) cv2.rectangle(img, (x1, y1), (x2, y2), CLASS_COLORS.get(cls_id, (255, 255, 255)), 2) cv2.putText(img, CLASS_NAMES.get(cls_id, str(cls_id)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, CLASS_COLORS.get(cls_id, (255, 255, 255)), 1) cv2.imwrite(out_path, img) # 遍历训练集前100张做抽样检查 for img_path in sorted(glob.glob(os.path.join(IMG_DIR, *.jpg)))[:100]: base os.path.splitext(os.path.basename(img_path))[0] txt_path os.path.join(LBL_DIR, base .txt) out_path os.path.join(debug_vis, base .jpg) os.makedirs(debug_vis, exist_okTrue) draw_yolo_boxes(img_path, txt_path, out_path)把debug_vis目录里的图翻一遍重点看两类情况。第一类是“漏标”红花周围没有框或者两个相邻花朵被并成了一个框。这类问题影响的是模型的学习上限漏得越多模型越容易把花苞当背景。第二类是“错位框”框的中心偏了半个花身这类问题训练和推理时都挺难发现loss也会偏高。一个具体的判断指标是随机抽查100张图如果超过5%的框有明显错位就别急着训练回标注软件调整一遍更划算。这个可视化脚本还可以顺手输出一个统计信息每张图的标注框数量分布、宽高分布以及是否存在宽高极小比如小于10像素的框。红花数据里这类小框很常见但如果小框扎堆把imgsz调高到960或1280模型学起来才不吃力。换句话说可视化不只是“看一眼”它能把训练潜在的风险提前排掉这才是划分脚本之外真正值得多花时间的一步。本文还有配套的精品资源点击获取
返回列表