芒果实例分割实战:COCO JSON转YOLOv8格式与训练全攻略
简介这是一份芒果实例分割数据集已转换为YOLOv8格式可直接用于目标检测与实例分割模型的训练和验证面向计算机视觉、深度学习和智慧农业相关开发者。数据内容围绕芒果个体识别展开可服务于精准农业中的生长监测、自动化采摘机器人定位、果实分级质检、病虫害筛查以及储运环节的实时监控等应用场景。压缩包共1088个文件其中主要为542张PNG图像和542个对应的TXT标签文件并附带train.json、test.json等数据集配置说明及cache缓存文件包体大小187.03MB结构清晰便于直接接入YOLOv8训练流程。数据划分包含453条训练数据和91条验证数据能够支撑常规训练与效果评估。目前已有761人学习下载适合需要快速获取标注数据以开展芒果目标检测或实例分割项目的研究者与工程人员使用。1. 芒果实例分割数据集把果园场景拆成一颗一颗可操作的目标做农业视觉项目最头疼的不是模型选型而是标注数据有没有对到业务上。这个芒果实例分割数据集一共 544 张真实果园 RGB 图像训练集 453 张、验证集 91 张图像来自 Azure Kinect 相机在固定曝光和 5fps 采样下的采集流文件名里的azure_rgb_5fps_2500expo就是这个意思。相比普通目标检测框实例分割要额外输出每个芒果的轮廓掩码这对采摘机器人定位果柄、成熟度分级和遮挡场景下的计数都有直接价值。数据量不大但场景单一、类目只有芒果一类正好适合跑通 YOLOv8 实例分割的完整链路也可以用来检验小数据集上的迁移学习和数据增强策略是否有效。如果你是刚接触 YOLOv8 实例分割手里只有这份 JSON 标注这篇文章会带你完成格式核对、标签转换、配置文件编写和训练调参如果你已经跑过 COCO 或别的分割训练我还会说几个容易在遥控器阶段翻车的细节比如 cache 文件怎么检查、验证集太小导致的波动怎么处理。2. 先搞清楚芒果数据集的文件格式COCO JSON、缓存文件与 YOLOv8 标签的关系2.1 从文件列表反推数据来源与格式打开压缩包后你会看到这些文件文件类型作用train.jsonCOCO 风格标注训练集标注含 images/annotations/categoriestest.jsonCOCO 风格标注验证集标注结构与 train.json 一致train.cache/test.cachePyTorch 缓存ultralytics 扫描数据集后生成的注册表避免每次启动都重新解析azure_rgb_5fps_2500expo_*.png图像图像本身命名体现了采集配置json里存的是多边形坐标和边界框而 YOLOv8 的segment模式默认要求标签是txt文件每行格式为class x1 y1 x2 y2 x3 y3 ...。这里的train.cache说明数据被 ultralytics 工具扫描过但如果你的环境里 cache 是旧的或缺失的就需要从 JSON 生成正确的 YOLO 标签文件。注意不要拿 COCO 格式的 JSON 直接填进data.yaml就给 YOLOv8 训练你可能会看到 no labels found 或全部类别为空的结果。2.2 YOLOv8 实例分割标签与 COCO JSON 的差异在 YOLOv8 实例分割任务中labels目录下每个.txt对应一张图像每行代表一个实例。前四个数值是归一化边界框中心点坐标和宽高xc, yc, w, h随后是归一化多边形顶点坐标。COCO JSON 里则保存的是未归一化的bbox和segmentation列表。这里的差异不仅仅是单位换算还牵扯到多边形是否闭合、边界是否裁剪到图像尺寸内。另外COCO 的segmentation可能有多个多边形例如一个芒果被树叶部分遮挡被标成两段而 YOLOv8 的segment标签要求一个实例对应一组闭合坐标。遇到这种数据在转换前要决定是取最大轮廓还是合并所有轮廓否则训练时 mask 会变形。2.3 写一个转换器把 train.json 转换成 YOLO 分割标签我通常先写一个独立的转换脚本不依赖 ultralytics 内部工具方便处理特殊数据。下面这段代码基于 COCO 格式遍历所有标注并输出为 YOLO segment 格式import json import os import numpy as np def coco_to_yolo_seg(json_path, img_dir, out_label_dir, img_width1280, img_height720): 将 COCO 风格 JSON 转换为 YOLOv8 实例分割格式 :param json_path: COCO 标注文件 :param img_dir: 图像所在目录 :param out_label_dir: 输出的标签目录 with open(json_path, r, encodingutf-8) as f: data json.load(f) # 建立 image_id 到文件名的映射 img_id_to_name {img[id]: img[file_name] for img in data[images]} # 建立 category_id 到 class_id 的映射这里只有芒果所以固定给 0 cat_id_to_class {cat[id]: i for i, cat in enumerate(data[categories])} os.makedirs(out_label_dir, exist_okTrue) # 按 image_id 聚合所有标注 anns_by_img {} for ann in data[annotations]: img_id ann[image_id] anns_by_img.setdefault(img_id, []).append(ann) for img_id, anns in anns_by_img.items(): file_name img_id_to_name[img_id] label_path os.path.join(out_label_dir, file_name.replace(.png, .txt)) lines [] for ann in anns: cls_id cat_id_to_class[ann[category_id]] bbox ann[bbox] # [x, y, w, h] 未归一化 x, y, w, h bbox # 过滤掉无效或过小的框避免训练崩溃 if w 1 or h 1: continue # 归一化边界框中心点 cx (x w / 2) / img_width cy (y h / 2) / img_height nw w / img_width nh h / img_height # 取 segmentation 中的第一个多边形 seg ann[segmentation][0] # 严格做一次裁剪与归一化防止顶点越界 points [] for i in range(0, len(seg), 2): px min(max(seg[i], 0), img_width) / img_width py min(max(seg[i 1], 0), img_height) / img_height points.append(f{px:.6f} {py:.6f}) # 每行至少要有一个多边形闭环4 个点以上 if len(points) 4: continue line f{cls_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f} .join(points) lines.append(line) if lines: with open(label_path, w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: coco_to_yolo_seg( train.json, ./images/train, ./labels/train, img_width1280, img_height720 )这段脚本的核心是把 COCO 的[x, y, w, h]改成中心点坐标并把多边形顶点全部归一化。这里img_width和img_height需要和实际图像尺寸一致如果你的数据集图像不是固定分辨率建议在循环里用cv2.imread(file_name).shape动态获取。写完后不要急着训练先数一数生成的.txt数量和json中的图片数量是否一致再用下面的校验脚本检查随机抽样结果。2.4 数据校验避免边界框和分割掩码错位转换后最常见的故障是标签顺序和图像错位也就是txt文件名与png对应不上。YOLO 会把文件名去掉扩展名后作为 key如果图像是azure_rgb_5fps_2500expo_539.png那么标签必须是azure_rgb_5fps_2500expo_539.txt不能多一个空格或改大小写。import os import random img_dir images/train label_dir labels/train imgs [f for f in os.listdir(img_dir) if f.endswith(.png)] labels [f for f in os.listdir(label_dir) if f.endswith(.txt)] img_stems {os.path.splitext(f)[0] for f in imgs} label_stems {os.path.splitext(f)[0] for f in labels} missing_labels img_stems - label_stems orphan_labels label_stems - img_stems print(f缺失标签数: {len(missing_labels)}) print(f多余标签数: {len(orphan_labels)}) # 随机抽 5 个标签打印归一化坐标范围检查是否在 0~1 内 for lbl in random.sample(list(label_stems), min(5, len(label_stems))): with open(os.path.join(label_dir, lbl .txt)) as f: first_line f.readline().strip() print(lbl, first_line)如果缺失标签数很多说明转换脚本里anns_by_img分组没有覆盖全或者 JSON 中有iscrowd标注没被过滤。一般 COCO 数据集中iscrowd1的物体是不参与实例分割训练的这类标注应该跳过。3. 配置数据集 YAML 并启动 YOLOv8 训练453 条训练数据怎么用3.1 目录组织与 data.yamlYOLOv8 训练时不会直接读 JSON而是通过data.yaml知道图像和标签放在哪里。推荐按下面结构组织mango_dataset/ ├── images/ │ ├── train/ │ │ ├── azure_rgb_5fps_2500expo_534.png │ │ └── ... │ └── val/ │ ├── azure_rgb_5fps_2500expo_549.png │ └── ... ├── labels/ │ ├── train/ │ │ ├── azure_rgb_5fps_2500expo_534.txt │ │ └── ... │ └── val/ │ ├── azure_rgb_5fps_2500expo_549.txt │ └── ... └── mango.yamldata.yaml的内容如下path: /path/to/mango_dataset train: images/train val: images/val names: 0: mango注意names里的索引必须和转换脚本里的 class_id 对应。这里只有一个类别所以0: mango。如果你的 JSON 里还有别的类别需要先跑一段代码统计categories列表别凭想象写。3.2 训练命令与关键参数说明使用 ultralytics 官方包训练实例分割模型命令如下yolo segment train \ modelyolov8s-seg.pt \ datamango.yaml \ epochs100 \ batch8 \ imgsz640 \ device0 \ patience20 \ project./runs \ namesegment_mango参数含义如下参数值作用modelyolov8s-seg.pt预训练分割模型s 是 small比 n 精度高比 m 和 l 快datamango.yaml数据集配置文件路径epochs100训练轮数小数据集建议先跑 100batch8输入并行数量根据显存调整GTX 1660Ti 跑 8 张 640 图像没问题imgsz640输入分辨率芒果目标不算太小640 够用patience20早停轮数验证集 mAP 20 轮不涨就停device0使用第一张 GPUCPU 训练可改为cpu训练结束后runs/segment_mango下会生成best.pt和last.pt同时有results.png包含 loss 曲线和 mAP 曲线。用yolo segment val单独验证yolo segment val modelruns/segment_mango/best.pt datamango.yaml device03.3 从 C2f 模块理解小数据集的训练开销YOLOv8 的骨干网络用了 C2f 结构替换了 YOLOv5 中的 C3。C2f 通过多个分支的 Cross-stage connection 增强梯度流参数量比 C3 略高但计算量更可控。这个设计在 COCO 这类大目标数据集上收益明显但在只有 544 张图的芒果场景里你不需要用到yolov8x-seg这种大模型更合理的起点是yolov8n-seg或yolov8s-seg。模型越大小数据集上越容易早停后过拟合而且训练时间翻倍收益却极小。我一般在拿到这种小数据集时先跑 30 个 epoch只看best.pt的验证集 mAP50-95。如果 mAP50 能到 80 以上再跑完整 100 轮如果连 60 都不到优先检查标签转换和数据增强而不是换大模型。4. 实例分割训练中的典型问题小数据集、遮挡与验证集划分4.1 类别不平衡和标签质量导致 mAP 波动这个数据集只有一类不存在类别不平衡问题。但 91 张验证集意味着每张图出现或缺少一个芒果都对 mAP 影响巨大。你可能会看到验证集 mAP 在几个 epoch 之间从 0.85 跳到 0.71这是小验证集的正常抖动不代表模型崩溃。更好的做法是确认test.json中每张图像的平均实例数。写一个小脚本统计import json with open(test.json) as f: data json.load(f) num_imgs len(data[images]) num_anns len(data[annotations]) print(验证集图像数:, num_imgs) print(实例总数:, num_anns) print(平均每张图实例数:, round(num_anns / num_imgs, 2)) # 统计边界框面积分布过滤掉转换时可能丢掉的极小白点 from collections import Counter area_bins Counter() for ann in data[annotations]: bbox ann[bbox] area bbox[2] * bbox[3] if area 32 * 32: area_bins[small] 1 elif area 96 * 96: area_bins[medium] 1 else: area_bins[large] 1 print(面积统计:, dict(area_bins))如果small占比很高说明数据里存在大量远距离小芒果实例分割在边缘轮廓上会非常吃力。这时应该降低验证置信度阈值conf0.001否则默认 0.25 的阈值会把许多真实小目标过滤掉导致 mAP 被低估。4.2 小数据集上的数据增强参数调节ultralytics 的默认增强对一般场景有效但 453 张训练图在 100 轮内会反复看到同一张图过强的几何增强反而让掩码学习不稳定。我一般会这样调整yolo segment train \ modelyolov8s-seg.pt \ datamango.yaml \ epochs100 \ batch8 \ imgsz640 \ hsv_h0.015 \ hsv_s0.5 \ hsv_v0.4 \ degrees10 \ translate0.1 \ scale0.4 \ fliplr0.5 \ mosaic0.5degrees10表示最多旋转 10 度芒果是自然物体旋转增益不大mosaic0.5降低马赛克增强的使用概率因为小数据集本身场景就单一过度拼接反而会让模型学到拼接缝特征。如果你的图像是从视频里按 5fps 截取的相邻帧之间的芒果形态非常接近此时更强的scale0.5或translate0.2能模拟不同拍摄距离。4.3 验证集只有 91 条怎么读结果可可靠不要只盯着val/目录生成的最后一行 mAP。建议用yolo segment val时加上save_jsonTrue把验证结果输出成predictions.json然后用代码统计不同置信度区间和尺寸下的性能yolo segment val \ modelruns/segment_mango/best.pt \ datamango.yaml \ conf0.001 \ save_jsonTrue打开生成的predictions.json每个目标会带有category_id,score,bbox和segmentation。你可以按置信度排序找出那些预测分数低但真实存在的芒果看它们是不是被树枝遮挡或者光照太强。这个检查通常比调参数更有反馈价值。5. 用 train.cache 快速定位训练数据异常一个能省半天的小技巧训练中途遇到过 Dataset not found 或者部分图片没参与训练的情况吗与其从头扫描图像列表不如直接读 YOLOv8 生成好的.cache文件。这个文件用torch.save保存本质是一个字典记录了每张图像的路径、尺寸、标签框数量、类别标签和分割多边形信息。只要你的代码路径不变train.cache就能复用省掉每次启动的数据解析时间。import torch import time def inspect_cache(cache_path): start time.time() data torch.load(cache_path, weights_onlyFalse) print(f加载 cache 耗时: {time.time() - start:.2f}s) print(cache 结构字段:, list(data.keys())[:5]) # 检查是否存在 labels 和 images 键 for key in [labels, images]: if key in data: item data[key] print(f[{key}] 数量:, len(item)) if key labels: # 打印前 3 条数据的内容结构 for i, (img_id, info) in enumerate(item.items()): if i 3: break print(img_id, 标签框数:, len(info[bbox])) # 找到异常没有框的图像 if labels in data: img_without_labels [k for k, v in data[labels].items() if len(v[bbox]) 0] print(\n无标签图像数量:, len(img_without_labels)) if img_without_labels[:5]: print(示例:, img_without_labels[:5]) inspect_cache(train.cache)这个脚本最实用的地方是快速确认 cache 中的标签数量和实际 txt 是否一致。如果 cache 显示有图无标签那说明你的labels目录里存在空 txt 或转换丢失。另外当你调整过train.json再训练时一定记得删除旧的train.cache和test.cache否则 ultralytics 会按cache里的旧信息加载数据等于白改。还有一个更容易被忽略的坑使用远程服务器或 NAS 挂载数据时cache 文件会包含绝对路径。如果你把数据集从/data/mango换到/workspace/mango不删 cache 的话yolo 训练时会报路径不存在的错误。这种情况下先删除 cache再设定环境变量DATASET_CACHE或直接重跑训练让 YOLOv8 重新扫描并生成新 cache。每次我换机器或换目录第一件事就是find . -name *.cache -delete然后再启动训练这个习惯省掉了我很多次“明明代码没错但数据读不到”的排查时间。本文还有配套的精品资源点击获取