尧图精选

YOLO稻穗数据集标签格式解析与训练前校验指南

🕒 发布时间:2026/9/13 2:11:49 📁 来源:尧图网络
简介这份YOLO数据集专注水稻稻穗检测共1个目标类别面向目标检测入门者与农业AI应用开发者解决稻穗标注数据零散、格式不统一、难以直接训练YOLO系列模型的问题。数据严格按YOLOv5目录结构保存训练集6108张图片及标签、验证集530张图片及标签均已完成划分标签采用相对坐标格式类别、中心点x、中心点y、宽、高并附带类别class文件可随时接入YOLO训练流程省去自行整理数据的环节。压缩包共2000个文件主体为1999个txt标注文件和1个可视化脚本整体仅91.54MBpy脚本无需修改随机传入一张图片即可绘制边界框并保存到当前目录极大方便标注效果自查。已有258人学习下载适合农业稻穗计数、生长监测、表型分析等真实场景也可作为YOLO系列算法改进与对比实验的基准数据是快速获得标准数据集的省心选择。1. 优质数据集到手第一件事不是急着训练而是先读标签这份水稻稻穗检测数据集只有1个类别“稻穗”训练集6108张图片和6108个txt标签验证集530张图和530个txt已经按YOLOv5的标准目录划分好。和很多随手打包的“数据集”不同它附带了classes class文件和show.py可视化脚本不需要你先写加载代码就能直接检查标注质量。问题在于很多新手把文件复制进YOLOv5后一训练就报“label shape mismatch”或“class index out of range”然后怀疑数据集坏了。实际上这两类错误几乎都出在对YOLO相对坐标标注的理解上txt里写入的不是像素坐标而是归一化后的中心点与宽高。下面先从标签文件结构讲起拆解数据集的划分逻辑、show.py的绘制原理以及训练前必须做的数据校验最后给出一个用可视化脚本对比模型预测结果的小技巧。2. YOLO相对坐标标注与数据集划分的底层逻辑2.1 标签txt里的五个数字类别与归一化坐标数据集里的每个txt文件和同名图片一一对应每行描述一个目标框。格式固定为五段0 0.514 0.406 0.231 0.182第一个数字是类别ID因为总共只有“稻穗”一个类所以全部为0。后面四个分别是x_centre、y_centre、w、h注意全部除以了图片宽度和高度范围理论上在0到1之间。这种归一化坐标是YOLO系列通用的标注协议。它带来的最大好处是图片分辨率从1920压缩到640再输入网络时标签不需要跟着改做letterbox时也不需要重新计算宽高比因为模型内部会在预处理阶段把输入统一到同一分辨率。坏处是如果显示图片时直接用这四个数作为像素坐标画框画出来的框会挤在左上角这就是很多“可视化脚本”跑出来框位置不对的根本原因。读取一个标签文件并转换为像素坐标的常见做法def read_yolo_txt(txt_path, img_width, img_height): boxes [] with open(txt_path, r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue parts line.split() if len(parts) ! 5: raise ValueError(f字段数量错误: {line}) cls_id int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) box_w float(parts[3]) box_h float(parts[4]) x1 (x_center - box_w / 2) * img_width y1 (y_center - box_h / 2) * img_height x2 (x_center box_w / 2) * img_width y2 (y_center box_h / 2) * img_height boxes.append((cls_id, x1, y1, x2, y2)) return boxes代码逻辑说明这里用x1,y1,x2,y2表示左上角和右下角的像素坐标方便后面OpenCV绘制。如果原图是矩形而非正方形必须分别乘以宽和高不能用一个统一缩放因子。txt文件里的五个字段是空格分隔的但有些工具导出时会用逗号或制表符所以先split()再逐个转成数字是最稳妥的。字段含义范围转换方式cls_id类别ID从0开始0 ~ nc-1直接读intx_center中心点X归一化0~1×图像宽度y_center中心点Y归一化0~1×图像高度box_w框宽归一化0~1×图像宽度box_h框高归一化0~1×图像高度2.2 6108/530的划分隐藏了什么信息划分比例大概是92:8。训练集6108张验证集530张。这个规模对单类别检测够用但要注意“够用”不等于“没有挑战”。稻穗在田间图片里通常密集排列每张图可能超过几十个目标而且穗与穗互相遮挡。YOLO训练时一般按图片数量估算训练步数但真正影响收敛的是标签数量。建议训练前统计所有标签的边界框数量如果平均每张超过20个小目标居多就要考虑调整anchor或使用更高的输入分辨率。验证集530张在统计上能给出相对稳定的mAP估计。如果验证集太小比如只有几十张单次验证的波动会很大早停逻辑很容易被带偏。这里的划分没有单独test目录所以调超参时建议再手动从val中切出一部分作为固定测试集避免对验证集过拟合。统计训练集标签总数的脚本也很简单from pathlib import Path total_boxes 0 for label_file in Path(datasets/labels/train).glob(*.txt): with open(label_file) as f: total_boxes len([l for l in f if l.strip()]) print(f训练集标签总数: {total_boxes}) print(f平均每张目标数: {total_boxes / 6108:.2f})参数说明6108是训练图片数代码里直接写在除数位置上。更稳妥的写法是用len(list(Path(datasets/images/train).glob(*.jpg)))动态获取图片数量避免数据集更新后手动改数字。空标签文件在统计时不会计入但如果数量很多说明标注阶段漏标严重需要回到数据校验环节处理。2.3 目录结构、classes文件与data.yaml的对应关系数据集目录通常长这样datasets/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── classes.txt └── data.yaml注意有些版本会把labels放在images同级但名字叫labels有的放在dataset根目录下。这里“datasets-images-train”的描述意味着images和labels是分开的两个根目录。训练框架要求图片和标签文件的“相对路径”保持一致比如images/train/abc.jpg必须对应labels/train/abc.txt。classes.txt是类别清单内容为一行一个类名。本数据集中就是rice_panicledata.yaml 需要手动维护标准写法train: datasets/images/train val: datasets/images/val nc: 1 names: [rice_panicle]这里names的顺序必须和标签txt里的类别ID一致。如果将来要增加第二类只能往列表尾部追加不允许插入到中间。class文件就是给人在训练前核对用的训练代码本身并不直接读它而是读data.yaml里的names。注意有些用户把classes.txt拷贝到data目录当配置文件然后训练报AssertionError: class number mismatch原因就是data.yaml里nc与标签实际类别数不一致。这类错误在只有一类时特别容易忽略因为0号类别和空列表都有可能蒙混。3. show.py 可视化脚本的原理与免配置复现3.1 从随机取图到绘制框的调用链这份数据集自带show.py官方描述是“随机传入一张图片即可绘制边界框并保存在当前目录脚本无需更改”。也就是说脚本内部已经写死了数据集的相对位置或者它在运行时用glob自动发现图片。最朴素的实现思路是在datasets/images/val或train下用glob收集所有jpg文件。随机选一张图片。在对应的datasets/labels目录拼接同名txt路径。用OpenCV读图并把txt里的归一化坐标转成像素坐标。用cv2.rectangle绘制矩形cv2.imwrite保存。关键点在于第3步的对应关系。如果标签目录的路径拼错脚本不会报错输出就是一张没有框的原图新手往往会误以为模型没训练好。所以第一步先确认图片名和标签名严格一致包括大小写和后缀。复刻一个功能等价的版本核心函数如下。import cv2 import random from pathlib import Path def draw_yolo_boxes(image_path, label_path): img cv2.imread(str(image_path)) if img is None: raise FileNotFoundError(f图片读取失败: {image_path}) h, w img.shape[:2] with open(label_path, r) as f: for line in f: line line.strip() if not line: continue parts list(map(float, line.split())) cls_id int(parts[0]) xc, yc, bw, bh parts[1:5] x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) color (0, 200, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, frice_panicle:{cls_id}, (x1, max(y1 - 5, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) return img if __name__ __main__: image_dir Path(datasets/images/val) label_dir Path(datasets/labels/val) candidates list(image_dir.glob(*.jpg)) pick random.choice(candidates) label_path label_dir / (pick.stem .txt) result draw_yolo_boxes(pick, label_path) output_path Path(visualize_ pick.name) cv2.imwrite(str(output_path), result) print(fsave {output_path.resolve()})代码逻辑说明先读图取shape得到高h和宽w。接着逐行解析标签乘以对应像素尺寸。注意int()截断转换是必要的cv2.rectangle不接受浮点坐标。max(y1-5, 0)用于防止标签文本画到图片外。random.choice保证每次运行看到的图不同。参数说明rectangle函数传入的是左上角(x1,y1)和右下角(x2,y2)不是中心点和宽高putText的字体大小0.5在低分辨率图上偏小可以在1920分辨率下调到1.0text thickness同理。如果你想把输出图导出到指定目录修改output_path即可。3.2 cv2.rectangle 与文本绘制的易错点一个常见的错误是把归一化后的bw直接当作像素宽导致矩形只占一个像素。绘制参数的对应关系如下cv2函数参数来源常见误用cv2.rectanglept1(x_center-w/2)*w直接用x_centercv2.rectanglept2(x_centerw/2)*w直接用x_centerbwcv2.putTextorg(x1, y1-5)用(xc, yc)做文本位置cv2.imwritefilename绝对或相对路径与标签文件名不一致另外YOLO坐标是中心点加宽高而OpenCV的矩形需要左上和右下两个点所以很多人在这一步转换时忘了减半宽高。画完后建议先人工确认一组框是否贴住稻穗边缘再大规模做训练。只有1个类别时文本标签写不写都影响不大但如果是多类别数据不画文本就没法快速区分框的类别来源。3.3 免配置运行背后的路径假设原版show.py能“无需更改”直接运行说明它内部大概率使用了相对于脚本的路径比如BASE_DIR Path(__file__).resolve().parent image_dir BASE_DIR / datasets / images / val这样无论你把项目移到哪里只要保持内部目录结构完整脚本都能找到数据。如果你的数据集不是下载后原样放置而是改过目录名脚本就会失效。遇到这种情况优先检查Path(__file__).resolve().parent下的相对路径不要在代码里写死绝对路径。还有一个容易被忽略的细节随机选图的“随机”没有固定随机种子每次运行结果不同。如果你要复现论文里的某张可视化图可以在脚本顶部加random.seed(42)但这样也会让每次运行都画同一张图。按需选用。实际上这种随机选图的设计是为了快速抽样真正的系统性检查还是要遍历全部图片上一章的校验脚本能覆盖这一点。4. 训练前数据校验边界、空标签与类别映射4.1 用脚本扫出越界标签和空txtYOLO训练最常见的一类坑是标签坐标超过[0,1]范围。产生原因很多标注工具导出精度丢失、手动改txt时误操作、Roboflow导入时没有归一化。越界标签不一定导致训练崩溃但会在计算损失时扩大梯度拖慢收敛甚至让mAP曲线出现“抬头又掉下去”的抖动。下面这个脚本会遍历整个训练集把问题文件列出来。import cv2 from pathlib import Path def check_dataset(image_root, label_root): images list(Path(image_root).glob(*.jpg)) errors [] empty_count 0 for img_file in images: label_file Path(label_root) / (img_file.stem .txt) if not label_file.exists(): errors.append((str(img_file), missing label)) continue img cv2.imread(str(img_file)) if img is None: errors.append((str(img_file), broken image)) continue h, w img.shape[:2] with open(label_file, r) as f: lines [line.strip() for line in f if line.strip()] if not lines: empty_count 1 for line in lines: parts line.split() if len(parts) ! 5: errors.append((str(label_file), ffield count {len(parts)})) continue try: cls_id int(parts[0]) xc, yc, bw, bh map(float, parts[1:]) except ValueError: errors.append((str(label_file), non-numeric value)) continue if cls_id ! 0: errors.append((str(label_file), funexpected cls {cls_id})) if not (0 xc 1 and 0 yc 1 and 0 bw 1 and 0 bh 1): errors.append((str(label_file), coordinate out of [0,1])) break return errors, empty_count errors, empties check_dataset(datasets/images/val, datasets/labels/val) print(f问题文件: {len(errors)}, 空标签: {empties}) for e in errors[:10]: print(e)代码逻辑说明这段代码的核心是用图片的真实尺寸去约束归一化坐标。x_center/y_center必须在0~1宽高必须大于0且不超过1。注意0 bw 1而不是 1因为宽度等于1意味着这个框覆盖整张图虽然不越界但已经是不合理的标注大多数训练代码能接受但可视化时框的边缘就在图边界上。参数说明image_root和label_root对应训练或验证目录跑完输出前10条错误。如果错误集中在某个标注宽高为0说明标注工具在保存时出了问题原始标注往往出现在这张图上某个极小的像素点最好人工检查。脚本里没有递归遍历子目录如果训练集按类别分子目录存放需要把glob(*.jpg)改成rglob(*.jpg)。注意如果校验脚本输出大量越界坐标先把问题标签修掉再训练否则后续调优全白费。4.2 类别ID与classes文件的映射关系前面提到这个数据集只有rice_panicle一个类标签里的ID全部是0。在后续训练配置里data.yaml的names列表必须和classes.txt保持同一顺序。一旦写错names: [weed, rice_panicle] # 错误示例那么标签中类别0会被当成weed训练类别1才对应稻穗但标签里没有1所以模型会困惑。由于只有1类这类错误很容易被误判为正常。检查方法很简单from pathlib import Path label_files list(Path(datasets/labels/train).glob(*.txt)) all_ids set() for f in label_files: with open(f) as fh: for line in fh: if line.strip(): all_ids.add(int(line.split()[0])) print(类别ID集合:, sorted(all_ids))输出[0]才是正常。如果出现[0,1]说明标签里混杂了多类而data.yaml里nc: 1必然报错。另外还要检查classes.txt和data.yaml里的类名大小写是否一致因为有些框架会做字符串匹配Rice_Panicle和rice_panicle会被当成两个类。4.3 对接YOLOv5/YOLOv8训练命令与超参数据校验通过后最直接的训练命令是python train.py --data data.yaml --weights yolov5s.pt --img 640 --epochs 100 --batch-size 16如果是YOLOv8yolo detect train datadata.yaml modelyolov8s.pt imgsz640 epochs100 batch16这里的imgsz对应训练输入尺寸。稻穗属于中小目标如果显卡显存足够建议用imgsz1280或至少960对小目标检测提升比很多网络结构改进都明显。同时要注意模型anchor如果使用默认coco anchor会遇到“Anchor不适合目标尺度”的瓶颈。常见做法是在训练时开启--autoanchorYOLOv5里是--autoanchor参数YOLOv8会自动执行。训练过程中的损失曲线需要结合yolo损失函数来看box_loss反映边框回归质量obj_loss反映目标置信度cls_loss因为只有单类通常很小。如果obj_loss迟迟不降先回去检查标签而不是改损失权重。下面是常见训练错误和排查方向表错误信息可能原因排查/修复Label shape mismatch标签txt不是5字段重新导出标注或修脚本Image not found路径不对或图片损坏检查data.yaml与images目录Class index out of range标签ID nc检查classes顺序CUDA out of memorybatch太大/分辨率高降低batch或imgszAssertion fail dataset图片与标签数量不一致用脚本对比文件名集合实际执行时建议先跑30个epoch看验证集mAP0.5是否在0.5以上。如果超过0.5说明数据和模型至少是通道畅通的再调增强参数才有意义。如果只有0.1左右大概率是训练数据本身有问题优先排查上一节的校验脚本。5. 用同一个可视化函数对比真值与预测框的小技巧5.1 让预测结果输出为YOLO txt格式训练完成后YOLOv5的val.py在--save-txt参数下会把预测框写入runs/val/exp*/labels/每行格式和训练标签完全一致cls_id x_center y_center width height。比如python val.py --data data.yaml --weights runs/train/exp/weights/best.pt --save-txt --conf-thres 0.25这样得到的结果可以直接复用上文draw_yolo_boxes的解析逻辑不需要额外适配。5.2 真值绿框与预测红框同屏对比给同一个图片同时加载真值和预测标签并把两种框画在同一画布上。import cv2 from pathlib import Path def draw_gt_pred(image_path, gt_path, pred_path): img cv2.imread(str(image_path)) h, w img.shape[:2] for label_path, color, tag in [ (gt_path, (0, 255, 0), GT), (pred_path, (0, 0, 255), PRED) ]: if not Path(label_path).exists(): continue with open(label_path, r) as f: for line in f: line line.strip() if not line: continue parts list(map(float, line.split())) xc, yc, bw, bh parts[1:5] 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), color, 2) cv2.putText(img, tag, (x1, max(y1 - 5, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) return img img_dir Path(datasets/images/val) gt_dir Path(datasets/labels/val) pred_dir Path(runs/val/exp/labels) for img_file in list(img_dir.glob(*.jpg))[:10]: gt gt_dir / (img_file.stem .txt) pred pred_dir / (img_file.stem .txt) canvas draw_gt_pred(img_file, gt, pred) cv2.imwrite(fcompare_{img_file.name}, canvas)这段代码会检查前10张验证图片绿色是标注真值红色是模型预测。从图像上能快速定位“漏检”和“误检”稻穗密集区域如果红色明显少于绿色优先考虑提升输入分辨率或降低置信度阈值如果红色覆盖面积明显大于绿色则可能是标签本身有漏标这种情形在自建数据集里非常常见。检查时把关注点放在单个框的IoU上可以进一步提高效率先计算每个预测框与真值之间的IoU把IoU在0.3~0.7之间的样本挑出来画图这些是最有指导意义的。真值边界有争议的样本与其反复调损失函数不如先修标注。yolo目标检测流程里数据质量永远排在模型结构前面。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →