☰
茶叶与杂草检测数据集实战:从标注解析到YOLO训练全流程
2026/10/2 14:50:36 网站建设 项目流程

简介:这份茶叶与杂草检测数据集面向农业AI开发者、计算机视觉研究者及农业机器人方向的学生与从业者,用于构建茶园场景下的目标检测或实例分割模型,解决杂草自动识别与精准除草问题。资源包共656个文件,包含327张jpg实拍图片、327个同名txt标注文件、1个yaml配置文件与1份docx说明文档,压缩包约25.71MB,图片按训练集288张、验证集25张、测试集14张划分,标注采用YOLO格式,覆盖maleza(杂草)与te(茶)两个类别。目前已有54人学习下载。数据源自真实农业环境,标注定位准确、类别标签明确,可直接接入主流深度学习框架,帮助读者快速完成数据加载、模型训练与效果验证,适用于农业智能监测系统开发、自动化除草设备集成、学术研究及教学培训等场景,具备较强的任务适配性与实际泛化价值。

1. 茶叶与杂草检测数据集:从标注文件到 YOLO 训练的第一公里

茶园里最头疼的不是采茶,而是分辨茶芽和杂草。嫩芽和某些禾本科杂草在 RGB 图像里颜色接近、纹理相似,人工巡检一亩地要花掉大半天,漏检率还高。这份茶叶与杂草检测数据集就是冲着这个场景来的——它把茶叶嫩芽、茶树叶片、几类常见杂草的边界框和实例掩码都标好了,直接能喂给 YOLO 系列做目标检测或实例分割训练。适合谁用?做农业视觉的算法工程师、搞智慧农业落地的团队、以及想拿真实田间数据跑通检测流程的学生。它不是玩具数据集,标注密度和类别分布都按实际巡检需求来,能省掉你从零标数据的两三周。

2. 拆开压缩包:目录结构、标注格式与类别体系

2.1 解压后先看什么

拿到茶叶与杂草检测数据集_20251116_211936.zip,别急着写训练脚本。先解压,用tree或find把目录层级打出来。常见做法是数据集按images/和labels/分开放,也可能带annotations/存 COCO JSON。我一般先跑一遍文件计数,确认图片和标注文件数量对得上,差一个都可能是标注漏了或者命名不匹配。

# 解压后统计文件数量,确认图片与标注一一对应 unzip 茶叶与杂草检测数据集_20251116_211936.zip -d tea_weed_dataset cd tea_weed_dataset echo "图片数量: $(find . -name '*.jpg' -o -name '*.png' | wc -l)" echo "标注数量: $(find . -name '*.txt' -o -name '*.json' | wc -l)" # 列出前 10 个文件名,观察命名规律 find . -name '*.jpg' | head -10

这段命令做三件事:解压到独立目录、分别统计图片和标注文件数、抽样看命名。参数上注意-o是 find 的或条件,统计时别把labels/里的 txt 和classes.txt混进去。如果图片数和标注数差得多,先别往下走,大概率是压缩包里混了缓存文件或者标注只覆盖了部分子集。

2.2 标注格式判断:YOLO txt 还是 COCO JSON

这份数据集的关键词里有“实例分割”,所以标注大概率是两种形态之一:YOLO 格式的 txt(每行class_id x_center y_center w h,分割任务则是多边形点序列),或者 COCO 格式的 JSON(含segmentation字段的多边形或 RLE)。判断方法很简单,打开一个标注文件看头几行。

# 快速判断标注格式 import os, json label_dir = "tea_weed_dataset/labels" sample = os.listdir(label_dir)[0] path = os.path.join(label_dir, sample) if sample.endswith(".txt"): with open(path) as f: lines = f.readlines()[:3] print("YOLO txt 前 3 行:") for l in lines: print(l.strip()) # 若每行字段数 > 5,说明是多边形分割标注 print("字段数:", len(lines[0].split())) elif sample.endswith(".json"): with open(path) as f: data = json.load(f) print("JSON 顶层键:", data.keys()) print("类别数:", len(data.get("categories", [])))

逻辑说明:txt 每行字段数等于 5 是纯检测框,大于 5 且为偶数说明是x1 y1 x2 y2 ...的多边形点,对应实例分割。JSON 则看有没有categories和annotations,annotations里每条有没有segmentation。参数上,YOLO 的坐标是归一化到 0~1 的,COCO 是绝对像素坐标,转换时别搞反。

2.3 类别体系与分布检查

茶叶与杂草检测的类别通常不会太多,常见是tea_bud(嫩芽)、tea_leaf(成叶)、weed(杂草)三类,也可能把杂草细分成禾本科和阔叶两类。类别定义一般在classes.txt或 JSON 的categories里。拿到之后必须做一次分布统计,否则训练时某类样本过少会导致模型直接摆烂。

# 统计各类别实例数量(以 YOLO txt 为例) from collections import Counter import glob counter = Counter() for txt in glob.glob("tea_weed_dataset/labels/*.txt"): with open(txt) as f: for line in f: cls_id = int(line.split()[0]) counter[cls_id] += 1 print("类别 ID 分布:", dict(counter)) # 对照 classes.txt 映射名称 with open("tea_weed_dataset/classes.txt") as f: names = [n.strip() for n in f.readlines()] for cid, cnt in sorted(counter.items()): print(f"{names[cid]}: {cnt}")

这段代码遍历所有标注文件,按类别 ID 累加实例数。参数上cls_id是每行第一个字段,YOLO 从 0 开始编号。如果发现某类只有几十个实例,而其他类上千,训练时要么过采样,要么在 loss 里给类别权重。这一步不做,后面 mAP 低了你都不知道是模型问题还是数据问题。

3. 转成 YOLO 可训练格式:划分、转换与配置文件

3.1 训练集/验证集/测试集划分

数据集拿到手不能直接全量训练,必须划分。常见做法是 7:2:1 或 8:1:1。划分时要注意同一块茶园、同一批拍摄的图片不能同时出现在训练集和验证集里,否则验证指标虚高。如果文件名里带地块编号或日期,按这个维度切分最稳妥。

import os, random, shutil random.seed(42) img_dir = "tea_weed_dataset/images" label_dir = "tea_weed_dataset/labels" out_root = "dataset_split" # 按文件名前缀分组,避免同地块泄漏 files = sorted(os.listdir(img_dir)) groups = {} for f in files: key = f.split("_")[0] # 假设文件名前缀是地块编号 groups.setdefault(key, []).append(f) keys = list(groups.keys()) random.shuffle(keys) n = len(keys) train_keys = keys[:int(n*0.7)] val_keys = keys[int(n*0.7):int(n*0.9)] test_keys = keys[int(n*0.9):] for split, klist in [("train", train_keys), ("val", val_keys), ("test", test_keys)]: os.makedirs(f"{out_root}/images/{split}", exist_ok=True) os.makedirs(f"{out_root}/labels/{split}", exist_ok=True) for k in klist: for f in groups[k]: shutil.copy(os.path.join(img_dir, f), f"{out_root}/images/{split}/{f}") lbl = os.path.splitext(f)[0] + ".txt" src_lbl = os.path.join(label_dir, lbl) if os.path.exists(src_lbl): shutil.copy(src_lbl, f"{out_root}/labels/{split}/{lbl}")

逻辑说明:先按文件名前缀分组,再打乱组顺序切分,保证同组图片只进一个 split。参数seed=42保证可复现,比例按 7:2:1。如果数据集本身已经分好 train/val,跳过这步,但一定要检查有没有泄漏。

3.2 COCO 转 YOLO 分割格式

如果标注是 COCO JSON 且带segmentation,需要转成 YOLO 的分割 txt。转换核心是把绝对坐标归一化,并把多边形点拉平。

import json, os from PIL import Image coco_file = "tea_weed_dataset/annotations/instances.json" img_dir = "tea_weed_dataset/images" out_label_dir = "tea_weed_dataset/labels_seg" os.makedirs(out_label_dir, exist_ok=True) with open(coco_file) as f: coco = json.load(f) img_info = {img["id"]: img for img in coco["images"]} cat_info = {cat["id"]: i for i, cat in enumerate(coco["categories"])} for ann in coco["annotations"]: if "segmentation" not in ann or not ann["segmentation"]: continue img = img_info[ann["image_id"]] w, h = img["width"], img["height"] seg = ann["segmentation"][0] # 取第一个多边形 cls_id = cat_info[ann["category_id"]] # 归一化并拉平 norm = [] for i in range(0, len(seg), 2): norm.append(seg[i] / w) norm.append(seg[i+1] / h) line = f"{cls_id} " + " ".join(f"{v:.6f}" for v in norm) out_path = os.path.join(out_label_dir, os.path.splitext(img["file_name"])[0] + ".txt") with open(out_path, "a") as f: f.write(line + "\n")

逻辑说明:cat_info把 COCO 的 category_id 重映射成从 0 开始的连续 ID,YOLO 要求这样。segmentation[0]取第一个多边形,如果一个实例有多个多边形需要分别写多行。归一化用图片宽高,保留 6 位小数足够。转换完记得抽查几个文件,用可视化脚本画出来对一下。

3.3 data.yaml 与模型配置

YOLOv8/v11 训练需要data.yaml指定路径和类别名。分割任务还要在模型配置里选-seg权重。

# data.yaml path: /abs/path/dataset_split train: images/train val: images/val test: images/test names: 0: tea_bud 1: tea_leaf 2: weed
# 分割任务训练命令 yolo segment train data=data.yaml model=yolov8n-seg.pt epochs=100 imgsz=640 batch=16

参数说明:imgsz=640是常见起点,茶叶嫩芽目标小可以试 1024;batch=16看显存调;epochs=100配合早停。如果是纯检测任务,把segment换成detect,模型换成yolov8n.pt。

4. 避坑与排查:标注、坐标与训练崩溃的五个血泪经验

4.1 现象:训练 loss 一直不降,mAP 接近 0

原因:标注坐标没归一化,或者归一化用了错误的宽高。COCO 转 YOLO 时如果忘了除以图片尺寸,坐标全是几百的绝对值,模型根本学不动。 解决:写个校验脚本,检查每行坐标是否都在 0~1 之间,超出就报错。

# 校验 YOLO 标注坐标范围 import glob bad = [] for txt in glob.glob("dataset_split/labels/train/*.txt"): with open(txt) as f: for i, line in enumerate(f): vals = list(map(float, line.split()[1:])) if any(v < 0 or v > 1 for v in vals): bad.append((txt, i)) break print("异常文件数:", len(bad))

4.2 现象:类别 ID 从 1 开始,训练报 index out of range

原因:有些标注工具类别从 1 编号,而 YOLO 要求从 0 开始。data.yaml里 names 只有 3 类,标注里出现 3 就崩。 解决:全局扫描最大类别 ID,统一减 1 重写标注,或者把 names 补齐。

4.3 现象:图片和标注文件名对不上,训练时找不到 label

原因:图片是.jpg,标注是.JPG.txt或者多了前缀。Windows 下大小写不敏感,Linux 下直接翻车。 解决:写脚本统一重命名,图片和标注都转小写、去空格。

# 统一文件名小写并去空格 for f in dataset_split/images/train/*; do new=$(echo "$f" | tr 'A-Z' 'a-z' | tr ' ' '_') mv "$f" "$new" done

4.4 现象:分割训练显存爆了,batch 调到 1 还 OOM

原因:imgsz设太大,或者多边形点数过多导致单张图计算量暴涨。 解决:先把imgsz降到 640,用yolov8n-seg这种小模型跑通,再逐步加。多边形点数超过 500 的实例考虑简化。

4.5 现象:验证集指标很好,实际田间测试一塌糊涂

原因:训练集和验证集来自同一地块、同一光照条件,模型过拟合了拍摄环境。 解决:划分时按地块或日期分组,验证集里必须包含不同时间段、不同天气的图片。如果数据集本身覆盖不够,只能靠数据增强补,但增强替代不了真实分布。

5. 进阶技巧:用切片推理和类别权重把嫩芽检出率拉上来

茶叶嫩芽在 640 分辨率下可能只占十几个像素,直接训练容易漏。我一般会做两件事:一是训练时用imgsz=1024配合mosaic增强,二是推理时用 SAHI 切片推理。SAHI 的思路是把大图切成重叠小图分别检测再合并,对小目标提升明显。

# SAHI 切片推理示例 from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model = AutoDetectionModel.from_pretrained( model_type="yolov8", model_path="runs/segment/train/weights/best.pt", confidence_threshold=0.3, device="cuda:0" ) result = get_sliced_prediction( "test_field.jpg", model, slice_height=512, slice_width=512, overlap_height_ratio=0.2, overlap_width_ratio=0.2 ) result.export_visuals(export_dir="sahi_output/")

参数说明:slice_height/width按目标大小调,嫩芽小就切 512;overlap0.2 是经验值,太小会漏跨切片目标,太大速度慢。另外类别不均衡时,在训练命令里加cls=1.5提高分类 loss 权重,或者用fraction参数控制每类采样比例。

验证方法上,别只看 mAP50,分割任务要看 mask mAP。跑完训练用yolo segment val输出每类指标,重点看weed和tea_bud的召回率。如果tea_bud召回低于 0.6,优先补嫩芽样本或调imgsz,别急着换模型。

从那以后我每次拿到新数据集,都强制先跑一遍文件计数、坐标校验和类别分布三件套,再动手写训练脚本。这三步花不了十分钟,但能省掉后面几小时的无效训练。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询